программы

Quake 2 на нашем RISC-V, или как мы поднимали старый Radeon на FPGA

538
2
21 октября 2024
Изображение создано с помощью нейросети
программы
538
2
21 октября 2024
Quake 2 на нашем RISC-V, или как мы поднимали старый Radeon на FPGA

RISC-V активно развивается как основная application-платформа в мире, и на ней уже много раз запускали стандартные программные тесты. А что, если запустить на RISC-V, например, Quake 2? Это новый вызов для экосистемы — особенно в ракурсе интерконнекта и производительности памяти.

Александр Разинков, ведущий инженер-программист группы разработки операционных систем, сделал это на RISC-V-кластере с двумя ядрами российского производства. В этом ему помог видеоадаптер Radeon 4350, выпущенный в далеком 2008 году. Проект Алексея и его команды вылился в захватывающий инженерный эпос в девяти актах — и колец здесь оказалось больше, чем у Фродо!

Изображение создано с помощью нейросети
Из статьи вы узнаете
  • из чего состоят драйвера видеокарт конца 2000-х
  • как можно решить ряд проблем, связанных с этими драйверами
  • как Vivado помогает восстанавливать работу старого «железа»
  • как быстро работает однопоточный Quake 2 на RISC-V-кластере и Radeon 4350

Цикл разработки процессорных ядер включает моделирование его работы через FPGA. Это позволяет провести полноценное тестирование до отправки дизайна «в печь».

Итак, наша исходная конфигурация такова:

  • FPGA-стенд под рабочим названием Soren, на котором смоделирован двухъядерный процессор частотой 120 МГц,
  • NVMe SSD, подключенный через PCIe,
  • видеокарта Radeon HD 4350, актуальная 15 лет назад и подключенная через другой PCIe,
  • Ubuntu 22.04,
  • монитор, подключенный через видеокарту,
  • веб-камера для удаленного наблюдения за стендом.

Что может пойти не так? Много чего, даже по самым оптимистичным оценкам. Видеоадаптер, поддержка которого остановилась на Linux 3.4, может не подружиться с актуальным Linux 6.6. А архитектуры RISC-V во времена этого Radeon вообще не существовало.

FPGA-стенд с Radeon 4350

Незадолго до выпуска нашего адаптера AMD купила ATI, и с этого переходного периода документации по картам в принципе осталось немного. Так что поиск приводит нас в основном к слезным тредам тех, у кого адаптер отваливался после обычного обновления ядра.

Проверить правильность выбранного драйвера и конфигурации ядра Linux было невозможно. Radeon мы тоже не из коробки достали, до этого он лежал на полке лет 8−10 — за его работоспособность ручаться было нельзя. Вишенкой на торте было то, что у нас в команде не было инженера с опытом работы в видеоподсистеме. В целом наши стартовые условия я бы сравнил с болотом, где отчаянно пытаешься нащупать хоть какую-то кочку, чтобы начать выкарабкиваться и осмысленно дебажить.

ошибки драйвера Radeon 4350

Для начала мы просто запустили Ubuntu. Конечно же, получили гору ошибок! Для PCIe не был выделен input/output-регион, ROM-хедеры оказались некорректны, а BIOS не найден… В общем, гарантированный fatal error. Начинаем разбираться.

ошибки драйвера Radeon 4350

Что такое input/output-регион для PCI? Это довольно древняя сущность, необходимая для совместимости со старым PCI. Оказалось, что Xilinx PCIe Root Complex наших FPGA уже невозможно сконфигурировать с таким регионом. Возникает мысль отказаться от всей затеи: стоит ли вообще заморачиваться с таким старым адаптером? Купим карту поновее и будет проще. Или хотя бы проверим эту с привычным x86.

Нет, мы продолжили идти по своему пути и были вознаграждены. Поиск подсказал, что I/O-пространство даже для PCIe v1.0 является легаси-фичей для совместимости с более древними PCI-устройствами. Так что ошибка эта не фатальна, она возникает и при успешном запуске Radeon.

Квест 1.2. Битва за PCIe ROM

Изучаем дальше.

ошибки драйвера Radeon 4350

Здесь имеется в виду BAR6 — видимо, какая-то флешка на самой карте. Она видна в PCIe, но драйверу что-то в ней не нравится.

ошибки драйвера Radeon 4350

Посмотрим lspci Linux:

Radeon 4350 в lspci Linux

Видим, что регион выключен! Значит, и декодер адресов для Expansion ROM (BAR6), этой флешки, на карте выключен, то есть обращения по этим адресам недоступны. Чтобы включить декодер, надо записать 0 в регистре ROM BAR Address Register:

С помощью OpenOCD можно напрямую почитать содержимое конфигурационного пространства PCIe. Ниже отмечено, где не стоит нужный бит:

ROM BAR в Radeon 4350

Запишем его через OpenOCD и посмотрим, что будет:

ROM BAR в Radeon 4350

Регион активирован, все видно. Прочитаем теперь эту флешку:

ROM BAR в Radeon 4350

Внутри пусто, одни ноли! Но, судя по упоминаниям BIOS выше, там должен быть некий BIOS.

Квест 2. Radeon ATOM BIOS

Поиски официальной документации успехом не увенчались, и мы погрузились в форумы десятилетней давности. Там реверс-инженеры сливали дампы и пытались по ним разобраться, что же это такое. Благодаря им мы поняли, что перед нами байт-код для архитектуры x86, содержащий настройки для ROM видеокарты. Из него мы можем узнать уровни напряжения, коды инициализации, параметры настройки PLL отдельных блоков, в том числе рабочие байт-код команды. В драйвере есть интерпретатор, который умеет это читать, и драйвер через него взаимодействует с видеокартой. Без интерпретатора вообще ничего не заработает, но найти на карте BIOS с ним мы не можем!

Что ж, когда гора не идет к Магомеду, Магомед идет к горе. Попробуем скачать ATOM BIOS из интернета и подсунуть драйверу. Подложили в начало DDR, немного хакнули сам драйвер. В итоге он прочитал ATOM BIOS, покрутился и выдал Fatal error during GPU init:

ошибки драйвера Radeon 4350

Квест 3. 32-bit DMA и 64-bit RISC-V

Разбираемся дальше. Driver (-12) — это ошибка out of memory в Linux. Конкретно здесь идет запрос на память для DMA32. DMA32 у нас нет, у нас 64-битная система. Что ж, давайте дадим, что есть, и посмотрим, что будет!

ошибки драйвера Radeon 4350

Запомним запрос именно на 32-битные DMA, это может пригодиться. Погасили таким образом ошибки.

Что дальше? Мы видим часть аллокации памяти. Видеокарта проинициализировалась, и начал загружаться некий микрокод.

загрузка драйвера Radeon 4350

Квест 4. Микрокод Radeon

Микрокод содержит куски прошивки для отдельных блоков, которые встраиваются в ядро Linux. Тут уже все понятнее: мы видим и можем нагуглить, что нам нужно, и ядро конфигурируется с этими кусками микрокода. Заканчиваем конфигурировать ядро:

Что получилось? Новые ошибки, много новых ошибок!

ошибки драйвера Radeon 4350

Но при этом у нас завелся DRM! Это подсистема Linux-ядра, которая организует доступ разных системных пользователей Linux к видеокарте.

DRM Radeon 4350 в Linux

Видим, что распознались коннекторы VGA и DVI. Карта теперь видна в Linux через dev/dri:

драйвер Radeon 4350

Проведем простой тест: запустим рандомный поток в этот framebuffer:

И-и… Получаем черный экран! Ничего не работает.

Radeon 4350 не выводит изображение

У Linux есть все, чтобы показать нам хоть что-то, но он не показывает ничего. После кучи шагов мы зашли в тупик и зацепиться было не за что.

Самая темная ночь — перед рассветом

На этом история могла закончиться, но Бог сжалился над несчастными инженерами, и к нам вдруг снизошло озарение. Просматривая логи и фото стенда в тысячный раз, мы заметили: у нас есть определившиеся DVI- и VGA-коннекторы, а есть фотография в Jira, где монитор подключен… через HDMI! То есть все это время монитор был подключен через порт, который не был распознан Linux. Мы откапываем древний VGA-кабель, подключаем монитор через него и видим волшебный снежок — так выглядит hello world в мире видеокарт:

Radeon 4350 выводит изображение

Ура! Мы выбрались на островок надежды!

Решилась сразу куча фундаментальных вопросов:

  • Radeon можно скрестить с RISC-V FPGA.
  • Radeon может работать с современным ядром Linux 6.6, на которое он в принципе не был рассчитан.
  • Стенд собран корректно, все наши соединения хоть как-то, но работают.
  • Можно не искать десктоп на x86, RaspberryPi и, если совсем повезет, новую видеокарту.

Теперь подумаем, что можно поднять на этом этапе.

Wayland без GPU-акселерации

Wayland — это графическая подсистема Linux уже в user space, альтернатива «иксов». Воспроизвели с ней колесики:

Radeon 4350 выводит изображение

Wayland заработал только напрямую с fb0, в обход драйвера. Производительность — пять кадров в секунду, столько выдает наша FPGA-плата с частотой 100 мегагерц, которая всю графику обрабатывает на себе. Это очень медленно.

производительность Radeon 4350 в RISC-V-стенде

Если подключить через DRM, задействуя драйвер, появляются разные краши: в системе явно что-то отвалилось.

ошибки драйвера Radeon 4350

Квест 5. DPM initialization failed

Что же не так с акселерацией? Почему драйвер не работает? Смотрим в лог. Здесь у нас множество странных ошибок, какой-то dpm, CAFEDEAD, а в итоге печальное disabling GPU acceleration:

ошибки драйвера Radeon 4350

Начнем по порядку. Что такое DPM? Это Dynamic Power Management, разгон карты под нагрузкой и отключение блоков для power save. Он может влиять на что угодно. У людей на форумах он периодически отваливается, в результате чего перестает загружаться даже десктоп. Эту подсистему просто отключают, и загрузке Linux она не мешает.

Мы руководствуемся принципом «если оно не нужно, давайте не будем связываться», так что тоже отключаем этот DPM через командную строку Linux. Сообщение драйвера об этой ошибке исчезло, но акселерация не заработала. Перед нами новая гора ошибок, в них есть строчка disabling GPU acceleration и упоминается какой-то Ring 0:

ошибки драйвера Radeon 4350

Квест 6. Битва за Ring 0

Что это за Ring 0? Раскопки форумов, анализ кода и скудная документация на еще более древние поколения Radeon приоткрыли завесу тайны.

Ring 0 в Radeon 4350

RIng 0 — это аппаратное кольцо, через которое CPU общается с GPU, с Radeon. В нашем случае запускается самый базовый тест на DMA. CPU пишет в SCRATCH-регистр Radeon значение 0xCAFEDEAD и шлет через кольцо управляющую команду: «GPU, запиши в SCRATCH 0xDEADBEEF». Если у Radeon есть хотя бы базовая связь с нами, тест должен проходить, но не проходит.

Почему же не работает DMA, почему Radeon не читает Ring 0? Вариантов несколько.

  • DMA Engine выключен. Документации у нас нет, мы не знаем, что там вообще происходит.
  • Драйвер кривой и не предназначен для работы с этим ядром Linux. Но мы верим в лучшее.
  • Radeon не видит Ring 0.
  • Сломана адресация, и Radeon читает не оттуда — нас же постоянно ругают за 64 бита. Как такое проверить…
  • Ring 0 кешируется, и Radeon читает из DDR, а не из кешей. Это единственное, что можно проверить легко.

Память, которую мы выделили под этот Ring 0, оказалась обычной кешируемой памятью ядра, что сразу дало +1 к последней гипотезе. Все, что туда попадает, идет в кеши. В драйвере нет flush/invalidate — специальных инструкций, которые скидывали бы данные из кеша или затягивали обратно. Мы их добавили, но ошибка все равно возникает:

ошибка Ring 0 в Radeon 4350

Опять очередной мучительный дебаг… Мы решили просто принудительно сбросить кеш в DDR для всего, что связано с этим Ring, в том числе управляющие структуры, которые лежат вообще в другой памяти. И Ring test 0 прошел!

Ring 0 в Radeon 4350

Какие можно сделать выводы? Железо GPU умное: оно умеет не только в Ring смотреть, но и управляющую конструкцию распарсить и все это обновлять. Ring — кольцевой буфер с read pointer и write pointer, и они модифицируются в процессе передачи данных. DMA read работает, и 64-битная адресация этому не помеха. Cache flush в драйвере у нас нет, а это значит, что все предполагает работу через когерентный DMA, когда GPU мониторит кеши CPU, пишет и читает напрямую из них, а не из DDR. В этом режиме мы работать не умеем. Собственно, одной из целей эксперимента и было определить, к чему наше железо не готово.

Но у команды YADRO есть в рукаве козырной туз: попросить помощи у FPGA-инженеров. «Держите когерентный DMA порт!» — ответили нам инженеры. Спасибо! Теперь можно убрать cache flush/invalidate и увидеть надпись с картинки выше уже без костылей. Дальше в логе всплывает строчка уже про какой-то Ring 3 и снова про CAFEDEAD:

ошибка Ring 3 в Radeon 4350

Квест 7. Битва за Ring 3

Начинаем сначала. Что это за Ring 3? Увы, поиск по форумам и другие привычные методы ни к чему не привели. Судя по коду, это похожий на Ring 0 кольцевой буфер с приставкой DMA, но с обратным тестом. CPU пишет в свою память 0xCAFEDEAD и просит: «Radeon, запиши нам в память 0xDEADBEEF». Но этого не происходит.

Что нам с этим делать? Может, дело в адресации. Может, DMA работает на чтение, а на запись нет. Океан возможностей. Но посреди него у нас есть островок стабильности — Ring 0, с которым мы уже прошли тест.

А что, если попробовать записать в память из Ring 0? Так мы проверим возможность DMA на запись со стороны Radeon. Раз нет документации, прибегнем к реверс-инжинирингу. Находим в .h-файле Linux похожую по смыслу команду:

Ring 3 в Radeon 4350

Ищем, как эта команда используется в коде, адаптируем под свои нужды. Попробуем написать 0xCOFFEE42 в начало DDR, которое у нас находится по адресу 0×40000000:

Ring 3 в Radeon 4350

Не сработало. Что не так? В этот момент понимаешь, что софтверных возможностей, к которым привыкли обычные разработчики, уже не хватает. Мы тычемся как слепые котята. Значит, пришло время расчехлять тяжелую артиллерию — Vivado! Для разработчика ПО это совершенно новый и дивный мир. В нем можно увидеть состояние внутренних шин CPU-кластера, которое никак не узнать софтовыми средствами со стороны процессора.

С Vivado мы видим, как Radeon мониторит память процессора, где размещен Ring 0. В сторону памяти процессора идет какая-то запись по одному и тому же странному адресу, который ни с чем не ассоциируется. Наш 0хCOFFEE42 от Ring 0 не видно, а память, где размещен Ring 3, видеокарта даже не мониторит: по нему нет абсолютно никакой активности.

Посмотрим, что это за странные записи по странному адресу. Похоже на обновления Read Pointer кольцевого буфера, но при этом адрес никак с ним не ассоциируется. Начинаем рыть код, и в какой-то момент нас осеняет: Radeon использует трансляцию адресов! Он работает не с физическими адресами, а со своими внутренними. У него внутри есть таблицы трансляции из своих виртуальных адресов в наши физические. И мы в начало DDR как раз записали такой физический адрес. Исправляем на внутренний адрес Radeon:

Ring 3 в Radeon 4350

Ура, наш COFFEE42 появился в Vivado!

Radeon 4350 в Vivado

Но что же не так с Ring 3? Почему вообще нет реакции на команды и даже мониторингов никаких нет?

Возможно, дело в первом попавшемся ATOM BIOS? Нет, не в нем: мы перебрали 20 других, и эффекта это не дало. Возможно, дело в каком-то нештатном состоянии самого Ring 3? Смотрим в регистр Ring 3, видим в коде статус 0x44483146. Что бы ни значила эта последовательность, документации для ее расшифровки у нас нет.

В коде мы нашли говорящий статус DMA_IDLE — за это состояние отвечает нулевой бит.

Ring 3 в Radeon 4350

Статус выше заканчивается на 6, значит, бит IDLE не проставлен, и мы не в IDLE.

Что делать дальше? Давайте все перезагрузим, вдруг повезет. А как это сделать правильно без документации? Опять роемся в коде драйвера, находим надпись:

драйвер Radeon 4350

Согласно ей, если аппаратная акселерация не работает на AGP, можно откатиться на PCI. Далее идет соответствующая инструкция. Что ж, раз у них это когда-то работало, попробуем и мы.

драйвер Radeon 4350

Ура, бит поднялся! Мы перезагрузили Ring 3 и наконец увидели долгожданное прохождение теста:

Ring 3 в Radeon 4350

Получается, у нас был отломан целый блок. И без дополнительной перезагрузки он не приходил в штатное состояние. Конечно, в логе осталась еще куча ошибок:

драйвер Radeon 4350

Но при этом грустное сообщение disabling GPU acceleration пропало и появился HDMI:

драйвер Radeon 4350

Wayland заработал через драйвер, с DRM-бэкендом и без крашей. Это огромный шаг вперед! А что у нас с акселерацией?

FPS Radeon 4350 в Linux

Увы, пока акселерация не работает, видим те же пять фреймов в секунду. Ну, хотя бы драйвер ее уже не отключает.

Стали доступны внутренние бенчмарки драйвера:

бенчмарки драйвера Radeon 4350

Скорость передачи данных между Radeon и процессором очень низкая — 16 Мбит/с. Но DMA как-то работает, и это дает надежду!

Квест 8. Битва за Ring 5?

Почему не работает акселерация? Давайте разбираться.

ошибки драйвера Radeon 4350

Что это за гора ошибок UVD not responding? Находим, что UVD — это Unified Video Decoder. Он отвечает за аппаратное декодирование таких форматов, как MPEG-2, H.264, AVC.

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

драйвер Radeon 4350

Что он означает, не очень понятно, но вроде бы нерабочее аппаратное декодирование не должно быть блокирующей проблемой. Оставим всю портянку ошибок как есть.

Квест 9. Битва за прерывания

Почему же не работает акселерация? Может, дело в том, что нас постоянно ругают за 64-битную архитектуру, тогда как MSI ограничена 32 битами:

драйвер Radeon 4350

Что нам говорит Linux по поводу прерываний? «Прерываний нет у вас, ребят, они не ходят».

драйвер Radeon 4350

Почему не ходят прерывания? Анализ кода открывает вот такой комментарий:

ограничения драйвера Radeon 4350

Однажды какой-то разработчик под IBM Power обнаружил, что на их серверах эта карта не работает, поскольку нормально не работают 64-битные прерывания. Так что он откатил 64-битные прерывания на 32-битные, и это помогло. Но для нас это шоу-стоппер, потому что мы 64-битная платформа. Неужели все?

Если приглядеться, то наш DDR по 0×400000000 — это 35-битный адрес. В комментарии к фиксу написано, что поддерживаются только 40-битные адреса прерывания, так что мы выпилим этот фикс.

драйвер Radeon 4350

Прерывания сразу полетели! Оказалось, что Radeon-таки умеет в 35-битные прерывания. Акселерация наконец заработала!

Вот сравнение без акселерации и с ней:

производительность Radeon 4350 на RISC-V-стенде

В некоторых случаях разогнались примерно в тысячу раз! В Wayland мы уперлись в фиксированную частоту обновления монитора — это чисто конфигурационный параметр. А бенчмарки сразу показывают огромный прирост.

Quake 2 на российском RISC-V

У нас распределенная команда, мы работаем в основном удаленно: из Питера, Москвы, Нижнего Новгорода и дальше на восток. Мы редко съезжаемся вместе, но как раз на следующий день после описанных событий к 10 утра все по плану должны были приехать в петербургский офис YADRO. В ночи на кухне я задумался: успею ли что-нибудь показать, чтобы порадовать коллег?

Ubuntu на стенде серверная, никакой графики нет. Поставил «иксы». Ребята советуют: давай что-нибудь типа Doom, Quake 2. Попробуем! У нас FPGA, 100 МГц, здоровенный Quake 2 собирается нативно очень долго. На ночь поставил на сборку, а утром уже надо было ехать. В электричке, автобусе дебажил на ходу и в какой-то момент увидел вот это:

Quake 2 работает на стенде с RISC-V и Radeon 4350

Я был в восторге: получилось 7−17 FPS в зависимости от числа врагов. Это здорово, учитывая, что Quake 2 выпущен в 1997 году без поддержки многопоточности, а мы имеем дело с FPGA — довольно медленной технологией в принципе.

Финальные титры

Этот проект — общая победа нашей команды, работающей над кластером на RISC-V. Мы живем в удивительное время, когда ты можешь ехать в автобусе и с напарником из Сибири удаленно дебажить будущий процессор где-то в лаборатории в Москве. Десять лет назад это и представить было нельзя.

Когда результат можно пощупать руками, причем таким веселым образом, это тоже вдохновляет. Вот она, прелесть инженерной профессии!

В инженерном эпосе также снимались:

  • FPGA,
  • двухъядерный RISC-V-кластер с частотой около 120 МГц,
  • два PCIe c когерентным DMA,
  • Radeon HD 4350 (2008 г. в.) c GPU-ускорением,
  • Linux kernel v6.6 и драйвер Radeon.
Историю проекта Александр также рассказал на конференции FPGA-Systems 2024.1. С другими докладами конференции вы можете ознакомиться в нашем посте на Хабре. А 26 октября в Санкт-Петербурге пройдет уже FPGA-Systems 2024.2 — регистрируйтесь на онлайн- и офлайн-участие!
Наверх
2 комментария
  • Здорово, конечно, но практический смысл в этом всём то какой?

    Видеокарта старая. Недокументированная. Неподдерживаемая. Один набор функций кое-как заработал, а остальное? Каждый раз напильником допиливать? Я понимаю — свое графическое ядро — в этом есть еще смысл и ваш работодатель еще платит вам за такой мартышкин труд?)) Ну-ну.

    • Здравствуйте, Вася! В начале статьи мы писали о ценности тестов с графикой: DMA GPU нагружает подсистемы процессора, память и интерконнект намного интенсивнее обычных системных тестов. А это ценно, мы стараемся тщательно тестировать свои ядра. Кроме того, это позволило нам доработать нашу систему с аппаратной точки зрения. Например, мы добавили поддержку когерентного DMA со стороны PCIe. Также это расширило наши компетенции в работе с графической подсистемой Linux. Да и, в конце концов, это просто интересно с инженерной точки зрения! У этой истории счастливый конец, а мы получили драйв и море удовольствия!