Опубликован: 19.04.2025 | Доступ: свободный | Студентов: 1 / 0 | Длительность: 07:05:00
Лекция 13:

Вопросы создания системного или инструментального программного обеспечения для софт-ядер архитектуры RISC-V

< Лекция 12 || Лекция 13: 12 || Лекция 14 >

Это основной оператор switch функции синтаксического анализа riscv_ip(), который обрабатывает различные операнды. Ориентировочное положение в файле - в районе строки с номером 2000.

match: полученное ранее значение MATCH

mask: аналогично для MASK

match_opcode: указатель на функцию, которую вы хотели бы использовать, чтобы "определить", какой код операции вы используете

pinfo: Используется для указания некоторого специального поведения. В основном используется с инструкциями перехода и сжатыми инструкциями RISC-V. Значение равно 0, если нет специального поведения.

Аналогично первых шагам процесса необходимо пересобрать RISC-V GNU/GCC Compiler Toolchain


Теперь можно использовать свои пользовательские инструкции в своих программах. Чтобы иметь возможность вызывать свои пользовательские инструкции, следует использовать ассемблерные вставки.

Наконец, инструкции RISC-V собираются в машинный код. Можно использовать obj-dump для проверки сгенерированных двоичных файлов RISC-V и проверить, где вызывается новая инструкция.

В общем - такой путь модификации инструментария не радует - 7 гигабайт трафика, около часа процессорного времени на перекомпиляцию за сомнительное удовольствие работать с ассемблерными вставками.

Конечно, на самом деле настроить компилятор языка высокого уровня на работу с "крафтовыми" командами достаточно комплексная задача, особенно если требуется оптимизированный код.

Open source ассемблеры

Ситуация с ассемблерами с открытым исходным кодом несколько проще, также как и в случае с gcc-risc-v имеются исходные тексты компиляторов. Случае с исходниками на Python разобраться с исходными текстами и правилами модификации исходной версии ассемблерного компилятора относительно не сложно.

Относительно простой переход к языкам высокого уровня в проектах FPGA с софт-процессорами возможен для языков с т.н. "виртуальными машинами" (ВМ) - наподобие упомянутого уже LISP, экзотического в современном мире Forth или Java. Связано это с тем, что реализуется на низком уровне ядро ВМ (возможно даже с учетом принятого в RISC-V ABI), а далее просто идет ее развитие в требуемом направлении.

Для относительно широкого применения, снижения "входного порога", а также для повторного использования кода и применения наработок кода, целесообразнее перейти на ЯВУ, отличный от Форта (отчасти это связано с суевериями и заблуждениями майн-стрим программистов относительно сложностей данного языка и читабельности его кода (к слову, один из авторов данной работы аналогичного мнения о С-подобных языках)).

Интересен вариант с адаптацией некоторого ограниченного подмножества языка Python к RISC-V. Python - высокоуровневый язык программирования общего назначения, ориентированный на повышение производительности разработчика и читаемости кода, поддерживающий несколько парадигм программирования, в том числе структурное, объектно-ориентированное, функциональное, императивное и аспектно-ориентированное, плюс - "его преподают даже в детском садике".

Некоторое время назад был анонсирован компилятор Uzh - небольшой компилятор для программного процессора FPGA Zmey. Uzh - это также статически скомпилированное подмножество Python, основывается на перспективном инструментарии raddsl (набор инструментов для быстрого создания прототипов DSL-компиляторов).

Компилятор Uzh принимает код на языке Python и формирует на выходе загрузочный поток для инициации памяти программ и памяти данных процессора (да, практически любой язык программирования можно использовать и в варианте интерпретатора, и в варианте компилятора с той или иной степенью комфорта), ключевым моментом является то, что на этапе компиляции доступен весь функционал языка.

Для установки компилятора Uzh достаточно скачать его архив и распаковать в любую удобную папку (лучше придерживаться общих рекомендаций для специализированного программного обеспечения - избегать путей, содержащих кириллицу и пробелы). Также необходимо скачать и распаковать в основную папку компилятора инструментарий raddsl.

Папка test компилятора содержит примеры программ для софт-процессора, папка src - исходные тексты элементов компилятора. Для удобства работы лучше создать небольшой командный файл (расширение .cmd) с содержимым: c.py C:\D\My_Docs\Documents\uzh-master\tests\abc.py , где abc.py - имя файла с программой для софт-процессора.

Для адаптации компилятора Uzh-а к конкретному процессору потребуются некоторые изменения в его исходном коде. К счастью, мест, подлежащих корректировке в компиляторе не много. Основные "аппаратно-зависимые" файлы:

  • asm.py - ассемблер и формирование чисел (литералов);
  • gen.py - низкоуровневые правила формирования кода (функции, переменные, переходы и условия);
  • stream.py - формирование загрузочного потока;
  • macro.py - макроопределения, по факту - расширения базового языка аппаратно-специфичными функциями.

Следующие изменения в компиляторе коснутся модуля asm.py в котором описывается система команд процессора (прописываются мнемоники команд и опкоды команд) и способ представления/компиляции числовых значений - литералов.

Команды упаковываются в словарь, а за литералы отвечает функция lit(). Если с системой команд все просто - просто меняется список мнемоник и соответствующих им опкодов, то с литералами дело обстоит немного иначе.

Основные и самые ответственные изменения/определения - в модуле gen.py. Данный модуль определяет основную логику работы/исполнения высокоуровневого кода на уровне ассемблера:

  • условные и безусловные переходы;
  • вызов функций и передача им аргументов;
  • возврат из функций и возвращение результатов;
  • подстройки под размеры памяти программ, памяти данных и стеков;
  • последовательность действий при старте процессора.

Для поддержки ЯВУ процессор должен уметь достаточно произвольно работать с памятью и указателями и иметь область памяти для хранения локальных переменных функций - здесь вполне логично распределить функциональность регистров в соответствии с принятым в RISC-V ABI.

Теперь можно приступить к модификации кода модуля gen.py.

Переменные *_SIZE в комментариях не нуждаются и требуют только подстановки значений, заданных в проекте процессорного ядра.

Список STUB - временная заглушка для формирования места для адресов переходов с последующим их заполнением компилятором (текущие значения соответствуют 24-битному адресному пространству памяти кода).

Список STARTUP - задает последовательность действий, выполняемых ядром после сброса - будет задан начальный адрес памяти локальных переменных - 900, и переход на точку старта.

Функция func() прописывает действия, производимые при вызове функции, а именно - перенос аргументов функции в область локальных переменных, выделение памяти для собственных локальных переменных функции.

Epilog() определяет действия при возвращении из функции - освобождение памяти временных переменных, возврат на точку вызова.

Работа с переменными идет посредством их адресов, ключевое определение для этого - push_local(), возвращающее адрес "высокоуровневой" переменной.

Следующие ключевые моменты - это условный и безусловный переходы. И основное определение компилятора на низком уровне - набор правил для операций языка и работы с памятью.

Модуль macro.py позволяет несколько "расширить" словарь целевого языка за счет макроопределений на ассемблере целевого процессора. Для компилятора языка высокого уровня определения в macro.py не будут отличаться от "родных" операторов и функций языка.

Путь создания своего компилятора, несмотря на возможно чувствительный по времени цикл разработки, представляется перспективным. Плюсы - возможность быстрой и простой адаптации инструмента под текущий проект.

  1. http://github.com/riscv/riscv-gnu-toolchain
  2. Adding custom instructions compilation support, to RISCV toolchain. - http://medium.com/@viveksgt/adding-custom-instructions-compilation-support-to-riscv-toolchain-78ce1b6efcf4
  3. Adding Custom Instructions to the RISC-V GNU-GCC toolchain - http://hsandid.github.io/posts/risc-v-custom-instruction/
  4. GNU toolchain for RISC-V, including GCC - http://github.com/riscvcollab/riscv-gnu-toolchain
  5. http://github.com/celebi-pkg/riscv-assembler
  6. Lisp RISC-V Assembler - http://gitmemories.com/technoblogy/lisp-riscvassembler
  7. GitHub - true-grue_uzh_ Uzh compiler // http://github.com/true-grue/uzh
  8. GitHub - true-grue_raddsl_ Tools for rapid prototyping of DSL compilers // http://github.com/true-grue/raddsl
  9. http://sovietov.com/txt/dsl_python_conf.pdf
< Лекция 12 || Лекция 13: 12 || Лекция 14 >