В книге Inside an Open-Source Processor: An Introduction to RISC-V заявлено, что процессор YRV реализует подмножество шины AHB-Lite. Поэтому у меня возник вопрос: можно ли использовать с этим процессорным ядром примеры из репозитория MIPSFpga Plus (у меня печатный букварь Харрисов еще по MIPS, так что не забываю).
Здесь же возникает следующий вопрос: как реализовать шину внутри SoC? Ведь доступа к исходникам MIPSFpga уже нет, репозитории закрыты или удалены. «Дом-то снесли. — Снесли... А стеночка осталась». Нам помогут:
Очень хорошая презентация Станислава Жельнио «Работа с периферийными устройствами и памятью посредством шины AHB-Lite, пример миграции стороннего модуля на шину». Действительно хорошая презентация, некоторые картинки я взял оттуда.
Статья в LiveJournal Юрия Панчула «Как делать парсинг текста голым хардвером, без процессора и без софтвера». На дворе 2025 год, а я на ЖЖ ссылку делаю.
Статьи на Хабре по теме MIPSFpga.
Проект будет без реализации на ПЛИС, и отладки, любезно предоставленные FPGASystems.ru, не использовались. Это только доказательство возможности портирования, к тому же я хотел получить простой проект исключительно для симулятора, чтобы впоследствии запускать разные примеры и смотреть диаграммы.
Разрабатываем мост YRV – AHB
Шина AHB-Lite из книжки System-on-Chip Design with Arm Cortex-M Processors выглядит так:

Шина в микроконтроллере YRV выглядит совершенно по-другому, но в книге довольно подробно рассмотрено соответствие сигналов и даже есть вот такая табличка.

Вызывают вопросы сигналы, которые помечены в таблице как (see text). С сигналом mem_trans особо проблем нет: второй байт — mem_trans[1] — эквивалентен сигналу HPROT[0], а первый байт — mem_trans[0] — эквивалентен сигналу HTRANS[1]. В YRV нет привилегированного режима, а также кешей, поэтому на шине AHB-Lite будет использоваться только первый бит сигнала HPROT.

YRV не поддерживает последовательный доступ, поэтому будет использоваться только второй бит HTRANS. Это и объясняет, почему в Coremark у процессора довольно низкий результат.

Таким образом, сигнал mem_trans соответствует сразу двум сигналам шины AHB-Lite.
А вот с сигналом mem_ble все сложнее. В целом сигнал выполняет то же самое, что и HSIZE, но немного по-другому: сигнал указывает значащие биты на шине. Если в случае чтения все равно защелкивается полслова или слово целиком, то в случае записи сигнал маркирует биты, предназначенные для записи. А у HSIZE нет размера в три байта, поэтому прямое преобразование mem_ble в HSIZE затруднительно.

Обратим внимание на сигнал bus_32, он переключает шину процессора с 32 бит на 16 бит. Так как на самом деле это и есть «фишка» процессора, то запустим процессор в режиме узкой шины. Например, будем считать, что у нас внешняя 16-битная RAM. В общем, полслова с шины — дизайнеру легче.
В этом случае сигнал mem_ble будет соответствовать младшему биту HSIZE. Чтобы считать старший байт, просто инкрементируем адрес.

Подключаем YRV к шине AHB-Lite
Чтобы подключить ядро YRV к шине AHB-Lite, посмотрим статью Юрия Панчула «Как делать парсинг текста голым хардвером, без процессора и без софтвера». Сам парсер нам без надобности, но статья описывает назначение всех компонентов для построения шины. Так как парсер — это мастер-устройство, то видно, что он подключается через мост (bridge), преобразующий транзакции. Аналогично разработаем мост для YRV по образу и подобию mfp_srec_parser_to_ahb_lite_bridge.v. Результирующий модуль называется yrv_ahb_bridge.
Матрица AHB-Lite
Осталось собрать SoC. Сама матрица AHB-Lite сохранилась в проекте MIPSfpga+ и доступна на GitHub. В проекте YRV я переименовал файл yrv_mcu.v в yrv_soc.v, удалив все, что не касается подключения к ядру, и там же разместил мост yrv_ahb_bridge. Да и вообще постарался удалить все лишнее, что не нужно для симуляции. Само подключение к матрице AHB-Lite аналогичное, и неплохо описано в статьях на Хабре.
Файл mfp_ahb_lite_matrix.v скопирован из репозитория. В нем изменена только разметка памяти. Адреса я заимствовал у SoC PicoRV32. Мне показалась удачной идея размещения оперативной памяти (памяти данных) в начале адресного пространства, а память программ — с адреса 0x01000000. Так как оперативной памяти у нас все равно много не будет, то пусть она будет вначале. Оставил ссылку на видеопамять — может быть, когда-нибудь соберу на этом ретрокомпьютер. Область данных портов ввода-вывода так и осталась по адресу 0xffff0000.
Все остальное напрямую заимствовано из MIPSfpga+. Файлы RAM — mfp_ahb_ram_slave.v и mfp_dual_port_ram.v — я скорректировал с учетом 16-битной шины данных. Модуль mfp_ahb_gpio_slave послужил основой для модуля с родными портами YRV, которые именовались в проекте как port0…port5. Результирующий модуль называется mfp_ahb_port_slave. UART пока убрал, вывод символов осуществляется в порт port0. Плагин surfer хорошо умеет декодировать значения в ASCII, поэтому просто регистра будет достаточно.
Запускаемая программа
Для тестирования напишем простенькую программу из нескольких ассемблерных команд, которая будет записывать в порт port0 слово HELO, как в протоколе SMTP, но с некоторыми извращениями. Исходники с Makefile и gcc я выложил на GitHub.

Программа записывает в оперативную память по адресу 0x00000000 слово 0x00450000, в третьем байте которого содержится ASCII код буквы E. Как дальше жить с NULL pointer, непонятно. Затем записывает в порт букву H. Читает третий байт из оперативной памяти, где должна быть записана буква E, выводит в порт port0 просчитанное значение, а затем еще два оставшихся символа L и O.
Сборка ровно такая же, как и в предыдущих проектах. C одним но: у родного YRV старт программ начинается с адреса 0x200, а выше находятся обработчики прерываний и ошибок. Я ничего не стал здесь менять, и, естественно, сейчас процессор стартует с адреса 0x01000200, так как память программ начинается с адреса 0x01000000. Для этого в файле yrv_opt.v необходимо поправить параметр `define RST_BASE.
Linker script поправлен с учетом старта с этого адреса. В принципе, код можно было вытащить и напрямую из объектного файла, но пусть это будет заготовка на будущее. Результирующий файл mem.mem16 будет содержать программу в формате загрузки в регистровый файл командой readmemh$.
Тестбенч
Тестбенч tb_yrv_mcu.sv взят из проекта YRVPlus. Оставил в нем только reset и clk, а также запись в порты port4 и port5 случайных значений. Хоть это и не нужно в нашем тесте, но убирать не стал.
Запуск производится скриптом simulate.sh, который вызывает iverilog. Как я написал выше, для просмотра VCD я использую плагин surfer. Запускаем, смотрим значение порта port0 и видим, что наш SoC немножко работает:

Процессор шуршит, вычитывает по два байта, данные приходят в следующем такте.

Можно накидать других сигналов и посмотреть, как преобразуются mem_ble в HSIZE или mem_trans в HTRAS или HPROT. Вроде все соответствует задуманному.

Что дальше
Дальше я планирую запустить пример с медленной памятью из MIPSFpga+, а также посмотреть примеры работы с памятью от ARM — там есть пример, как readmemh$ раскидать на четыре чипа по 8 бит.
Хотя ядро YRV в CoreMаrk проигрывает Cortex-M0, но зато оно открыто. Поэтому можно спокойно открывать документацию ARM или книгу System-on-Chip Design with Arm Cortex-M Processors. Знакомиться с примерами построения SoC от ARM — они-то как раз открыты и бесплатны — и пытаться построить собственную реализацию шины. Не помешает скачать репозиторий с модельками и вообще поискать материалы в GitHub по запросу AHB-lite matrix.
А можно взять книгу «Программирование на языке ассемблера RISC-V» Стивена Смита под редакцией многоуважаемого А. Ю. Романова, делать примеры и смотреть, что же реально происходит на шине. Конвейер-то подлиннее, чем у некоторых, в шесть стадий.
Исходники проекта, описанного в статье, выложены в моем репозитории.