Набор непривилегированных инструкций RISC-V
Как уже было показано выше, инструкции этого типа берут непосредственное значение imm, в качестве источника используется регистр rs1, в качестве регистра назначения - rd.
Поскольку инструкция addi rd, rs1, imm использует знаковое непосредственное значение imm, она может сработать как для сложения (если imm меняется от 0 до 2047), так и для вычитания (imm меняется от -2048 до -1).
Инструкции xori, ori и andi rd, rs1, imm выполняют побитовые операции xor (исключающее или), or (логическое сложение) и and (логическое умножение). Например, пусть в регистре x1 лежит значение 0x9, которому в побитовом представлении соответствует число 0b1001, тогда инструкция xori x1, x1, 0x3 выполнит побитовое исключающее или над содержимым регистра x1 и числом 0x3, что можно представить следующим образом:
x1 = x1 xor 0x3 = 0b1001 xor 0b0011 = 0b1010 = 0xa
Инструкции slli, srli, и srai выполняют операции побитового сдвига с использованием непосредственного значения, задающего шаг сдвига. В качестве такого непосредственного значения может выступать положительное пятибитное число, представляющее значения от 0 до 31.
Код операции логического (побитового) сдвига влево синтаксически описывается следующим образом: slli rd, rs1, imm, и в процессе выполнения сдвигает каждый бит регистра, задаваемого как rs1, на imm позиций влево. Результат при этом записывается в регистр rd. При каждом сдвиге на 1 бит младший значащий бит устанавливается в 0, а значение старшего значащего бита теряется.
По аналогии с этим выполняется команда побитового сдвига вправо: srli rd, rs1, imm. Значение регистра rs1 побитового сдвигается вправо, результат записывается в регистр, задаваемый как rd. При этом в старший значащий бит при каждом сдвиге записывается значение 0, а значение младшего значащего бита теряется.
Результат выполнения команды srai в плане заполнение старшего бита отличается для арифметического сдвига вправо для команды srai rd, rs1, imm. Информация о старшем значащем бите для этой операции сохраняется. Если до сдвига он был равен 0, старший бит заполняется 0 после сдвига. Если он был равен 1, он заполняется 1. Такой сдвиг является знакорасширяющим для значений в rs1, которые записаны в дополнительном коде.
Рассмотрим пример. Пусть значение регистра x1 равно 0x9, двоичное представление которого записывается как 0b1001. Тогда в результате выполнения инструкции slli x1, x1, 0x2 выполнится логический сдвиг содержимого регистра x1, который можно описать следующим кодом:
x1 = x1 << 0x2 = 0b1001 << 2 = 0b100100 = 0x24
Оставшиеся две инструкции slti rd, rs1, imm и sltiu rd, rs1, imm сравнивают содержимое регистра-источника с непосредственным значением и устанавливают значение регистра-приёмника в 0 в случае, если значение в регистре-источнике меньше непосредственного значения imm. Отличие между этими двумя командами в том, что команда slti использует значения в дополнительном коде и сравнение происходит со знаком.
Арифметические и логические операции (с регистрами)
Следующая категория R-формата включает в себя арифметические и логические операции, которые используют три регистра: два регистра-источника и регистр-приёмник. Большинство этих инструкций по своему действию аналогичны рассмотренным выше. К этим инструкциям относятся (табл.3.5):
Инструкция | Тип | Формат | Код | funct3 | funct7 | Описание |
---|---|---|---|---|---|---|
Add | ADD | R | 0110011 | 0x0 | 0x00 | rd = rs1 + rs2 |
Sub | SUB | R | 0110011 | 0x0 | 0x20 | rd = rs1 - rs2 |
Xor | XOR | R | 0110011 | 0x4 | 0x00 | rd = rs1 ^ rs2 |
or | OR | R | 0110011 | 0x6 | 0x00 | rd = rs1 | rs2 |
and | AND | R | 0110011 | 0x7 | 0x00 | rd = rs1 & rs2 |
sll | Shift Left Logical | R | 0110011 | 0x1 | 0x00 | rd = rs1 << rs2 |
srl | Shift Right Logical | R | 0110011 | 0x5 | 0x00 | rd = rs1 >> rs2 |
sra | Set Right Arith. | R | 0110011 | 0x5 | 0x20 | rd = rs1 >> rs2 |
slt | Set Less Than | R | 0110011 | 0x2 | 0x00 | Rd = (rs1 < rs2)? 0:1 |
sltu | Set Less Than Un. | R | 0110011 | 0x3 | 0x00 | rd = (rs1 < rs2)? 0:1 |
Рассмотрим дополнение предыдущего примера с учётом новых инструкций:
Адрес Машинный код Инструкции 0x0 0x00100093 addi x1, x0, 1 0x4 0x00108093 addi x1, x1, 1 0x8 0x00108133 add x2, x1, x1
В последней строке примера содержимое регистра x1 добавляется к самому себе, результат записывается в регистр x2. В результате выполнения всех операций в регистре x2 содержится число 4.
Все эти инструкции в отдельном рассмотрении не нуждаются, поскольку являются функциональными аналогами операций из предыдущего раздела. Исключением является инструкция sub, но, как нам кажется, её функциональность является очевидной и в рассмотрении не нуждается.
Загрузка и сохранение
Для того, чтобы получить доступ к памяти, реализован ряд инструкций, которые относятся к I-формату и предназначены для загрузки данных из памяти в регистры, а также ряд инструкций, относящихся к S-формату (S-Format, Save) для сохранения данных из регистров в память. Инструкции указаны в таблице 3.6:
Инструкция | Имя | Формат | Код | funct3 | Описание |
---|---|---|---|---|---|
lb | Load Byte | I | 0000011 | 0x0 | rd = M[rs1+imm][7:0] |
lh | Load Half | I | 0000011 | 0x1 | rd = M[rs1+imm][15:0] |
lw | Load Word | I | 0000011 | 0x2 | rd = M[rs1+imm][31:0] |
lbu | Load Byte Un. | I | 0000011 | 0x0 | Rd = M[rs1+imm][7:0] |
lhu | Load Half Un. | I | 0010011 | 0x0 | rd = M[rs1+imm][15:0] |
sb | Store Byte | I | 0010011 | 0x0 | M[rs1+imm][7:0] = rs2[7:0] |
sh | Store Half | I | 0100011 | 0x1 | M[rs1+imm][15:0] = rs2[15:0] |
sw | Store Word | I | 0100011 | 0x2 | M[rs1+imm][31:0] = rs2[31:0] |
Рассмотрим следующий пример. Предполагается, что в памяти по адресу 0x10000000находится значение 0x02.
Адрес Машинный код Значение 0x0 0x00100093 addi x1, x0, 0x1 0x4 0x01c09093 slli x1, x1, 28 0x8 0x00008103 lb x2, 0(x1)