Как добавить новую пользовательскую инструкцию в набор команд RISCV

В течение многих дней я пытался смоделировать новые пользовательские инструкции с помощью симулятора RISV ISA, но не смог создать новую инструкцию и скомпилировать ее с помощью ассемблера riscv. Я попытался добавить свой новый код операции в утилиты риск-инструменты / риск-операции / коды операций. Я попытался создать собственный код операции для выполнения функции ADD, а затем заменил ADD моей собственной инструкцией в коде сборки, но ассемблер не смог скомпилировать его, сказав, что инструкция не найдена. Также, когда я пытаюсь использовать встроенные пользовательские инструкции custom0,1,2, я всегда получаю ошибку недопустимых операндов.

1 ответ

Вы можете обратиться к следующему руководству о том, как добавить новые инструкции в компилятор RISCV: https://nitish2112.github.io/post/adding-instruction-riscv/

Однако некоторые шаги имеют незначительные изменения, вероятно, из-за недавних обновлений компилятора, поэтому я снова укажу шаги с обновленными изменениями в этом ответе. Я предполагаю, что путь к рабочему каталогу, который мы используем, равен . Допустим, мы добавим инструкцию по модулю, аналогичную учебнику. Инструкция по модулю и ее семантика в соответствии с приведенным выше руководством:

      mod r1, r2, r3

R[r1] = R[r2] % R[r3]

Шаг 0: перейдите в ваш рабочий каталог, который я назову:

      cd ~/dir/

Шаг 1: Скопируйте RISCV-GNU-Toolchain в свой рабочий каталог (или~/dir/как мы называли ранее), если вы еще этого не сделали. Для этого вы можете использовать команду:

      $ git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git

Шаг 2: Установите и соберите инструменты riscv, используя следующие команды.

      $ git clone https://github.com/riscv/riscv-tools.git
$ git submodule update --init --recursive
$ export RISCV=/path/to/install/riscv/toolchain
$ ./build.sh

Шаг 3: откройте файл~/dir/riscv-tools/riscv-opcodes/opcodesи добавьте следующую строку:

      mod     rd rs1 rs2 31..25=1  14..12=0 6..2=0x1A 1..0=3

Объяснение этой строки: Инструкция по модулю, которую мы пытаемся добавить, имеет три регистра: регистр назначения, исходный регистр 1 и исходный регистр 2. Это означает, что эта инструкция соответствует формату R-типа в соответствии с руководством RISCV, которое можно найти здесь: https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf

Глядя на формат R-типа на рисунке ниже, биты с 31 по 25 определяют FUNCT7, а биты с 14 по 12 определяют FUNCT3. Теперь я не уверен, почему это отличается от руководства, но биты с 6 по 2 являются кодом операции, а не с 6 по 0. Я уверен, что есть причина, но я пока не мог ее понять.

Теперь, сравнивая эту информацию со строкой, которую мы добавили, мы добавили инструкцию по модулю с FUNCT7 как 0x01, FUNCT3 как 0x0 и кодом операции как 0x1A. Вот как учебник хочет определить код операции, FUNCT3 и FUNCT7 для этой инструкции. Вы определенно можете изменить эти числа, если хотите, но вы должны убедиться, что код операции, FUNCT7 и FUNCT3, которые вы выбираете, не совпадают с любой другой инструкцией. По крайней мере, одна из этих трех инструкций должна отличаться от другой инструкции. Например, две инструкции могут иметь один и тот же код операции и FUNCT7, но разные FUNCT3: это верно!

Шаг 4: перейдите в каталог~/dir/riscv-tools/riscv-opcodesи выполните следующую команду.

      cat opcodes-pseudo opcodes opcodes-rvc opcodes-rvc-pseudo opcodes-custom | ./parse-opcodes -c > ~/temp.h

Шаг 5: откройте файл~/temp.h(вероятно, с помощью командыvi. Затем найдитеMATCH_MOD. Вы должны найти следующие две строки:

      #define MATCH_MOD 0x200006b
#define MASK_MOD 0xfe00707f

Скопируйте эти две строки и добавьте их в файл~/dir/riscv-gnu-toolchain/riscv-binutils/include/opcode/riscv-opc.h

Объяснение MATCH_MOD и MASK_MOD: инструкция декодируется по следующей формуле:((insn ^ op->match) & op->mask) == 0;. Таким образом, MASK_MOD и MATCH_MOD используются компилятором точно так, как показано в формуле. Когда результат дает 0, это означает, что он соответствует инструкции по модулю, и вот как он декодируется! -это, насколько я понимаю, кто-то другой может попытаться добавить к этому, если у них есть больше знаний :)

Шаг 6 (Примечание: этот шаг УСТАРЕЛ в руководстве по ссылке выше): Откройте файл.~/dir/riscv-gnu-toolchain/riscv-binutils/opcodes/riscv-opc.cи добавьте следующую строку:

      {"mod",   0, INSN_CLASS_I, "d,s,t",         MATCH_MOD, MASK_MOD, match_opcode, 0 },

Шаг 7: Создайте цепочку инструментов riscv gnu, и все готово! Следуйте инструкциям по этой ссылке, чтобы создать набор инструментов: https://github.com/riscv-collab/riscv-gnu-toolchain.

Например, для сборки 64-битного компилятора в Linux вы можете использовать следующие команды:

      $ cd ~/dir/riscv-gnu-toolchain
$ mkdir build
$ cd build
$ ../configure prefix=/opt/riscv64
$ sudo make linux
$ export PATH=/opt/riscv64/bin/:$PATH

Обратите внимание , что сборка компилятора может занять некоторое время (на моей машине это занимает почти час).

Шаг 8: Готово!! теперь вы можете протестировать его, как показано в учебнике! Вы можете добавить инструкцию по модулю как встроенную сборку, а затем скомпилировать свой код. На этом этапе вы можете только скомпилировать код. Чтобы запустить ваш код, вы также должны добавить инструкцию в симулятор. Я знаю только, как добавить инструкцию в GEM5, поэтому я собираюсь поделиться этим. Обратите внимание, что шаги для GEM5 в учебнике почти неверны, потому что они по какой-то причине не используют правильный код операции/FUNCT7/FUNCT3, который они определили ранее на шаге 3.

Добавление инструкции в GEM5

Шаг 0: клонируйте gem5 в свой рабочий каталог, используя следующую команду:

      $ git clone https://github.com/gem5/gem5.git

Шаг 1: Откройте файл~/dir/gem5/src/arch/riscv/isa/decoder.isa. Найдите строку, в которой говорится:0x3: decode OPCODE. Добавьте новую строку и напишите следующее:

      0x3: decode OPCODE {
// Add your code HERE:
    0x1A: decode FUNCT3 {
        format ROp {
              0x0: decode FUNCT7 {
                    0x01: mod({{ Rd = Rs1_sd % Rs2_sd; }});
              }
        }
    } 
// END of your Code

0x00: decode FUNCT3 {
      format Load {
....

Как вы заметили, код операции — 0x1A, FUNCT7 — 0x01, а FUNCT3 — 0x0, как мы определили ранее! линияformat ROpопределяет формат инструкции как R-тип. По какой-то причине в учебнике не используются правильные числа, поэтому GEM5 не сможет правильно определить инструкцию.

Шаг 2: создайте gem5 с помощью следующей команды, и все готово!!

      $ scons build/RISCV/gem5.opt

Шаг 3: После компиляции кода ac с компилятором riscv, который мы создали ранее, вы можете запустить свой код с помощью gem5, используя следующую команду, предполагая, что имя вашего исполняемого файлаmain:

      $ GEM5 ~/dir/gem5/configs/example/se.py -c ./main

Вы можете изменить конфигурацию по умолчанию, используемую se.py, передав для нее другие параметры. Например, следующая команда использует O3CPU с объемом памяти 16 ГБ, типом памяти DD4_2400_4x16, размером кэша данных L1 64 КБ и размером кэша инструкций L1 16 КБ.

      $ GEM5 ~/dir/gem5/configs/example/se.py --cpu-clock=5GHz --cpu-type=O3CPU --mem-size=16GB --mem-type=DDR4_2400_4x16 --l1d_size=64kB --l1i_size=16kB --caches -c ./main

Надеюсь, это поможет любому, кто попытается добавить инструкции в RISCV. Мне потребовались месяцы, чтобы работать над всем этим, поэтому я подумал, что будет полезно, если кто-то сможет поместить всю эту информацию в один пост! Пожалуйста, имейте в виду, что мой ответ написан в 2022 году и возможны обновления компилятора RISCV и GEM5, поэтому мой ответ может устареть в ближайшие несколько лет.

Другие вопросы по тегам