Запуск DOOM на процессоре собственной разработки

Культовый шутер Doom, представленный студией id Software в 1993 году, навсегда изменил облик игровой индустрии. Сегодня игра стала мерилом технического прогресса: фраза «запустить Doom на чём угодно» давно стала девизом энтузиастов, которые переносят классику на самые неожиданные устройства — от бытовой техники до колоний бактерий.
Две недели назад мы совершили невозможное: запустили Doom на собственном процессоре, спроектированном «с нуля». Видео с этим достижением мгновенно стало виральным, собрав миллионы просмотров. Мы проделали огромный путь: от разработки архитектуры CPU на уровне логических вентилей и создания периферийных контроллеров до портирования исходного кода игры и реализации всего этого комплекса на базе FPGA в режиме реального времени. Ранее наша система справлялась лишь с примитивными задачами вроде Pong или фракталов Мандельброта, но теперь она способна запускать полноценные коммерческие игры, хотя реализация этого проекта оказалась крайне тернистой.
Амбициозные цели
Начав с конвейерной архитектуры, мы стремились к покорению новых высот. Однако, оставив позади 50-летние игры, мы столкнулись с жесткими ограничениями: нехваткой памяти и низкой производительностью. Вся доступная BRAM-память нашей FPGA составляла менее мегабайта, в то время как лишь один файл данных shareware-версии Doom (doom1.wad) весит 14 МБ. Кроме того, на нашей базовой архитектуре игра едва функционировала, превращаясь в бесконечное слайд-шоу.
Решение требовало разделения усилий. Лиам занялся внедрением механизмов исполнения с изменением последовательности (out-of-order execution) для повышения параллелизма, а я взял на себя задачу интеграции внешней памяти DDR3 — задачу обманчиво простую на бумаге, но сложную в реализации.
Работа с памятью
Изначально наш процессор полагался на внутреннюю BRAM с предсказуемой задержкой в один такт. Это позволяло избежать простоев конвейера. Внешняя DDR3-память работает по иным правилам: она медленнее, обладает переменной латентностью и широкой шиной данных. Чтобы не допустить падения производительности CPU до катастрофических значений, нам потребовалось внедрить систему кэширования. Грамотно реализованный кэш позволяет удерживать наиболее востребованные данные в быстрой BRAM, минимизируя обращения к «тяжелой» DDR3.
Архитектурные решения
Наш процессор построен по классической пятиступенчатой схеме: выборка (Fetch), декодирование (Decode), считывание регистров (Read), исполнение (Execute) и запись (Writeback). Каждая стадия абстрагирована через единый интерфейс.
Основные узлы конвейера
Стадия Fetch теперь опирается на кэш команд (ICache) и самостоятельно управляет задержками при перенаправлении потока выполнения. В условиях работы с DDR3 это стало сложнее, так как запросы к памяти могут быть отправлены до момента осознания необходимости перехода, что потребовало от нас механизмов отмены неактуальных операций.
Этап Decode выполняет трансформацию 32-битных инструкций в управляющие пакеты, понятные исполнительным блокам.
Блок Read претерпел серьезные изменения: чтение теперь также конвейеризировано. Для предотвращения конфликтов данных (RAW — Read-After-Write) мы внедрили Register Usage Map (RUM). Эта 32-битная карта состояния регистров позволяет процессору динамически решать, когда следует приостановить выполнение, что значительно надежнее прежних попыток предсказания состояний.
Стадия Execute управляет логикой вычислений, передавая команды в АЛУ или модули доступа к памяти. В случае ветвления конвейер очищается, а при работе с памятью процессор уходит в режим ожидания ответа от кэша.
Подсистема памяти
Поскольку процессор требует данные в каждом такте, а DDR3 выдает их порциями с высокой задержкой, мы разделили кэши на командный (ICache) и данных (DCache). Каждый из них имеет архитектуру прямого отображения с 2048 линиями. Метка (tag) гарантирует корректность данных, а сигналы валидности и модификации (dirty bit) обеспечивают целостность при записи.

Управление конфликтами
Для разрешения одновременных обращений двух кэшей к единственному контроллеру DDR3 был создан блок арбитража. Он выстраивает запросы в очередь и отдает приоритет DCache, что предотвращает взаимные блокировки системы.
Интеграция с реальным «железом»
Переход от симуляции к реальному FPGA потребовал использования IP-блока Xilinx MIG для работы с DDR3. Из-за проблем с таймингами и ограничениями текущей платы мы перешли на конфигурацию с 64-битной шиной. Стабильность работы между различными тактовыми доменами обеспечили FIFO-буферы и методы CDC (Clock Domain Crossing).
Периферия и ввод-вывод
Для взаимодействия с внешним миром мы реализовали MMIO. Контроллер VGA был расширен до поддержки 12-битной палитры и вывода через HDMI. UART-порт с FIFO-буфером взял на себя роль отладочного канала, а вместо USB-хоста, с которым возникли аппаратные сложности, мы организовали передачу нажатий клавиатуры через серийный интерфейс с ноутбука.
Портирование Doom
Адаптация игры проходила с помощью проекта doomgeneric. Процесс сопровождался борьбой с багами: от проблем с printf до нестабильности при обработке прерываний. Выяснилось, что ряд ошибок был вызван неверными предположениями игры об обнулении оперативной памяти при старте — пришлось внедрить принудительную инициализацию DDR3 в коде загрузчика.

Стартовая производительность составила всего 0,7 FPS, что заставило нас перейти к этапу глубокой оптимизации.
Путь к играбельной частоте кадров
Повышение тактовой частоты до 125 МГц дало незначительный прирост. Основные успехи пришли с оптимизацией конвейера (устранение дублирующих запросов ICache) и переходом на архитектуру RV32I-ZMMUL. Прямая запись данных в BRAM контроллера VGA и сокращение латентности кэша до одного такта подняли FPS до 6,7.
Финальным рывком стала компиляторная оптимизация (флаг O2). После устранения бага с пропущенным квалификатором volatile для MMIO-регистров таймера, игра наконец «взлетела», показав стабильные 15-20 FPS.
Заключение
Этот проект стал для нас настоящей школой инженерного мастерства. Мы прошли путь от примитивной логики до сложной системы с кэшированием, арбитражем памяти и оптимизацией ПО. Вирусный успех нашего видео лишь подтверждает: сообщество ценит смелые эксперименты. В планах — дальнейшее развитие архитектуры, внедрение полноценного out-of-order исполнения и, возможно, будущий портинг Quake 2 на более мощной аппаратной базе.
Эволюция футбольных симуляторов: от истоков до наших дней
Возвращение морских сражений в Battlefield 6: что нового в четвертом сезоне?
Как купить Assassin’s Creed Black Flag Resynced на ПК, PS и Xbox в 2026 году
Разработка торгового мода для Avorion: от интерфейса и Lua до скрытых проблем
Как создавали Heroes of Might and Magic V: история спасения культовой серии
Как я взломал Soundcore: кастомная прошивка и установка DOOM. Часть 1
«Жизнь и страдания господина Бранте» бесплатно раздают в Steam до 23 июля
Doom запускается на всем, включая Neo Geo