Как я писал в прошлом посте, название моего проекта на GSoC-2016 — «порт RISC-V на Parallella», и первая вещь, которую я должен был сделать, это познакомиться с экосистемой RISC-V. Один из лучших способов это сделать, — посмотреть видео с презентации первого воркшопа RISC-V на Youtube. Для того, чтобы понять самые важные концепции, я рекомендую следующие презентации:
Ещё одна ссылка, если вы интересуетесь Chisel, языком, основанным на Scala, который используется для описания текущей аппаратной реализации ядра RISC-V (ядро Rocket имеет in-order конвейер, BOOM — out-of-order), и любых будущих реализаций.
Краткое руководство по Chisel (Jonathan Bachrach) видео слайды
Работа с Rocket Chip, добавление расширений, инфраструктура ASIC и FPGA (Colin Schmidt) видео слайды
Если вы глубоко заинтересованы в RISC-V и развитии сообщества, я предлагаю вам принять участие в воркшопах.

Наш порт RISC-V на Parallella будет использовать совершенно стандартное ядро, сгенерированное с помощью Rocket Chip из исходников на Chisel (в статье даётся очень хорошее введение). Конечным результатом будет Verilog RTL (в нечитаемом виде, только исходники на Chisel читаемы), который мы можем включить в наш проект FPGA для Parallella, который доступен в репозитории Parallella OH (Open Hardware). Однако я не буду вдаваться в подробности сейчас, всё это будет рассмотрено в следующих постах.
Для того, чтобы компилировать ПО для этого ядра, вы можете использовать тулчейн RISC-V GNU, который можно взять здесь. Есть два варианта компиляции: либо с библиотекой Newlib C, которая используется для программ, работающих на прокси-ядре (proxy kernel, pk) либо компилируете с библиотекой GNU C library (glibc), для компиляции Linux и программ под Linux. Я скомпилировал обе версии, (ядро Linux, релиз 4.1), используя прилагаемые инструкции, и всё заработало, как ожидалось, при симуляции в Spike, референсном симуляторе RISC-V ISA.
Главный репозиторий Rocket Chip связан путём использования субмодулей Git со всеми р��позиториями, содержащими исходники (главным образом, Chisel), которые нужны для правильной генерации с использованием своих файлов-обёрток, расположенных в src/main/scala/. Каждый субмодуль связан с определённым коммитом, заведомо рабочим, в результате сборка получается стабильной. Лично я не встретил никаких ошибок при генерации дефолтного ядра RV64G (64 бита, [I]nteger, [M]ultiply / Division, [A]tomic, [F] Single Floating, [D]ouble Floating), сокращение IMAFD сокращается до G (General), содержащего L1-кэши инструкций и данных, TLB и FPU с интерфейсами MemIO и HostIO для связи с внешним миром.
В типичной FPGA-системе (как показано на диаграмме из презентации Y.Lee по Rocket Chip), эти два интерфейса нужны для доступа к основной памяти и для загрузки ядра RISC-V (бутстрап). Последний используется для управления CSR (control-status registers) и поддержки системных вызовов (для операций ввода-вывода) из целевой машины (на которой запущено прокси-ядро или ядро Linux) в хост-машину (т.е. в двухядерный процессор ARM в Zynq FPGA), на которой запущен инстанс riscv-fesvr (front end server) для диспетчеризации системных вызовов в операционную систему (т.е. ARM Linux).
Для конфигурирования ядра вы можете также изменить значения внутри файла Config.scala. Этот файл содержит набор конфигураций для различных применений (модель C++, FPGA обычного размера, маленькая FPGA, VLSI, и т.д.). Всё, что делает скрипт, принимающий переменную CONFIG=XXX (т.е. DefaultFPGAConfig), это устанавливает требуемую конфигурацию, используемую для сборки симулятора C++ либо Verilog RTL для FPGA или VLSI. Это очень полезная вещь, если вы хотите экспериментировать с разными доступными опциями, быстро генерировать и тестировать их в программном либо аппаратном виде.
Хорошей идеей является скомпилировать тулчейн (с Newlib лучше всего), прежде чем генерировать любые ядра, так как вам нужно компилировать и запускать тесты для верификации вашего ядра с помощью симулятора C++ или Synopsys VCS. К сожалению, я смог использовать только первый, но не последний. Вы не можете использовать любой симулятор Verilog (например, Verilator, Icarus Verilog, Modelsim, QuestSim), так как тестбенч использует функции DirectC (связывающие тестбенчи Verilog и произвольный код C/C++). К счастью, симулятор С++ обеспечивает вывод VCD и я смог использовать GTKWave для просмотра сгенерированных временных диаграмм тестов.

Я также добавил сессию с простыми командами, нужными для сборки тулчейна RISC-V и генерации ядра RV64G для FPGA. Эти команды будут добавлены в финальный скрипт компиляции, который я разрабатываю для полной автоматизации всего необходимого для того, чтобы получить рабочее ядро RISC-V внутри чипа Zynq на Parallella.
В следующем посте мы рассмотрим, как получить IP-блок из сгенерированного ядра RV64G для того, чтобы его можно было легко подключать к проектам, для тестирования на Zedboard или для использования в большом проекте на Parallella.
- Введение (Krste Asanović) видео слайды
- Тулчейн RISC-V (Andrew Waterman) видео слайды
- SoC-генератор RISC-V “Rocket Chip” в Chisel (Yunsup Lee) видео слайды
- Структура программного стека RISC-V (Sagar Karandikar) видео слайды
- Отладка на RISC-V (Albert Ou) видео слайды
- Портирование нового кода на RISC-V с OpenEmbedded (Martin Maas) видео слайды
- Окружение тестирования RISC-V (Stephen Twigg) видео слайды
Ещё одна ссылка, если вы интересуетесь Chisel, языком, основанным на Scala, который используется для описания текущей аппаратной реализации ядра RISC-V (ядро Rocket имеет in-order конвейер, BOOM — out-of-order), и любых будущих реализаций.
Краткое руководство по Chisel (Jonathan Bachrach) видео слайды
Работа с Rocket Chip, добавление расширений, инфраструктура ASIC и FPGA (Colin Schmidt) видео слайды
Если вы глубоко заинтересованы в RISC-V и развитии сообщества, я предлагаю вам принять участие в воркшопах.

Наш порт RISC-V на Parallella будет использовать совершенно стандартное ядро, сгенерированное с помощью Rocket Chip из исходников на Chisel (в статье даётся очень хорошее введение). Конечным результатом будет Verilog RTL (в нечитаемом виде, только исходники на Chisel читаемы), который мы можем включить в наш проект FPGA для Parallella, который доступен в репозитории Parallella OH (Open Hardware). Однако я не буду вдаваться в подробности сейчас, всё это будет рассмотрено в следующих постах.
Для того, чтобы компилировать ПО для этого ядра, вы можете использовать тулчейн RISC-V GNU, который можно взять здесь. Есть два варианта компиляции: либо с библиотекой Newlib C, которая используется для программ, работающих на прокси-ядре (proxy kernel, pk) либо компилируете с библиотекой GNU C library (glibc), для компиляции Linux и программ под Linux. Я скомпилировал обе версии, (ядро Linux, релиз 4.1), используя прилагаемые инструкции, и всё заработало, как ожидалось, при симуляции в Spike, референсном симуляторе RISC-V ISA.
Главный репозиторий Rocket Chip связан путём использования субмодулей Git со всеми р��позиториями, содержащими исходники (главным образом, Chisel), которые нужны для правильной генерации с использованием своих файлов-обёрток, расположенных в src/main/scala/. Каждый субмодуль связан с определённым коммитом, заведомо рабочим, в результате сборка получается стабильной. Лично я не встретил никаких ошибок при генерации дефолтного ядра RV64G (64 бита, [I]nteger, [M]ultiply / Division, [A]tomic, [F] Single Floating, [D]ouble Floating), сокращение IMAFD сокращается до G (General), содержащего L1-кэши инструкций и данных, TLB и FPU с интерфейсами MemIO и HostIO для связи с внешним миром.
В типичной FPGA-системе (как показано на диаграмме из презентации Y.Lee по Rocket Chip), эти два интерфейса нужны для доступа к основной памяти и для загрузки ядра RISC-V (бутстрап). Последний используется для управления CSR (control-status registers) и поддержки системных вызовов (для операций ввода-вывода) из целевой машины (на которой запущено прокси-ядро или ядро Linux) в хост-машину (т.е. в двухядерный процессор ARM в Zynq FPGA), на которой запущен инстанс riscv-fesvr (front end server) для диспетчеризации системных вызовов в операционную систему (т.е. ARM Linux).
Для конфигурирования ядра вы можете также изменить значения внутри файла Config.scala. Этот файл содержит набор конфигураций для различных применений (модель C++, FPGA обычного размера, маленькая FPGA, VLSI, и т.д.). Всё, что делает скрипт, принимающий переменную CONFIG=XXX (т.е. DefaultFPGAConfig), это устанавливает требуемую конфигурацию, используемую для сборки симулятора C++ либо Verilog RTL для FPGA или VLSI. Это очень полезная вещь, если вы хотите экспериментировать с разными доступными опциями, быстро генерировать и тестировать их в программном либо аппаратном виде.
Хорошей идеей является скомпилировать тулчейн (с Newlib лучше всего), прежде чем генерировать любые ядра, так как вам нужно компилировать и запускать тесты для верификации вашего ядра с помощью симулятора C++ или Synopsys VCS. К сожалению, я смог использовать только первый, но не последний. Вы не можете использовать любой симулятор Verilog (например, Verilator, Icarus Verilog, Modelsim, QuestSim), так как тестбенч использует функции DirectC (связывающие тестбенчи Verilog и произвольный код C/C++). К счастью, симулятор С++ обеспечивает вывод VCD и я смог использовать GTKWave для просмотра сгенерированных временных диаграмм тестов.

Я также добавил сессию с простыми командами, нужными для сборки тулчейна RISC-V и генерации ядра RV64G для FPGA. Эти команды будут добавлены в финальный скрипт компиляции, который я разрабатываю для полной автоматизации всего необходимого для того, чтобы получить рабочее ядро RISC-V внутри чипа Zynq на Parallella.
# Скачиваем и инициализируем репозиторий Rocket Chip git clone https://github.com/ucb-bar/rocket-chip.git cd rocket-chip git submodule update --init export TOP=$(pwd) # Устанавливаем необходимые пакеты (Ubuntu) sudo apt-get install autoconf automake autotools-dev curl \ libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex \ texinfo gperf libtool patchutils bc # Компилируем тулчейн RISC-V (используется Newlibдля работы без ОС) export RISCV=$TOP/riscv-tools # Добавляем всё, что будет скомпилировано, в пути) export PATH=$PATH:$RISCV/bin cd $RISCV git submodule update --init --recursive # Редактируем файл build.common # JOBS = Количество ядер / потоков, которые поддерживает ваша машина ./build.sh # Тестируем тулчейн RISC-V, скомпилировав простую программу # и запускаем её на Spike (симулятор ISA) # с использованием имеющегося прокси-ядра (pk) cd $TOP echo -e '#include <stdio.h>\n int main(void) \ { printf("Hello world!\\n"); return 0; }' > hello.c riscv64-unknown-elf-gcc -o hello hello.c spike pk hello # Опционально: компилируем эмулятор Rocket Chip # Заменить -j8 на -jN (количество ядер/потоков на вашей машине) cd $TOP/emulator make # Запускаем тесты (внимание: это может занять некоторое время) # make -j8 run-asm-tests # make -j8 run-bmark-tests # лучше просто запустить конкретный тест: make output/rv64ui-p-add.out # Опционально: компилируем эмулятор Rocket Chip с выводом VCD # Меняем -j8 на -jN cd $TOP/emulator # Если у вас нет утилиты vcd2vpd, открываем src/main/scala/Testing.scala # файл и меняем все .vpd на .vcd (6 раз) make debug # Запускаем тесты (внимание: это может занять время и место на диске) # make -j8 run-asm-tests-debug # make -j8 run-bmark-tests-debug # запускаем определённые тесты и тогда нам не нужен приведённый выше # хак при отсутствии утилиты vcd2vpd: make output/rv64ui-p-add.vcd # Генерируем Rocket Chip Verilog RTL для FPGAs (в fsim/generated-src/) # Дефолтная конфигурация "DefaultFPGAConfig", но вы не можете её изменить # например, на "SmallFPGAConfig" если дефолтная реализация с FPU # не влезает в ваше устройство. Используем make CONFIG=ConfigName verilog для установки # желаемой конфигурации из числа доступных (src/main/scala/Configs.scala) # Финальный вывод Verilog RTL называется Top.ConfigName.v cd $TOP/fsim make verilog
В следующем посте мы рассмотрим, как получить IP-блок из сгенерированного ядра RV64G для того, чтобы его можно было легко подключать к проектам, для тестирования на Zedboard или для использования в большом проекте на Parallella.