Набор непривилегированных инструкций RISC-V
Первые две инструкции записывают в регистр x1 значение 0x10000000. Для этого в регистр x1 записывается число 1, после чего сдвигается влево на 28 позиций, в результате чего в регистр x1 формируется значение 0x10000000. Третья инструкция загружает байт из памяти по адресу, задаваемому регистром x1.
Синтаксис вызова этой команды следующий: lb rd, imm(rs1). Это можно развернуть в следующее представление: rd = M[rs1+imm][7:0]. Значение одного байта, задаваемого конструкций bits[7:0], загружается в регистра, определяемый как rd (register destination) из памяти по адресу, взятому из регистра, определяемого как rs1 (Register Source), к которому добавляется непосредственное значение смещения imm.
Из приведённого примера видно, что задавать адрес в регистре с использованием описанных инструкций довольно неудобно. Для исправления этого был разработан ряд инструкций U-формата (U-Format, Upper) (Табл.3.7):
Инструкция | Имя | Формат | Код | funct3 | Описание |
---|---|---|---|---|---|
lui | Load Upper Imm. | U | 0110111 | - | rd = imm << 12 |
auipc | Add Upper Imm. to PC | U | 0010111 | - | rd = PC + (imm << 12) |
Инструкция lui rd, imm задаёт значение регистра, определяемого как rd, равным непосредственному значению imm, сдвинутому на 12 бит влево. А инструкция auipc rd, imm устанавливает значение регистра, определяемого как rd, в результат сложения значения регистра программного счётчика PC и непосредственного значения imm, сдвинутого влево на 12 бит.
Разберём следующий пример:
Адрес Машинный код Значение Комментарий 0x0 0x100000b7 lui x1, 0x10000 Загружает 0x10000000 в регистр x1 0x4 0x10000117 auipc x2, 0x10000 Загружает 0x10000004 в регистр x2
Инструкция lui x1, 0x10000 задаёт регистру x1 значение 0x10000 << 12, таким образом, x1 = 0x10000000, поскольку сдвиг влево на 12 бит "добавляет" три нуля в шестнадцатеричном представлении. Инструкция auipc x2, 0x10000 устанавливает содержимое регистра x2 равным сумме текущего значения PC (счётчика команд), которое в данный момент равно 0x04 с прибавленным значением 0x10000 << 12. Таким образом, значение регистра x2 после выполнения этого кода будет равно 0x10000004.
Управление
Каждая из предыдущих рассмотренных инструкций устанавливает программный счётчик PC на адрес следующей инструкции, расположенной в памяти: pc = pc + 4. Для реализации возможности контроля за процессом исполнения используются инструкции B-формата (B-Format, Branching), которые реализуют механизм ветвления. Они используются для условного перехода к инструкции, адрес которой указывается непосредственно в составе соответствующих команд в случае, если условие выполняется (Табл.3.8):
Инструкция | Тип | Формат | Код | funct3 | Описание |
---|---|---|---|---|---|
beq | Branch == | B | 1100011 | 0x0 | if (rs1 == rs2) pc+=imm |
bne | Branch != | B | 1100011 | 0x1 | If (rs1 != rs2) pc+=imm |
blt | Branch < | B | 1100011 | 0x4 | if (rs1 < rs2) pc+=imm |
bge | Branch >= | B | 1100011 | 0x5 | if (rs1 >= rs2) pc+=imm |
bltu | Branch < Un. | B | 1100011 | 0x6 | if (rs1 < rs2) pc+=imm |
bleu | Branch >= Un. | B | 1100011 | 0x7 | if (rs1 >= rs2) pc+=imm |
Инструкция ветвления сравнивает значения регистров rs1 и rs2 способом, определяемым самой инструкцией. Если условие истинно, программный счётчик увеличивается на непосредственное значение imm. В выражении blt rs1, rs2, imm проверяется, что rs1 меньше, чем rs2, и если это так, программный счётчик PC изменяется следующим образом: pc = pc + imm. В противном случае он принимает значение адреса следующей инструкции в памяти (pc = pc +4).
Инструкции blt и bge учитывают знак числа, инструкции bltu и bleu - нет, воспринимают числа как беззнаковые.
Рассмотрим пример:
Адрес Машинный код Значение Комментарий 0x00 0xffb00093 addi x1, x0, -5 x1 = -5 0x04 0x00500113 addi x2, x0, 5 x2 = 5 0x08 0x0020c463 blt x1, x2, 0x8 if (x1 < x2) pc = pc + 8 0x0c 0x00100193 addi x3, x0, 1 skipped if (x1 < x2): x3 = 1 0x10 0x00200193 addi x3, x0, 2 x3 = 2
Инструкция по адресу 0x08 проверяет условие, что x1 < x2, то есть -5 < 5. Поскольку выражение истинно, программный счётчик устанавливается в значение 0x10 и инструкция по адресу 0x0c пропускается (не выполняется). Если бы инструкция по адресу 0x08 была bltu, отрицательное значение, хранящиеся в регистре x1 (-5) интерпретировалось бы как положительное значение, равное 0xFFFFFFFB (представление положительного числа, в двоичном коде идентичного отрицательному числу -5), а значит условие бы не выполнилось.
Помимо инструкций для условного перехода, есть инструкции для безусловного перехода: jalr и jal. Эти инструкции позволяют переходить на требуемый адрес путём модификации программного счётчика PC, однако при этом сохраняют адрес возврата, который равен PC+4 на момент вызова инструкции. Такой подход позволяет вернуться в то место, откуда произошёл переход. Именно так реализуется модульность разрабатываемых программ.
Инструкции записываются в разных форматах (Табл.3.9):
Инструкция | Тип | Формат | Код | funct3 | Описание |
---|---|---|---|---|---|
jal | Jump and Link | J | 1101111 | - | rd = PC+4; PC += imm |
jalr | Jump and Link Register | I | 1100111 | 0x0 | rd = PC+4; PC = rs + imm |
Инструкция jal rd, imm добавляет непосредственное значение к программному счётчику PC, PC += imm, и пишет адрес инструкции, следующей за jal в регистр, задаваемый как rd. Инструкция jalr rd, rs, imm устанавливает программный счётчик в значение, равное результату сложения регистра, задаваемого как rs, и непосредственного значения imm, pc = rs + imm, и пишет адрес возврата в регистр, задаваемый как rd.
Рассмотрим пример:
Адрес Машинный код Значение Комментарий 0x00 0x00c000ef jal x1,0xc set pc to 0x0 + 0xc = 0xc, x1 = pc + 4 = 0x4 0x04 0x00000013 addi x0, x0, 0 no operation 0x08 0x00010093 addi x1, x2, 0 set x1 = x2 0x0c 0x00008167 jalr x2, x1, 0 set pc to x1, x2 = pc + 4 = 0x10 0x10 0x00100093 addi x1, x0, 1 x1 = 1
Программа работает следующим образом. Она начинается с инструкции jal x1, 0xc, которая расположена по адресу 0x00. В результате своего выполнения адрес возврата пишется в регистр x1 (следующая инструкция за инструкцией по адресу 0x00 размещается по адресу 0x04, именно это число помещается в регистр x1) и осуществляется переход по адресу 0x0c. По адресу 0x0c размещается инструкция jalr x2, x1, 0, которая пишет уже свой адрес возврата (значение 0x10) в регистр x2 и осуществляет переход по адресу, который лежит в регистре x1 (там размещается адрес возврата для первого выполненного перехода, это адрес 0x04). Программа продолжает выполняться и исполняется инструкция, расположенная по адресу 0x04, которая ничего не делает. Следующая за ней инструкция, расположенная по адресу 0x08) записывает значение 0x10 (адрес возврата для команды jalr) в регистр x1. После этого, вторая итерация выполнения команды jalr по адресу 0xc передаёт управление по адресу 0x10 (значение регистра x1). Итого, программный счётчик PC последовательного принимает следующие значения: 0x00, 0x0c, 0x04, 0x08, 0x0c, и 0x10.