River Raid на FPGA

Ще не робили River Raid на FPGA? Ок, тоді я зроблю.


Зовсім недавно FPGA для мене був чорним ящиком, а Verilog здавався взагалі магією — ну як можна написати програму, за якою будуватиметься схема на логічних елементах? Вивчити це я планував давно, але без реальної залозки навіть не хотів починати.

І ось нещодавно з Aliexpress до мене прийшло недороге і непогане пристрій на базі Cyclone IV, але з (на той момент) фатальним недоліком: документацією на китайській мові. Зізнаюся, я впав у смуток і навіть просив поради тут на Хабре. Зібравшись з силами, я таки зумів запустити примітивну програму від китайців. Пристрій заморгало світлодіодом і я про себе закричав «ура». Покопавшись в інших прикладах, навіть я, перебуваючи на початковому рівні, зрозумів, що правду кажуть: китайський код жахливий. Вчитися на кривому коді я не збирався, оскільки свербіли руки, захотів одразу написати якусь простеньку програму. Вирішив, що це буде пінг-понг: алгоритм примітивний, а результат ефектний. Модулі роботи з VGA і клавіатурою я побачив тут на хабре статті про FPGA-Тетрисе (спасибі авторам цих модулів), а решта вже «справа техніки».

На пінг-понг я витратив кілька годин. Чесно кажучи, не раз хотілося битися головою об стіну бо я в упор не розумів чому деякі речі не виходять — сама концепція verilog йшла в розріз з будь-якими, якими я до цього зустрічав.

До речі, дуже добре допомогла фіча Quartus-а малювати логічні схеми з вихідного коду. Я зрозумів як реалізуються «в залізі» умови, цикли і тощо

Закінчивши пінг-понг, я прийшов до висновку, що завдання виявилася занадто простий і треба вибрати що-небудь цікавіше. Зрозуміло, що другим «проектом» теж буде гра — її робити цікаво і, як я вже говорив, ефектний результат. З дитинства прийшло спогад як я дивився на інших дітей, що грають в River Raid — дозволити собі пограти не міг, дорого. Подивившись відео ігрового процесу на ютубі, приступив до реалізації мрії дитинства. Забігаючи вперед, ось що у мене вийшло в результаті:



Відразу скажу: досвіду цієї галузі у мене пара тижнів, тому я не даю поради — можу помилятися і т. д. В коментарях прошу досвідчених товаришів поправити в чому я помиляюся. Дана стаття мотивуюча. Як кажуть «не боги горщики обпалюють» і ви зможете.

З чим у мене виникли труднощі?

Не можна просто взяти і записати що-небудь в регістр в різних місцях коду, якщо компілятор не може чітко визначити умови: при таких — пишемо це, при таких — це. Чому так відбувається зрозуміло — вхід на запис у регістр один і не можна подати 2 сигналу без будь-якої логіки на один вхід.

Ми програмісти любимо ділити код на класи, підпрограми і т. д. Логічно винести блок формування однієї сутності в один модуль (в термінах verilog), а формування іншої сутності — в інший модуль. Але якщо обидва модулі змінюють значення однієї і тієї ж змінної?

Вирішує такі проблеми правильна архітектура. Це і є 2я труднощі. Наскільки мені зараз бачиться, правильна архітектура в verilog трохи-чи не важливіше ніж у класичних мовах програмування. Пам'ятаю, коли я реалізував блок роботи ворогів (літаки, кораблі і т. д.), після компіляції в мене кількість задіяних елементів fpga збільшилася на кілька тисяч і це загрожувало тим, що мені взагалі могло не вистачити елементів і на мінімальний функціонал! Довелося переробляти архітектуру.

Вдале правило define-ить все, навіть розрядність змінних. На жаль, я не повністю дотримався цього правила і тому, якщо ви раптом захочете збільшити дозвіл екрану — вам доведеться поміняти пару десятків змінних.

про проект


У пристрої немає ні формувача відеосигналу ні відеопам'яті. Відеосигнал формується «вручну». Модуль, що здійснює це, надає 2 динамічних параметра: поточні x і y відповідні позиції на екрані в даний момент часу. Щоб що-небудь намалювати, потрібно постійно моніторити ці 2 параметра і в потрібний момент посилати на RGB монітора певний колір пікселя.

Як відомо, VGA використовує аналоговий сигнал для кольору, але китайці секономили — підключили по одному цифровому виходу на RGB. В результаті у мене в арсеналі всього 8 кольорів. Я, звичайно, намагався компенсувати це дозволом екрану, але все одно прийнятна картинка на 8 кольорах не вийшла. Пробував грати: в один прохід малювати точку одним кольором, в іншій — іншим, щоб отримати півтони, але нічого путнього не вийшло, миготіння дратували.

Ще хочу сказати, що архітектура мені не дуже подобається — слабо вдалося розбити на модулі, оскільки логіка «промальовування екрану» дуже тісно переплітається з обчисленнями. Можливо, якщо зробити перепочинок, то через час я би повчився і) переглянув би її (архітектуру).

Перш за все я зробив скролинг річки. Для цього є масив, в якому вказується позиція скролінга і швидкість розширення (звуження) річки починаючи з цієї позиції. Кожен фрейм я «проходжу» з цього масиву з початкової позиції (плаваюче вікно по суті), модифікуючи поточну позицію X берега річки відповідно до поточної координаті Y і поточними даними в цьому масиві. Річка і острови (для них окремий масив) у мене симетричні (як і в оригінальній грі) — тому права сторона річки і острови відмальовує дзеркально.

Спрайт для ворогів спершу зберігав у масивах, але досить швидко у мене перестало вистачати елементів fpga — довелося перенести в ROM Cyclone IV. З останнім є невелика проблема. Справа в тому, що ROM синхронна, тому щоб виставити адреса пікселя в спрайте, мені потрібно за такт (ну або за пів-такту, якщо використовувати negedge) знати координати поточної точки відносно верхнього лівого кута об'єкта. Це, природно, здійсненно, треба просто шукати перетин поточної точки на екрані з координатами об'єктів змістивши їх координати вліво на 1 піксель при порівнянні. Оскільки такі речі робляться в циклі, ця додаткова логіка накинула б сотню елементів. Я вирішив зекономити (оскільки елементів залишалося в притик) і не морочитися. У підсумку спрайт малюється на один піксел праворуч ніж він насправді є.

Також є баг накладення ворогів один на одного (з іншими об'єктами все ок) — оскільки ROM для спрайтів на всіх ворогів одна, то щоб обчислити адресу якого спрайту використовувати в разі перетину об'єктів, потрібно знати піксель якого об'єкта в даний момент прозорий. Тут досить складна логіка, тому вирішив теж не заморочуватися.

Щоб максимально розбити код на сутності, я реалізував своєрідний конвеєр (машину станів): на першому етапі перевіряється варто змінити напрям річки, на другому варто помістити в FIFO нового ворога, на третьому здійснити переміщення ворогів і т. д.

Картинки для спрайтів люб'язно надав Гугл, а mif-файли формував скриптом на python.

Світ промальований зовсім небагато, приблизно на хвилину гри, просто втомився — проект пиляв у неробочий і для сімейного час ночами. Якщо хтось зуміє запустити на своїх залізяках — можу дописати

» Проект на Гітхабі

Висловлюю подяку ishevchuk оскільки завдяки йому я зрозумів наскільки потужна річ fpga
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.