Опубликован: 19.01.2025 | Доступ: свободный | Студентов: 1 / 0 | Длительность: 02:34:00
Лекция 4:

Набор непривилегированных инструкций RISC-V

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >
Системный интерфейс

Две инструкции, ecall и ebreak задаются в I-формате (I-format, immediate), и обе используются для доступа к системным функциям. Системные функции обычно требуют привилегированного доступа и наличия инструкций, которые лежат вне пользовательского режима доступа.

Таблица 3.10.
Инструкция Тип Формат Код 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):

Таблица 3.11.
Инструкция Тип Формат Код funct3 Описание
fence Fence I 0001111 0x0 rd, rs1 зарезервированы. Для всех типов доступа к памяти imm = 0b000011111111.
'M'-расширение команд

В качестве примера расширений, рассмотрим M-расширение, реализующее операции умножения и деления. Инструкции приведены в таблице 3.12:

Таблица 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.

Контрольные вопросы

  1. Какие наборы инструкций RISC V являются базовыми?
  2. Какой набор инструкций поддерживает процессор с архитектурой RV128IMFDV?
  3. Для чего используются регистры x10-x17?
  4. Чем отличаются различные форматы/типы инструкций?
  5. Можно ли выполнить арифметическую операцию над значениями, не загруженными в регистр?
  6. Закодируйте инструкцию andi x1, x0, 7
  7. Среди арифметических инструкций есть инструкции add, sub, addi. Почему нет инструкции subi?
  8. Инструкцией какого типа является lb x2, 0(x1)?
  9. Можно ли явно менять значение регистра PC?
  10. Какая инструкция используется для совершения системного вызова?
  11. Какой из барьеров памяти реализует инструкция fence?
  12. Можно ли в архитектуре RV32IM с помощью одной инструкции получить результат умножения 46340 * 46341?
< Лекция 3 || Лекция 4: 12345 || Лекция 5 >