Akcelerator grafiki 2D na FPGA i STM32F429
Więcej o bitmapach
Planuję wprowadzić dwa tryby pracy dla FPGA. W trybie transferu (passthrough) układ FPGA będzie przesyłał dane otrzymane z mikrokontrolera bezpośrednio na magistralę LCD. To pozwoli mikrokontrolerowi bezpośrednio sterować kontrolerem wyświetlacza LCD Renesas R61523 z dużą szybkością – choć nie tak szybko, jak gdyby były bezpośrednio połączone. Ten tryb pozwala inicjalizować LCD, wyświetlać ekran startowy i ekran najlepszych wyników, a także przesłać instrukcje przygotowujące wyświetlacz do pracy w drugim trybie – bitmapowym.
W trybie bitmapowym FPGA przejmuje sterowanie danymi kierowanymi do wyświetlacza zgodnie ze schematem pokazanym wyżej. Mikrokontroler może jedynie przesyłać komendy związane z bitmapami, takie jak ich wczytanie, przesunięcie, pokazanie lub ukrycie. Układ FPGA potrzebuje 127-bitowego rekordu, aby przechować pełny stan bitmapy. Z kolei szerokość magistrali BRAM musi być potęgą 2. Wynika z tego, że możemy przechować w FPGA 512 bitmap – jedna bitmapa odpowiada jednej grafice lub jednej klatce animacji. To powinno być w zupełności wystarczające. W dalszym ciągu okaże się, że ograniczają mnie przede wszystkim wymagania czasowe.
Aby wyświetlić pełną ramkę, bitmapy muszą zająć 100% powierzchni ekranu. Nie ma polecenia wypełniającego tło kolorem, zatem tło również musi stanowić jedna lub więcej bitmap, które pokrywają całą ramkę. Jeśli wymagane jest wypełnienie jednolitym kolorem, potrzebna będzie do tego bitmapa w takim kolorze. Projekt FPGA pozwala automatycznie powtarzać bitmapy wzdłuż osi X oraz Y, aby zoptymalizować użycie pamięci Flash i pamięci bitmap.
Bitmapy są ułożone w ten sposób, że pierwsza znajduje się z tyłu, a ostatnia – z przodu. Dostępna jest informacja o przezroczystości piksela (ale nie kanał alfa), dzięki czemu bitmapy mogą mieć nieregularne kształty lub dziury w środku.
Ostatnia funkcja, którą zrealizuję, to tryb wyświetlania częściowego. Pozwala on mi określić, która kolumna i/lub rząd bitmap powinny być wyświetlone jako pierwsze, a które jako ostatnie.
Rzędy i kolumny wychodzące poza widoczny obszar są ignorowane podczas zapisu do wyświetlacza.
Na powyższym obrazku ostatnia kolumna bitmapy 4 wystaje poza ekran z prawej. Natomiast pierwsza kolumna i pierwszy rząd bitmapy 5 zaczyna się poza lewym dolnym rogiem ekranu,
W praktyce ta funkcja pozwala bitmapom wchodzić i wychodzić poza powierzchnię wyświetlacza, a także uzyskać płynne przewijanie w każdym kierunku. Zamierzam użyć obu możliwości podczas testów demonstracyjnej gry.
Ograniczenia
Zasadniczym ograniczeniem liczby wyświetlanych bitmap jest czas tworzenia ramki wyświetlacza LCD. Renderowanie wszystkich bitmap do bufora ramki realizowane na FPGA musi się zakończyć w czasie jednej ramki, czyli 16,2 ms. Zobaczmy, co można zrealizować w tym czasie.
FPGA będzie przeglądać 512 bitmap sprawdzając, czy powinny być wyświetlone, czy nie. Sprawdzenie każdej bitmapy zajmuje 30 ns, co daje stały narzut 30 x 512 = 0,015 ms. W praktyce ten czas jest pomijalny.
Dla każdej widocznej bitmapy występuje stały czas konfiguracji i wykonania równy 280 ns. Jest tak nawet, gdy bitmapa jest automatycznie powtarzana wzdłuż osi X lub Y. Dla każdego piksela narzut wynosi 40 ns. Otrzymujemy zatem wzór ((40 x liczba pikseli) + 280) / 1.000.000 ms na bitmapę. To ważne wyliczenie.
W grze, w której tło ma jednolity kolor, powoduje ono stały narzut ((40 x 640 x 360)+280) / 1.000.000 = 9,21 ms. Daje to tylko 6,99 s na bitmapy reprezentujące obiekty w grze. Przekształcając ten wzór możemy wyliczyć, że zostawia nam to do dyspozycji około 174.000 pikseli lub 75% powierzchni wyświetlacza. To czynnik ograniczający każdą tworzoną grę, o czym należy pamiętać.
Schemat elektryczny
Znając elementy, które zamierzam wykorzystać, mogę stworzyć schemat łączący jest wszystkie razem.
Schemat jest dość duży i zgodnie z przewidywaniami zdominowany przez FPGA oraz mikrokontroler. Znacznie łatwiej zrozumieć go, omawiając poszczególne moduły.
Zasilacz
Wejścia trzech głównych stabilizatorów napięcia są podłączone przez wtyczkę jack do zewnętrznego zasilacza 5 V. Zasilacz 2,8 V jest fizycznie umieszczony daleko od wejścia 5 V, zatem wygodniej było zasilić go z linii 3,3 V. Tantalowe kondensatory 10 uF i 22 uF wygładzające napięcie są umieszczone bardzo blisko stabilizatora, z którym będą pracować.
Kondensatory elektrolityczne C26, C18 i C23 zapewniają zgrubne odsprzęganie niskich częstotliwości na płytce. W jednym z licznych artykułów projektowych Xilinx są zalecane kondensatory odsprzęgające co dekadę do wartości 100 uF, zatem trzymam się tej rekomendacji.
Opornik 120 Ω między linią 2,5 V a masą jest kolejnym rozwiązaniem Xilinx. W nocie aplikacyjnej Xilinx XAPP453 jest wyjaśniony sposób konfiguracji (programowania) FPGA za pomocą mikrokontrolera pracującego z poziomami 3,3 V. Jednym z ważnych kroków jest dodanie opornika bocznikującego 120 Ω między linią 2,5 V a masą, aby regulator napięcia nie obserwował ujemnego prądu na swoim wyjściu. Wadą tego rozwiązania jest powstanie stałego prądu 20,8 mA (strata 50 mW) płynącego nawet wówczas, gdy układ nie pracuje.
Warto przyjrzeć się charakterystykom temperaturowym w karcie katalogowej regulatora napięcia. Podczas wstępnych eksperymentów uruchomiłem projekt z napięciem zasilającym 12 V zamiast 5 V. Po pewnym czasie zauważyłem, że system samoistnie się resetuje. Byłem zaskoczony, dopóki nie dotknąłem płytki. Była bardzo nagrana w pobliżu zasilacza AMS1117. Ten układ wyłączał się z powodu przegrzania, aby zapobiec spaleniu. Zajrzałem do karty katalogowej, aby przekonać się, dlaczego.
We fragmencie karty katalogowej poświęconym wpływie temperatury znajduje się wzór na wydzielaną moc: PD = (VIN – VOUT) * IOUT. Przy zasilaniu 12 V i prądzie 400 mA oznacza to w najgorszym wypadku wydzielanie 3,48 W. Przekłada się to na temperaturę złącza 233°C, podczas gdy maksymalna dozwolona wynosi 125°C. Nie jest więc zaskakujące, że układ powodował problemy. Zmniejszenie napięcia wejściowego oznacza spadek wydzielanej mocy do 0,68 W, przy której maksymalna temperatura złącza wynosi 65°C. Teraz układ pracuje znacznie lepiej.
FPGA
Poniżej widać układ FPGA – kondensatory odsprzęgające nie są widoczne, aby oszczędzić miejsce. Pełna sieć odsprzęgająca jest widoczna na powiększonym pliku PDF. Odsprzęganie FPGA jest bardzo ważne i zostało zrealizowane w oparciu o notę aplikacyjną Xilinx Power Distribution System (PDS) Design.
W tym projekcie wykorzystuję 62 spośród 63 dostępnych wejść/wyjść – wszystko, czego potrzebuję, ledwo mieści się w tym układzie. Zamierzam umieścić komponenty obsługiwane sygnałami wysokiej szybkości jak najbliżej FPGA, aby nie było konieczności stosowania przelotek. Jest to projekt przeznaczony dla hobbysty – realizowany na płytce dwuwarstwowej, zatem muszę starannie poprowadzić sygnały.
Kość SRAM jest najbardziej wymagającym układem – potrzebnych jest 28 wejść/wyjść, aby obsłużyć adres, dane i sygnał kontrolny WE. Zaoszczędziłem dwa wyprowadzenia, zwierając piny CS oraz OE do masy – według karty katalogowej SRAM jest to dozwolone. Pamięć Flash, jako układ sterowany przez SPI, wymaga w sumie tylko 6. wyprowadzeń. Nie mogę niestety zewrzeć wyprowadzenia CS, ponieważ jest używane przez protokół SPI do zakończenia pewnych ciągów poleceń.
Wejścia sygnałów z mikrokontrolera to 10-bitowa szyna danych D0..9, sygnał strobujący WR i wejście reset (aktywne w stanie wysokim). Wyjścia dla mikrokontrolera to sygnał busy i wyjście debugujące używane podczas projektowania, aby zakomunikować wystąpienie określonych stanów. Debugowanie samego układu FPGA byłoby niezwykle trudne.
Wszystkie sygnały programujące: PROG_B, INIT_B, CCLK, DIN i DONE są widoczne. Będę programował FPGA za pomocą trybu nazywanego przez Xilinx „slave serial mode”, w którym mikrokontroler taktuje dane przesyłane do FPGA i monitoruje sygnały wyjściowe, aby określić, czy operacja zakończyła się sukcesem. Skompilowany plik projektowy w formacie .bit zajmuje około 55 KB, a jego załadowanie z mikrokontrolera zajmuje ułamek sekundy.
Plik projektowy .bit umieściłem wewnątrz programu mikrokontrolera podczas kompilacji. Jest on ładowany do FPGA po uruchomieniu. Konfiguracja FPGA jest przechowywana w wewnętrznej pamięci ulotnej SRAM, zatem jest tracona po wyłączeniu zasilania i musi zostać przywrócona po uruchomieniu systemu. Niektóre układy FPGA zawierają wewnętrzną pamięć konfiguracji Flash, ale ta rodzina nie daje takiej możliwości.
Sygnały przeznaczone dla wyświetlacza LCD obejmują 8-bitową szynę danych, kontrolę zatrzasku (LE) oraz linie RS i WR. Do FPGA wchodzi kluczowy sygnał TE, który pozwala zsynchronizować generację ramki LCD.
Linie VCCO[0..7] są wejściami poziomu 3,3 V, jedna przypada na każdy bank wejść/wyjść FPGA. VCCINT są wejściami 1,2 V, a VCCAUX – 2,5 V. Daje to dużą liczbę wejść zasilania i towarzyszących im kondensatorów odsprzęgających. Sytuacja mogłaby się tylko pogorszyć w przypadku większych obudów FPGA. Jest to kolejny powód, aby w projektach hobbystycznych wykorzystywać mniejsze układy.