EdigE
← ЛЕНТА
FPGA · 24.07.2026

Самодельный RISC-V-процессор разогнал Doom с 0,7 до 15–20 FPS

Задержка внешней DDR3 в 30–60 тактов превратила запуск Doom на самодельном FPGA-процессоре в задачу про кэши, арбитраж и синхронизацию доменов. Раздельные I-cache и D-cache, аппаратное умножение и однократный доступ к кэшу на hit подняли заявленную частоту кадров с 0.7 до 15–20 FPS. Финальная ошибка оказалась в объявлении MMIO-таймера для оптимизирующего компилятора.

Проект Hopper CPU показал 15–20 FPS в Doom на RISC-V soft-core, синтезированном в FPGA. Начальная версия выдавала около 0.7 FPS, хотя процессор уже исполнял код игры, работал с MMIO-периферией, видеобуфером, клавиатурным вводом и внешней памятью. Основной причиной оказалась разница между локальной BRAM и DDR3: доступ к встроенной памяти укладывается в один такт, тогда как используемый тракт DDR3 возвращал данные с задержкой порядка 30–60 тактов.

Ядро описано на Scala/Chisel и реализует пятиступенчатый конвейер Fetch, Decode, Register Read, Execute и Writeback. ISA относится к стандартизованному RISC-V RV32I; в поздней ревизии добавлено расширение Zmmul с аппаратным умножением. Собственными для проекта остаются микроархитектура, конвейерная логика, кэш-подсистема, арбитраж запросов и FPGA-обвязка. Контроллер DDR3 предоставлен Xilinx MIG, вокруг которого собраны FIFO и логика пересечения тактовых доменов.

Ограничение по BRAM стало заметно сразу: файл doom1.wad занимает около 14 МБ, а доступный объём встроенной памяти составляет менее 1 МБ. Для скрытия латентности DDR3 в процессор добавили независимые direct-mapped кэши инструкций и данных. Каждый содержит 2048 строк по четыре 32-битных слова, то есть 32 КиБ данных, вместе с тегами и valid-битами. D-cache хранит dirty-биты и выполняет writeback при вытеснении строки. Оба кэша делят один канал к DDR3 через арбитр; конфликтные запросы обслужляются с приоритетом D-cache, поскольку стадия данных находится дальше по конвейеру и сильнее влияет на остановку исполнения.

Liam Poole / outercloud.dev; отдельное авторство фотографии не указано.

Первая версия кэш-тракта всё ещё расходовала два такта на попадание в кэш. Сокращение hit-латентности до одного такта, устранение повторного запроса I-cache для одной инструкции и повышение частоты FPGA с 100 до 125 МГц заметно сократили число пузырей в конвейере. Аппаратный умножитель снял крупную нагрузку с программной реализации арифметики. Видеобуфер также перенесли в BRAM контроллера видеовывода: запись пикселей перестала конкурировать с процессором и кэшами за внешний DDR-канал.

Последний прирост обнаружил типичную ошибку в стыке прошивки и аппаратуры. После включения оптимизаций Doom перестал корректно работать, поскольку указатель на MMIO-регистр таймера не имел квалификатора volatile. Компилятор посчитал чтение регистра лишним и удалил его, хотя значение менялось аппаратно между обращениями. Исправление объявления вернуло тайминг игре и дало опубликованный авторами результат 15–20 FPS.

Doom в этой конфигурации оказался почтим интеграционным тестом для soft-core. Нагрузка выявила проблемы writeback-кэша, арбитража I/D-запросов, CDC между CPU и MIG, расчёта immediate-значений, начального состояния RAM и компиляции обращений к MMIO. Показатель FPS остаётся замером разработчиков, однако опубликованные исходники ядра и порта Doom позволяют воспроизвести архитектурные решения и проверить их на другой FPGA-платформе.