Архитектура RISC-V
Архитектура набора команд (instruction set architecture, ISA) - часть архитектуры компьютера, определяющая программируемую часть ядра микропроцессора.
Микроархитектура
На этом уровне определяются реализованные в микропроцессоре конкретного типа:
- архитектура памяти;
- взаимодействие с внешними устройствами ввода/ вывода;
- режимы адресации;
- регистры;
- машинные команды;
- различные типы внутренних данных (например, с плавающей запятой, целочисленные типы и т. д.),;
- обработчики прерываний и исключительных состояний.
Помимо системы команд в архитектуру микропроцессора входят наборы регистров и прочие структуры для хранения и обработки данных.
Архитектура RISC-V изначально позиционируется, как открытая расширяемая архитектура.
Одна из существенных составляющих ее взрывного роста на рынке - "открытость" - т.е. никому, ни за что не надо платить "роялити" - лицензионные отчисления. Что в перспективе массового производства не такие уж и маленькие суммы. А в условиях нестабильной политической остановки открытость архитектуры, это еще и существенное снижение рисков попасть под экономические или политические запреты - на любой стадии готовности продукта или технологии.
В базовом варианте RISC-V наследует характерные черты классической RISC-архитектурами, со всеми вытекающими из этого последствиями:
- небольшой набор базовых команд, в основном регистрово-ориентированных;
- эффект "разрастания" кода из-за того, что для выполнения комплексных действий приходится прописывать последовательность нескольких простых;
- распределение "сложности формирования эффективного исполнимого программного кода" между архитектурой и компилятором, с перевесом бОльшего объема работы на компилятор.
В архитектуре RISC-V имеется обязательное для реализации небольшое подмножество команд (набор инструкций I - Integer) и несколько стандартных опциональных расширений.
В базовый набор входят команды условных/безусловных переходов, минимальный набор регистровых арифметико-логических операций, операций с памятью (load/store), а также небольшое число служебных инструкций.
Команды ветвления не используют каких-либо общих флагов, а непосредственно сравнивают свои регистровые операнды. Базис операций сравнения минимален, а для поддержки комплементарных операций (например, операций больше/меньше) операнды просто меняются местами.
Регистровая модель очень напоминает MIPS, хотя и имеет ряд отличий:
-
32-регистра общего (условно)) назначения:
- регистр x0 (zero);
- 31 целочисленный регистр общего назначения (x1 - x31);
- Регистр счётчика команд (PC, используется только косвенно);
-
Набор CSR-регистров или регистров специального назначения (Control and Status Registers, всего их может быть до 4096, хотя реально в каждой конкретной микроархитектурной реализации их заметно меньше).
Довольно важным моментом в экосистеме RISC-V является ABI, дающий рекомендации по распределению ролей регистров. Следовать ABI не обязательно и программист может распоряжаться любыми регистрами по своему усмотрению, но в ряде случаев произвольное распределение ролей регистров может вызвать проблемы:
- очевидный случай, связанный с вызовом бинарного кода сторонних разработчиков и ему подобные проблемы совместимости;
- потеря преимуществ в случае микроархитектурных оптимизаций "железа" по рекомендациям ABI.
(Двоичный интерфейс приложений (ABI) - набор соглашений для доступа приложения к операционной системе и другим низкоуровневым сервисам, спроектированный для переносимости исполняемого кода между машинами, имеющими совместимые ABI.
В отличие от API, который регламентирует совместимость на уровне исходного кода, ABI можно рассматривать как набор правил, позволяющих компоновщику объединять откомпилированные модули компонента без перекомпиляции всего кода, в то же время определяя двоичный интерфейс.)
Имена регистров в системе команд и соглашения о псевдонимах в EABI и psABI
32 целочисленных регистра (базового набора инструкций)
x0 | zero | Константа - ноль (0); |
x1 | ra | Адрес возврата (return address) |
x2 | sp | Указатель стека (stack pointer) |
x3 | gp | Глобальный указатель (global pointer) |
x4 | tp | Потоковый указатель (thread pointer) |
x5 | t0 | временная переменная/ альтернативный адрес возврата |
x6 | s3 | временная переменная/Вызывающий |
x7 | s4 | временная переменная/Вызывающий |
x8 | s0/fp | сохраняемая переменная / Указатель фрейма (frame pointer) |
x9 | s1 | сохраняемая переменная |
x10 | a0 | Аргумент функции / возвращаемое значение |
x11 | a1 | Аргумент функции / возвращаемое значение |
x12 | a2 | Аргумент функции |
x13 | a3 | Аргумент функции |
x14 | s2 | Аргумент функции |
x15 | t1 | Аргумент функции |
x16 | s5 | Аргумент функции |
x17 | s6 | Аргумент функции |
x18-27 | s7-16 | сохраняемые переменные |
x28-31 | s17-31 | Временные переменные |
Расширения набора команд для работы с плавающей точкой добавляют в набор регистров процессорного ядра еще 32 регистра:
32 регистра с плавающей точкой
f0-7 | ft0-7 | Временные значения |
f8-9 | fs0-1 | Сохраняемые регистры |
f10-11 | fa0-1 | Аргументы функций/возвращаемые значения |
f12-17 | fa2-7 | Аргументы функций |
f18-27 | fs2-11 | Аргументы функций , сохраняемые значения |
f28-31 | ft8-11 | Временные значения |
Как показывает практика, количество доступных архитектурных регистров может оказывать значительное влияние на размер кода, производительность и энергопотребление. Большее количество целочисленных регистров также повышает производительность в тех случаях, когда практикуется развертывание циклов (помним - быстрый код - линейный код), конвейерная микроархитектура, разбиение кэша на листы.
В динамическом использовании регистров, как правило, преобладают несколько часто используемых регистров, и микроархитектурные реализации регистровых файлов могут быть оптимизированы для снижения энергозатрат на доступ к часто используемым регистрам.
При одинаковой кодировке инструкций в RISC-V предусмотрены реализации архитектур с 32-, 64- и 128-битными регистрами общего назначения и операциями (RV32I, RV64I и RV128I, соответственно).
Разрядность регистровых операций всегда соответствует размеру регистра, а одни и те же значения в регистрах могут трактоваться как целые числа, как со знаком, так и без знака. Нет операций над частями регистров, нет каких-либо выделенных "регистровых пар".
Поскольку кодировка базового набора инструкций не зависит от разрядности архитектуры, то "один и тот же код потенциально может запускаться на различных RISC-V архитектурах, определять разрядность и другие параметры текущей архитектуры, наличие расширений системы инструкций, а потом автоконфигурироваться для целевой среды выполнения" (формально это действительно возможно, в стандартизованных регистрах специальных функций программа может получить информацию о характеристиках процессора на котором выполняется - версии, поддерживаемые наборы инструкций). Обязательным для процессора архитектуры RISC-V является только поддержка базового набора команд - RV32I (рис.2.1).
rs1 - номер регистра в котором находится первый операнд;
rs2 - номер регистра в котором находится второй операнд;
rd - номер регистра в который будет записан результат.
Для встраиваемых приложений с ограниченными ресурсами определено подмножество команд RV32E, которое оперирует всего 16-ю регистрами.
В абсолютном большинстве случаев в архитектуре RISC-V используется 32-битная кодировка команд за исключением некоторых расширений, допускающих команды переменной длины (о! а не скрытый ли вы CISC?)) и команд расширения С - т.н. сокращенного формата команд, иди набора сжатых инструкций (аналог наборов команд Thumb у архитектуры ARM). По всей видимости, появление у RISC-архитектур набора сжатых инструкций - "семейное" для всех RISC архитектур - комплексных команд мало, код разрастается, памяти жалко, операции с памятью дорогие по времени.
Дополнительный сжатый 16-разрядный формат команд (C) в основном имеет доступ только к 8 регистрам и, следовательно, может обеспечить плотное кодирование команд, в то время как дополнительные расширения набора команд при желании могут поддерживать гораздо большее пространство регистров.
Дополнительные сжатые 16-разрядные расширения набора команд имеют два младших бита, равных 00, 01 или 10. Стандартные расширения набора команд, закодированные более чем на 32 бита, имеют дополнительные младшие разряды, равные 1, с соглашениями для 48-разрядных и 64-разрядных длин. Длины команд от 80 до 176 бит кодируются с использованием 3-разрядного поля в битах [14:12] задает количество 16-битных слов в дополнение к первым 5 16-битным словам. Кодировка с битами [14:12], установленными на 111, зарезервирована для будущих более длинных кодировок команд - рис.2.2.
Список наборов команд
Формирование системы команд для процессоров RISC-V архитектуры можно сравнить с конструктором типа "Лего", или, что наверное еще точнее - стеки и профили сетевых протоколов для сетей малой мощности - в них также выделяется базовая часть и относительно независимые уровни для прикладных задач и для рутинных задач маршрутизации и доставки пакетов.
Да, здесь мы снова видим расхождения с первичной идеологией RISC - что нам хватит и нескольких команд, чтобы сделать всё, но требования приложений, ограничения скорости работы памяти вынуждают набор команд расширять - Таблица 2.1.