Архитектура процессоров
Набор инструкций RISC-V
Подход создания базового набора инструкций с выбором необходимых расширений в RISC-V позволяет беспрепятственно развивать архитектуру без затруднений для обратной совместимости. Также обеспечивается возможность производить на единой архитектуре чипы с одинаковой эффективностью, предназначенные для различных сегментов, например, встраиваемых системы где делается упор на энергоэффективность и цену, либо пользовательские решения, где важна производительность.
Как и в остальных архитектурах, операции в RISC-V кодируются в особый формат, однако (за исключением особого расширения сжатых инструкций), инструкции имеют всегда одинаковую длину кодировки (32 бит) вне зависимости от разрядности процессора. Такой подход существенно упрощает декодирование инструкций, работу с памятью и повторное использование частей расширения непосредственно в реализациях других процессоров.
Ниже перечислены базовые инструкции работы процессора RISC-V RVI32, необходимые для собственной реализации ядра процессора:
1. Арифметические инструкции
ADD: сложение.
Пример: ADD x3, x1, x2 - сложение значений в регистрах x1 и x2, результат записывается в x3.
SUB: вычитание.
Пример: SUB x3, x1, x2 - вычитание значения x2 из x1, результат записывается в x3.
2. Логические инструкции
AND: логическое И.
Пример: AND x3, x1, x2 - побитовое И значений в регистрах x1 и x2, результат записывается в x3.
OR: логическое ИЛИ.
Пример: OR x3, x1, x2 - побитовое ИЛИ значений в регистрах x1 и x2, результат записывается в x3.
XOR: исключающее ИЛИ.
Пример: XOR x3, x1, x2 - побитовое исключающее ИЛИ, результат записывается в x3.
3. Инструкции для работы с памятью
LW: загрузка слова из памяти.
Пример: LW x3, 0(x1) - загрузка 32-битного слова из адреса, основанного на значении в x1, в регистр x3.
SW: запись слова в память.
Пример: SW x3, 0(x1) - запись 32-битного слова из x3 по адресу, основанному на значении в x1.
4. Управление потоком выполнения
JAL: переход с сохранением адреса возврата.
Пример: JAL x1, label - переход к метке label, адрес возврата сохраняется в x1.
LUI: загрузка верхней части значения.
Пример: LUI x1, 0x12345 - загрузка значения 0x12345000 в регистр x1.
BEQ: условный переход (если равно)
Пример: BEQ x1, x2, label - переход к метке label, если значения в x1 и x2 равны.
JALR: переход по адресу в регистре с сохранением адреса возврата.
Пример: JALR x1, 0(x5) # Переход к адресу, указанному в x5. Возврат по выполнении по адресу, сохраненному в x1.
AUIPC: добавление адреса текущей инструкции.
Пример: AUIPC x1, 0x10 - добавляет к адресу текущей инструкции значение 0x10, результат помещается в x1.
BNE: условный переход (если не равно).
Пример: BNE x1, x2, label - переход к метке label, если значения в x1 и x2 не равны.
5. Инструкции для работы с непосредственными данными
ADDI: сложение с немедленным значением.
Пример: ADDI x3, x1, 10 - сложение значения в x1 с 10, результат записывается в x3.
SLTI: сравнение меньше с немедленным значением.
Пример: SLTI x3, x1, 10 - x3 будет равно 1, если x1 < 10, иначе 0.
6. Инструкции для операций с битами
SLL: сдвиг влево.
Пример: SLL x3, x1, 2 - сдвиг значения в x1 на 2 бита влево, результат записывается в x3.
SRL: логический сдвиг вправо.
Пример: SRL x3, x1, 2 - логический сдвиг значения в x1 на 2 бита вправо.
Все инструкции в RISC-V разделены на 6 типов:
- R - операции АЛУ типа регистр - регистр;
- I - операции АЛУ с непосредственным значением в команде;
- S - операции загрузки/сохранения;
- B - условная передача управления;
- U - операции с расширенным непосредственным значением;
- J - безусловная передача управления.
Форматы типов инструкций представлены на рисунке 7.9 и определены в "The RISC-V Instruction Set Manual " [6]. В приложении Б приведен перечень инструкции для базовой версии RV32I
Рассмотрим пример кодировки инструкции add - инструкции суммирования содержимого двух регистров и записи результата в третий регистр. На рисунке 7.10 представлен пример кодировки соответствующей инструкции.
Первая строчка отображает то, как будет записана типовая инструкция в данном расширении. Opcode - номер операции, который описан в документации на каждую инструкцию константным значением. В случае с инструкцией add это будет 0110011, rd - номер регистра назначения результата. Funct3 - указатель дешифратору операций, какую операцию реализовывать, на случай если opcode инструкций одинаковы, rs1 - номер первого операнда. rs2 - номер второго операнда. Оставшаяся часть заполняется нулями. При детальном рассмотрении типовой кодировки и кодировки конкретной инструкции можно заметить, что у нас отсутствует imm(произвольная константа), которая занимает 12 бит. Это связано с тем, что логика инструкции сделана так, что никаких констант не требуется. Освободившееся место использовано для второго операнда, положение которого не стандартизировано в типовой кодировке. Если бы у нас была константа, но не было нужды в номере регистра второго операнда, то операнд находился именно в поле с 31 по20-ый бит. Таким образом, при сложении r1 и r2 с записью результата в r3, кодировка будет иметь следующий вид: 0000000_00010_00001_000_00011_0110011.
При изучении и в сравнении с другими системами ISA можно заметить, что процессоры RISC-V на уровне микроархитектуры в силу упрощения и повышения быстродействия не реализуют множество инструкций, характерных для других решений. Поэтому в компиляторы ассемблера заложены псевдо-инструкции, которые может применять программист, а они в свою очередь будут транслированы в инструкции, реализуемые процессором. В таблицу 7.2 сведены псевдо-инструкции, транслируемые в инструкции процессоров RISC-V.
Псевдо-инструкция | Инструкция RISC-V | Описание |
---|---|---|
nop | addi zero,zero,0 | нет операции |
mv rd, rs1 | addi rd, rs, 0 | Копирование между регистрами rs в rd |
not rd, rs1 | xori rd, rs, -1 | Запись в rd логической инверсии RS (все биты проходят операцию XOR с 1) |
neg rd, rs1 | sub rd, x0, rs | Запись в rd числа в обратном коде rs |
seqz rd, rs1 | sltiu rd, rs, 1 | Устанавливает результат в 1 если rs <1 |
snez rd, rs1 | sltu rd, x0, rs | Устанавливает результат в 1 если rs не равен 0 |
sltz rd, rs1 | slt rd, rs, x0 | Устанавливает результат в 1 если rs < 0 |
sgtz rd, rs1 | slt rd, x0, rs | Устанавливает результат в 1 если rs >0 |
beqz rs1, offset | beq rs, x0, offset | Переход если равно 0 |
bnez rs1, offset | bne rs, x0, offset | Переход если не равно 0 |
blez rs1, offset | bge x0, rs, offset | Переход если меньше или равно 0 |
bgez rs1, offset | bge rs, x0, offset | Переход если больше или равно 0 |
bltz rs1, offset | blt rs, x0, offset | Переход если меньше 0 |
bgtz rs1, offset | blt x0, rs, offset | Переход если больше 0 |
bgt rs, rt, offset | blt rt, rs, offset | Переход если больше |
ble rs, rt, offset | bge rt, rs, offset | Переход если меньше или равно |
bgtu rs, rt, offset | bltu rt, rs, offset | Условный переход сравнить два беззнаковых числа. Если rs >rt переход по адресу offset |
j offset | jal x0, offset | безусловный переход |
jr offset | jal x1, offset | Безусловный переход по адресу, адрес возврата в x1 |
ret | jalr x0, x1, 0 | Возврат из функции |