Набор непривилегированных инструкций RISC-V
Системный интерфейс
Две инструкции, ecall и ebreak задаются в I-формате (I-format, immediate), и обе используются для доступа к системным функциям. Системные функции обычно требуют привилегированного доступа и наличия инструкций, которые лежат вне пользовательского режима доступа.
Инструкция | Тип | Формат | Код | funct3 | Описание |
---|---|---|---|---|---|
ecall | Environment Call | I | 1110011 | 0x0 | imm = 0, rd = rs1 = 0, передать управление системе |
ebreak | Environment Break | I | 1110011 | 0x0 | imm = 1, rd = rs1 = 0, передать управление отладчику |
Инструкция ecall запрашивает системную сервисную операцию, так называемый системный вызов. Согласно бинарному интерфейсу для приложений ABI, идентификатор и дополнительные параметры предоставляются через предварительно настроенные регистры, при этом значение регистра x17 задаёт тип системного вызова. В следующем примере показан вызов функции выхода в ОС Linux. Системный вызов exit говорит ОС Linux о том, что ОС должна закончить выполнение программы и выгрузить программу из памяти.
Адрес Машинный код Значение Комментарий 0x00 0x05d00893 addi x17, x0, 93 set x17 = 93, Linux system call exit 0x04 0x00000073 ecall call system
Инструкция ebreak используется отладчиком для отладки программ. Отладчик временно подменяет инструкцией ebreak код в том месте, где программа должна остановиться. Ситуация, когда вызов инструкции ebreak реализуется программистом вручную, нетипична.
Упорядочение памяти
Наконец, инструкция fence используется для синхронизации доступа к памяти между различными процессорами, использующими общую область памяти. Инструкция fence разделяет программный код на код, идущий до неё (предшествующий набор, predecessor set) и инструкции после неё (последующий набор, successor set). Команда полезна для систем, использующих более одного процессорного ядра RISC-V.
Инструкция fence заботится о том, чтобы каждая инструкция из предшествующего ей набора была выполнена исполняющим ядром (и так для каждого ядра) перед тем, как будет выполнена первая инструкция из последующего за ней набора. Инструкция fence очень важна для реализации синхронизации процессов в операционных системах, в том числе в таких механизмах, как механизмы блокировки. Однако эти механизмы выходят за пределы рассматриваемого курса и упоминаются лишь для полноты картины. Обычная инструкция fence имеет следующий I-формат (Табл.3.11):
Инструкция | Тип | Формат | Код | funct3 | Описание |
---|---|---|---|---|---|
fence | Fence | I | 0001111 | 0x0 | rd, rs1 зарезервированы. Для всех типов доступа к памяти imm = 0b000011111111. |
'M'-расширение команд
В качестве примера расширений, рассмотрим M-расширение, реализующее операции умножения и деления. Инструкции приведены в таблице 3.12:
Инструкция | Тип | Формат | Код | funct3 | funct7 | instruction |
---|---|---|---|---|---|---|
mul | Multiply | R | 0110011 | 0x0 | 0x01 | rd = (rs1 * rs2)[31:0] |
mulh | Multiply High | R | 0110011 | 0x1 | 0x01 | rd = (rs1 * rs2)[63:32] |
mulsu | Multiply High Sign/Uns. | R | 0110011 | 0x3 | 0x01 | rd = (rs1 * rs2)[63:32] |
mulu | Multiply Unsigned | R | 0110011 | 0x3 | 0x01 | rd = (rs1 * rs2)[63:32] |
div | Divide | R | 0110011 | 0x4 | 0x01 | rd = rs1 / rs2 |
divu | Divide Unsigned | R | 0110011 | 0x5 | 0x01 | rd = rs1 / rs2 |
rem | Remainder | R | 0110011 | 0x6 | 0x01 | rd = rs1 % rs2 |
remu | Remainder Unsigned | R | 0110011 | 0x7 | 0x01 | rd = rs1 % rs2 |
Поскольку перемножение двух (потенциально больших) чисел может превысить размер регистра (для RV32 этот размер составляет 32 бита), младшая часть результата умножения возвращается с использованием инструкции mul, а старшая часть - с использованием инструкций mulh, mulsu, mulu в зависимости от того, какая комбинация чисел в плане знака используется. Например:
Адрес Машинный код Значение Комментарий 0x00 0x000100b7 lui x1, 0x10 x1 = 0x00010000 0x04 0x00108093 addi x1, x1, 1 x1 = 0x00010001 0x08 0x00080137 lui x2, 0x80 x2 = 0x00080000 0x0c 0x022081b3 mul x3, x1, x2 x3 = 0x00080000, lower 32 bits of mult. 0x10 0x02209233 mulh x4, x1, x2 x4 = 0x00000008, higher 32 bits of mult.
Деление на нуль, результат которого не определён, записывает в регистр-приёмник значение -1 (или, другими словами, выставляет все биты в 1). Это значит, что перед выполнением операции деления программисту необходимо самостоятельно проверять делитель и использовать операторы ветвления для обработки исключительных ситуаций.
Замечания касательно архитектуры RV64
В целом, для архитектуры RV64 набор инструкций такой же, как и для архитектуры RV32. Однако для RV64 регистры имеют размер в 64 бита, а не в 32, и операции выполняются над 64-битными числами. Более того, для RV64 реализуется ряд инструкций, специфичных именно для 64-битной архитектуры, такие инструкции имеют в конце букву 'w'. Такие инструкции работают только с 32-битными числами, причём используются младшие 32 бита то 64-битных чисел. Примерами таких инструкций являются addiw, addw, sllw, mulw.
Контрольные вопросы
- Какие наборы инструкций RISC V являются базовыми?
- Какой набор инструкций поддерживает процессор с архитектурой RV128IMFDV?
- Для чего используются регистры x10-x17?
- Чем отличаются различные форматы/типы инструкций?
- Можно ли выполнить арифметическую операцию над значениями, не загруженными в регистр?
- Закодируйте инструкцию andi x1, x0, 7
- Среди арифметических инструкций есть инструкции add, sub, addi. Почему нет инструкции subi?
- Инструкцией какого типа является lb x2, 0(x1)?
- Можно ли явно менять значение регистра PC?
- Какая инструкция используется для совершения системного вызова?
- Какой из барьеров памяти реализует инструкция fence?
- Можно ли в архитектуре RV32IM с помощью одной инструкции получить результат умножения 46340 * 46341?