Вопросы создания системного или инструментального программного обеспечения для софт-ядер архитектуры RISC-V
Интеграция с FPGA проект софт-процессора с архитектурой RISC-V позволяет использовать при разработке программного обеспечения для него любой удобный инструментарий, особенно тот, в котором есть возможность указать, какие из наборов команд должны быть учтены при компиляции.
При работе с ядрами отвечающими стандартным популярным расширениям (все или любое из набора G) особых проблем наблюдаться не должно. Основное что необходимо соблюдать это распределение памяти, адресные пространства (как минимум адрес начала исполнимого кода - где-то адресация ведется с нуля, где-то с 0x40000000 и пр.).
Проблемы начинаются при модификации системы команд. Компилятор должен как-то узнать, что он система команд изменилась, а в случае с языками высокого уровня еще и знать когда и куда именно вставлять нестандартные инструкции.
Путей, как обычно, несколько - модификация имеющихся инструментов, написание своего инструментария.
Прямым способом является расширения существующих инструментов, добавляя новые инструкции напрямую. Например, можно модифицировать инструменты из набора GNU. Данный подход позволяет использовать довольно мощный инструментарий, проверенный годами и разрабатываемый большим количество разработчиков, но требующий большого количества знаний спецификации содержимого кода и большого количества времени на компиляцию данного инструментария из-за огромных габаритов.
Альтернативным вариантом является разработка собственного ассемблера или компилятора высокоуровнего языка, способного создавать поддерживаемые процессором объектные файлы. Данный подход является наиболее гибким, элегантным и небольшим, но трудозатратным в реализации (отладка, тестирование, необходимость развития). С вариантом собственной реализации ассемблера все достаточно просто (относительно, конечно) - уже есть ряд проектов с открытым кодом - в частности на языках Lisp, Python.
Преимущество интерпретируемых языков в данном случае в том, что инструментарий написанный на них будет работать на любой платформе на которую портирован сам интерпретатор, плюс это достаточно легое для модификаций решение с "низким порогом вхождения" (в отличие от С-инструментария с их make- и прочими специфическими зависимостями).
Путь модификации GNU-инструментария
В Сети относительно подробно раскрывается только методика модификации RISC-V GNU/GCC toolchain[1] только под Linux-системами [2-3]. На вид выглядит шаги простые, но отзывы на их эффективность несколько разные.
Надо клонировать модификации RISC-V GNU/GCC toolchain с субмодулями (из первых минусов - размер скачанного будет приличным - более 7 ГБ.
$ git clone https://github.com/riscv/riscv-gnu-toolchain.git $ git submodule update --init -recursive
Установить необходимые компоненты для набора инструментов RISC-V GNU/GCC (Ubuntu) :
$ sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev
Собрать кросс-компилятор RISC-V для GNU/GCC Newlib:
./configure --prefix=<installation_path> Make
Клонируются оригинальные опкоды RISC-V:
$ git clone https://github.com/riscv/riscv-opcodes
Потом надо добавить свою инструкцию - в примере - вычисление остатка от деления.
mod r1, r2, r3 Semantics: R[r1] = R[r2] % R[r3]
В корневой папке инструмента RISC-V Opcodes находятся различные файлы, каждый из которых относится к определенному набору RISC-V.
(http://hsandid.github.io/images/posts/risc-v-custom/risc-v-custom-6.jpeg)
Необходимо добавить свою инструкцию в один из наборов (в один из файлов opcodes-xxx).:
mod rd rs1 rs2 31..25=1 14..12=0 6..2=0x1A 1..0=3
Потом надо запустить следующую команду для генерации констант MATCH и MASK:
cat <opcodes-file-you-modified> | ./parse-opcodes -c > instructionInfo.h
В файле instructionInfo.h должна появиться запись:
#define MATCH_MOD 0x200006b #define MASK_MOD 0xfe00707f ... DECLARE_INSN(mod, MATCH_MOD, MASK_MOD)
Шестнадцатеричное значение MASK является 32-разрядным шестнадцатеричным значением, которое помещает "маску", указывающую, где находятся все поля, связанные с типом инструкции. Например, mod - это инструкция типа R со следующей шестнадцатеричной маской : 0xfe00707f Соответствующее шестнадцатеричное значение - это, по сути, 32-разрядный шестнадцатеричный код, в котором значения кодов операций/funct3/funct7/.
Например, mod имеет шестнадцатеричное соответствие 0x200006b, что означает, что в двоичном формате funct7 = 0b0010000; funct3 = 0b000 и код операции = 0b1101011.
Поскольку модификация самого компилятора GCC является отнюдь не тривиальной задачей, изменению подвергнется только RISC-V GNU/GCC Binutils, чтобы добавить дополнительную инструкцию.
В файле riscv-gnu-toolchain/riscv-binutils-gdb/include/opcode/riscv-opc.h надо добавить элементы #define и DECLARE_INSN(), которые были сгенерированы ранее для новой инструкции в соответствующие блоки.
#define
DECLARE_INSN():
Далее в файле riscv-gnu-toolchain/riscv-binutils-gdb/opcodes/riscv-opc.c нужно объявить новую инструкцию, используя определенный набор параметров
name: название желаемой инструкции
xlen: ширина целочисленного регистра в битах (0,32,64)
ISA: тип используемой инструкции. Вы можете найти более подробную информацию о различных директивах инструкции в том же файле, если прокрутите страницу вниз.
operands: Функция, обрабатывающая операнды, доступна в riscv-binutils/gas/config/tc-recv.c. Ознакомьтесь с ней, чтобы узнать, какие символы должны использоваться для представления ваших операндов.




