Sterownik VGA w układzie FPGA
Wtorek, 01 Marzec 2011
Zastosowanie monitora VGA w aplikacji zrealizowanej w układzie
FPGA nie jest trudne. Przekonamy się o tym po lekturze artykułu,
w którym zaprezentowano projekt układu, opisanego w językach
VHDL i Verilog, umożliwiającego wyświetlenie jakiegoś obiektu na
ekranie monitora. Dobry wzorzec dla uczących się VHDL\'a czy
Verilog\'a.
90 ELEKTRONIKA PRAKTYCZNA 3/2011
NOTATNIK KONSTRUKTORA
Dodatkowe materiały na CD/FTP:
ftp://ep.com.pl, user: 10460, pass: 0646g3n0
? listingi do artukułu
Jako przykład implementacji w ukła-
dach programowalnych prostego sterow-
nika VGA przedstawiamy projekt układu,
którego zadaniem jest wyświetlanie na
ekranie monitora poruszającego się i odbi-
jającego od czterech krawędzi ekranu pro-
stego obiektu zwanego dalej kulką. Kulka
w rzeczywistości jest kwadratem o boku
o zadanej liczbie pikseli. Kulka podczas po-
ruszania się po ekranie dodatkowo zmienia
cyklicznie swój rozmiar, sprawiając wraże-
nie pulsowania i przy każdym odbiciu od
górnej krawędzi ekranu zmienia również
swój kolor na jeden z trzech kolorów pod-
stawowych (czerwony, niebieski, zielony).
Obraz wyświetlany jest w standardowej
rozdzielczości VGA (640×480) z częstotli-
wością odświeżania równą 60 Hz.
W projekcie wykorzystano fragmenty
kodów źródłowych w języku VHDL analo-
gicznego projektu autorstwa Douga Hod-
sona, opublikowanego na stronie www.
retromicro.com. Układ opisany w udo-
stępnionym tam projekcie umożliwia wy-
świetlanie obrazu kulki o stałej wielkości,
poruszającej się po ekranie jedynie w osi
pionowej.
Opisywany w artykule układ składa się
z dwóch modułów (komponentów): genera-
tora sygnałów synchronizacji oraz modułu
odpowiedzialnego za animację ruchu kul-
ki na ekranie. W opisie projektu w języku
VHDL występuje jeszcze trzeci nadrzędny
moduł, który definiuje jedynie strukturę
połączenia wspomnianych wcześniej kom-
ponentów.
Generator sygnałów
synchronizacji
Zadaniem tego modułu jest wytwo-
rzenie sygnałów synchronizacji pionowej
i poziomej oraz udostępnienie bieżących
zawartości liczników określających współ-
rzędne położenia wybieranego w danej
chwili punktu (plamki) na ekranie moni-
tora. Sposób działania tego modułu jest
zdeterminowany strukturą ramki w stan-
Sterownik VGA
w układzie FPGA
Zastosowanie monitora VGA w aplikacji zrealizowanej w układzie
FPGA nie jest trudne. Przekonamy się o tym po lekturze artykułu,
w którym zaprezentowano projekt układu, opisanego w językach
VHDL i Verilog, umożliwiającego wyświetlenie jakiegoś obiektu na
ekranie monitora. Dobry wzorzec dla uczących się VHDL?a czy
Verilog?a.
dardzie VGA. Ramka sygnału VGA była już
wielokrotnie opisywana na łamach Elek-
troniki Praktycznej (np. w EP 4/2007 przy
okazji projektu testera monitorów VGA),
dlatego nie będzie omawiana.
Na listingu 1 przedstawiono kod w ję-
zyku VHDL opisujący generator sygnałów
synchronizacji. Oprócz wyjść sygnałów
synchronizacji poziomej (horiz_sync_out)
i pionowej (vert_sync_out) oraz stanu licz-
nika kolumn ? punktów w linii (pixel_co-
lumn) i licznika wierszy ? linii (pixel_row),
moduł ma również wejścia sygnałów kolo-
rów podstawowych (red, green, blue) oraz
wyjścia tych sygnałów (red_out, green_out,
blue_out). Na wyjściach sygnałów kolorów
podstawowych pojawia się ten sam sygnał
co na analogicznych wejściach wtedy, gdy
liczniki kolumn i wierszy wskazują na ak-
tywny obszar ekranu (sygnały video_on_h
oraz video_on_v mają poziom wysoki).
W czasie trwania przednich i tylnych prze-
działów wyrównawczych, jak również pod-
czas trwania samych impulsów synchroni-
zacji, wyjścia kolorów podstawowych są
wyzerowane (obraz jest wygaszany).
Moduł animacji ruchu kulki
Moduł jest głównym elementem pro-
jektu realizującym wszystkie efekty zwią-
zane z ruchem kulki na ekranie monitora.
Do modułu musi być dostarczony z genera-
tora sygnałów synchronizacji impuls syn-
chronizacji pionowej oraz stanu liczników
wierszy i kolumn. Z kolei wyjścia kolorów
podstawowych red, green, blue tego modu-
łu powinny być połączone z analogiczny-
mi wejściami modułu generatora sygnałów
synchronizacji. Kod opisujący moduł ani-
macji ruchu kulki przestawiono na listin-
gu 2.
W procesie nazwanym RGB_Dsiplay,
jest wyliczana wartość sygnału Ball_on,
którego poziom wysoki, przy danych war-
tościach liczników kolumn i wierszy ozna-
cza, że aktualnie wybierany (wyświetlany
na ekranie) punkt nie jest punktem tła lecz
należy do obszaru zajmowanego przez kul-
kę. Sygnał ten jest wyliczany na podstawie
koniunkcji stanów odpowiednich relacji
(narzędzia syntezy wywnioskują z tego
opisu zespół komparatorów) uwzględnia-
jących bieżące położenie kulki (środka
kwadratu ? współrzędne Ball_X_pos oraz
Ball_Y_pos), rozmiar kulki (bok kwadratu
- sygnał Size) oraz stan liczników wierszy
i kolumn (współrzędne aktualnie wybiera-
nego punktu).
Kolejny proces (Move_Ball) aktywo-
wany podczas każdego narastającego
zbocza sygnału synchronizacji pionowej
(vert_sync_out) odpowiada za sterowanie
ruchem kulki. W kolejnych instrukcjach
warunkowych sprawdzane są współrzędne
położenia środka kulki ? oddzielnie współ-
rzędna pozioma i pionowa oraz uwzględ-
niany jest rozmiar kulki. Jeżeli kulka osią-
gnęła którąś z krawędzi ekranu nadawana
jest odpowiednia wartość zmiennym Bal-
l_X_motion oraz Ball_Y_motion. Wartości
tych sygnałów następnie służą do wylicze-
nia następnego (podczas trwania następnej
ramki obrazu) położenia kulki ? następ-
nych pionowych i poziomych współrzęd-
nych położenia środka kulki. Na przykład,
jeżeli kulka poruszając się w górę ekranu
znalazła się na jego górnej krawędzi (czy-
li współrzędna pionowa położenia kulki
Ball_Y_pos jest równa rozmiarowi kuli Size
? lewy górny róg ekranu ma współrzędne
[0,0]), wówczas sygnałowi Ball_Y_motion
nadawana jest wartość pewnej stałej, któ-
ra definiuje szybkość poruszania się kulki
po ekranie (a dokładniej szybkość zmian
położenia w osi pionowej). W przedsta-
wionym kodzie na list. 2, wartość tej sta-
łej otrzymywana jest poprzez wywołanie
funkcji conv_std_logic_vector (w języku
VHDL wielkość liter jest nieistotna), która
jedynie dokonuje konwersji liczby całko-
witej (pierwszy argument funkcji) o zada-
nej liczbie bitów (drugi argument funkcji)
do typu standardowego std_logic_vector.
Zamiast wywoływania tej funkcji można
również wpisać stałą wartość w kodzie
dziesiętnym. W przypadku, gdy kulka osią-
gnęła dolną krawędź ekranu, zmiennej
Ball_Y_motion nadawana jest wartość tej
Dodatkowe materiały
na CD/FTP
91ELEKTRONIKA PRAKTYCZNA 3/2011
Sterownik VGA w układzie FPGA
Listing 1. Kod opisujący moduł generatora sygnałów synchronizacji
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity VGA_SYNC is
port(clock_25Mhz, red, green, blue : in std_logic;
red_out, green_out, blue_out, horiz_sync_out, vert_sync_out: out std_logic;
pixel_row, pixel_column: out std_logic_vector(9 downto 0));
end VGA_SYNC;
architecture sync_gen of VGA_SYNC is
signal horiz_sync, vert_sync : std_logic;
signal video_on, video_on_v, video_on_h : std_logic;
signal h_count, v_count :std_logic_vector(9 downto 0);
begin
-- video_on przyjmuje poziom wysoki, gdy ma być wyświetlony obraz
video_on <= video_on_H and video_on_V;
pixel_row <= v_count; pixel_column <= h_count;
process
begin
wait until (clock_25Mhz?EVENT) and (clock_25Mhz=?1?);
-- h_count zlicza piksele poziomo (640 + dodatkowe dla sygnału synchronizazji)
if h_count = 799 then h_count <= ?0000000000?;
else h_count <= h_count + 1; end if;
-- v_count zlicza wiersze pikseli (480 + dodatkowe dla sygnału synchronizacji)
if (v_count >= 524) and (h_count >= 699) then v_count <= ?0000000000?;
elsif h_count = 699 then v_count <= v_count + 1; end if;
-- Synchronizacja zmian wybranych sygnałów z sygnałem zegarowym
red_out <= red and video_on;
green_out <= green and video_on;
blue_out <= blue and video_on;
horiz_sync_out <= horiz_sync;
vert_sync_out <= vert_sync;
end process;
process (h_count)
begin
-- Generowanie sygnału synchronizacji poziomej z użyciem h_count
-- horiz_sync ------------------------------------__________--------
-- h_count 0 640 659 755 799
if (h_count <= 755) and (h_count >= 659) then horiz_sync <= ?0?;
else horiz_sync <= ?1?; end if;
-- Generowanie sygnału okreśaljącego obszar aktywny ekranu (H)
if (h_count <= 639) then video_on_h <= ?1?;
else video_on_h <= ?0?; end if;
end process;
process (v_count)
begin
-- Generowanie sygnału synchronizacji pionowej z użyciem v_count
-- vert_sync --------------------------------------_____------------
-- v_count 0 480 493-494 524
if (v_count <= 494) and (v_count >= 493) then vert_sync <= ?0?;
else vert_sync <= ?1?; end if;
-- Generowanie sygnału okreśaljącego obszar aktywny ekranu (V)
if (v_count <= 479) then video_on_v <= ?1?;
else video_on_v <= ?0?; end if;
end process;
end sync_gen;
samej stałej co poprzednio lecz z zanego-
wanym każdym z jej bitów. Nowa składowa
pionowa współrzędnej położenia kulki ob-
liczana jest poprzez zsumowanie jej bieżą-
cej wartości z wartością Ball_Y_motion. Po-
nieważ zgodnie z arytmetyką dwójkową dla
n-bitowych liczb A i B, A ? B = A + B? +1
(?prim? oznacza negację), gdy kulka osią-
gnęła dolną krawędź ekranu (Ball_Y_pos
+ Size = 479), to od jej pionowej współ-
rzędnej jest odejmowana stała wartość (+1
można tutaj zaniedbać) i kulka porusza się
w górę ekranu. Analogicznie oblicza się
położenie współrzędnej poziomej kulki.
Dodatkowo, wraz z instrukcjami wa-
runkowymi związanymi ze sprawdzaniem
pionowego położenia kulki, zaimplemento-
wano rejestr przesuwający, który przesuwa
o jeden bit w lewo trzybitowy sygnał RGB
podczas każdego odbicia kulki od górnej
krawędzi ekranu. Sygnał ten odpowiada
za kolor wyświetlanej kulki (czerwony,
zielony i niebieski). Jest to uwarunkowane
zastosowaniem dla sygnału RGB rejestru
przesuwającego pracującego w trybie licz-
nika pierścieniowego (?krążąca jedynka?).
Takie użycie rejestru przesuwającego wy-
maga, aby po włączeniu zasilania (zała-
dowaniu pliku konfigurującego do układu
FPGA) w rejestrze została ustawiona jedyn-
ka na wybranej pozycji bitu. Domyślnie po
załadowaniu pliku konfigurującego wszyst-
kie rejestry (przerzutniki) są zerowane.
W przypadku wykorzystania środowiska
projektowego Xilinx ISE i narzędzia synte-
zy XST, w celu nadania wartości początko-
wej dla wybranych przerzutników, można
posłużyć się atrybutem INIT ? tak jak po-
kazano to na list. 2 (tuż przed instrukcją
begin opisu architektury).
Ostatnia sekcja kodu w procesie Move_
Ball odpowiada za modulację rozmiaru wy-
świetlanego obiektu. Rozmiar kulki oscylu-
je pomiędzy dwiema wartościami granicz-
nymi (2 i 24 piksele ? stałe, argumenty
funkcji conv_std_logic_vector) i zmienia się
o jeden punkt wraz z każdym narastającym
zboczem impulsu synchronizacji piono-
wej.
Implementacja
Układ projektu można zrealizować
wykorzystując np. zestaw ewaluacyjny
ZL9PLD wraz z modułem dipPLD ZL10PLD
z układem XC3S200. Ponieważ w zestawie
dostępny jest generator sygnału zegarowe-
go o częstotliwości 3,6864 MHz, a genera-
tor impulsów synchronizacji wymaga do-
starczenia sygnału zegarowego o częstotli-
wości ok. 25 MHz (dokładnie 25,125 MHz
? ale częstotliwość ta nie jest krytyczna,
niewielkie odchyłki od tej częstotliwości
mogą spowodować jedynie pewne zmia-
ny rozmiarów obrazu na ekranie moni-
tora i ułamkowe zmiany częstotliwości
odświeżania), dlatego istnieje potrzeba od-
powiedniego powielenia częstotliwości ge-
neratora z modułu dipPLD, albo wymiany
tego generatora. W pierwszym przypadku
z pomocą przychodzi blok syntezera czę-
stotliwości (DCM) wbudowany w układ
XC3S200. Syntezer można zaprogramo-
wać tak, aby jego częstotliwość wyjściowa
była iloczynem częstotliwości wejściowej
i pewnej liczby wymiernej, której wielkość
określana jest poprzez nadanie wartości
odpowiednim atrybutom. Taki sposób za-
stosowano w omawianym projekcie. Plik
o nazwie chipIO.vhd, którego fragment po-
kazano na list. 3 zawiera również, oprócz
definicji struktury połączenia modułu ge-
neratora impulsów synchronizacji i modu-
łu animacji ruchu kulki, opis implemen-
tacji syntezera DCM wraz definicją odpo-
wiednich atrybutów. Dzięki temu uzysku-
je się częstotliwość sygnału zegarowego
bardzo bliską właściwej. Dokładnie jest to
24,88 MHz ? ze względu na ograniczenia
zakresu odpowiednich współczynników,
przy danej częstotliwości wejściowej, nie
jest możliwe uzyskanie częstotliwości wyj-
ściowej bliższej nominalnej 25,125 MHz.
Jak to zrobić w Verilogu
Dla tych, którzy preferują język opisu
sprzętu Verilog (do grona tych osób zalicza
się również autor niniejszego artykułu),
podajemy także wersję projektu opisaną
w tym właśnie języku.
Verilog jest językiem opisu sprzętu, któ-
ry powstał nieco później niż VHDL. Począt-
92 ELEKTRONIKA PRAKTYCZNA 3/2011
NOTATNIK KONSTRUKTORA
kowo służył jedynie do symulacji układów
cyfrowych i dopiero później został zaadap-
towany do celów syntezy logicznej. Skład-
nia języka Verilog oparta jest na szeroko
rozpowszechnionym języku C, podczas gdy
składnia VHDL bazuje na obecnie rzadko
używanym języku ADA. Język VHDL ma
zaawansowane konstrukcje i abstrakcyjne
typy danych, które nie występują w Verilo-
gu. Dzięki czemu VHDL znacznie bardziej
nadaje się do modelowania behawioral-
nego. Jednak tylko stosunkowo niewielki
podzbiór instrukcji VHDL akceptowany jest
przez narzędzia syntezy. Język Verilog z ko-
lei uważany jest za znacznie bliższy sprzę-
towi niż VHDL. Przyglądając się kodom
w języku VHDL i Verilog opisującym ten
sam projekt, można odnieść wrażenie, że
zazwyczaj kod w języku Verilog jest krótszy,
bardziej zwarty i przejrzystszy w porówna-
niu do kodu w języku VHDL.
Na listingach 4...6 (do pobrania z serwe-
ra FTP) przedstawiono kolejno opisy w ję-
zyku Verilog modułu generatora impulsów
synchronizacji, modułu animacji ruchu kul-
ki oraz modułu nadrzędnego, definiującego
połączenia dwóch pierwszych wymienio-
nych modułów.
Podstawową jednostką projektową w ję-
zyku Verilog jest moduł (module), który za-
wiera zarówno opis interfejsu jak również
opis wnętrza modułu (jego funkcji logicz-
nych). Nie ma tu podziału tak jak w języku
VHDL, na jednostkę projektową ? interfejs
(entity) oraz architekturę (architecture).
W Verilogu, podobnie jak w języku C, rozróż-
nine są duże i małe litery alfabetu.
W Verilogu występują dwa podstawowe
typy danych: wire (i pochodne typu wand,
wor, tri) oraz reg. Typ danych wire nie prze-
chowuje swojej wartości (tak jak zmienna)
lecz reprezentuje fizyczne połączenia w mo-
delowanym układzie i zwykle stosowany jest
w kontekście opisu strukturalnego. Danej
typu wire można przypisać wynik tzw. przy-
pisania ciągłego (continuous assignment ? in-
strukcja assign) lub sygnał wyjściowy innego
modułu lub bramki logicznej. Typ reg repre-
zentuje zmienne w języku Verilog.
W opisie układów kombinacyjnych
w przedstawionym projekcie zastosowano
dwie zasadnicze techniki kodowania: wy-
korzystanie przypisania do sygnału (signal
assignment) w VHDL (np.: pixel_row <=
v_coint;) i odpowiadającemu mu przypisania
ciągłe (continuous assignment) w Verilogu
(np.: assign pixel_row = v_count;) oraz wyko-
rzystanie sekcji proceduralnej always w Veri-
logu i instrukcji process w VHDL, wraz z od-
powiednio sformułowanymi listami wrażli-
wości (sensitivity list). Lista wrażliwości blo-
ku always (Verilog) i process (VHDL) musi
zawierać nazwy wszystkich sygnałów, które
są wejściami opisywanego układu kombina-
cyjnego (wyjście układu kombinacyjnego jest
Listing 2. Kod opisujący moduł animacji ruchu kulki
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity ball is
port(red,green,blue: out std_logic;
vert_sync_out: in std_logic;
pixel_row, pixel_column: in std_logic_vector(9 downto 0));
end ball;
architecture behavior of ball is
signal dir,shift_en, Ball_on : std_logic;
signal Ball_Y_motion : std_logic_vector(9 downto 0);
signal Ball_X_motion, Size : std_logic_vector(9 downto 0);
signal Ball_Y_pos, Ball_X_pos : std_logic_vector(9 downto 0);
signal RGB : std_logic_vector(2 downto 0);
attribute INIT : string;
attribute INIT of RGB : signal is ?001?;
begin
Red <= RGB(0) when Ball_on = ?1? else ?1?;
Green <= RGB(1) when Ball_on = ?1? else ?1?;
Blue <= RGB(2) when Ball_on = ?1? else ?1?;
-- Ball_on przyjmuje stan wysoki, gdy ma być wyświetlona ?kulka?
RGB_Display:
process (Ball_X_pos, Ball_Y_pos, pixel_column, pixel_row, Size)
begin
if (?0? & Ball_X_pos <= pixel_column + Size) and
(Ball_X_pos + Size >= ?0? & pixel_column) and
(?0? & Ball_Y_pos <= pixel_row + Size) and
(Ball_Y_pos + Size >= ?0? & pixel_row ) then Ball_on <= ?1?;
else Ball_on <= ?0?; end if;
end process RGB_Display;
Move_Ball:
process
begin
-- Realizuje przesunięcie kulki raz na każdy przedział sychr. V
wait until vert_sync_out?event and vert_sync_out = ?1?;
-- odbicie od góry i dołu ekranu
if (Ball_Y_pos + Size) >= CONV_STD_LOGIC_VECTOR(479,10) then
Ball_Y_motion <= not CONV_STD_LOGIC_VECTOR(2,10); shift_en <= ?0?;
elsif Ball_Y_pos <= Size then
Ball_Y_motion <= CONV_STD_LOGIC_VECTOR(2,10);
if shift_en = ?0? then -- realizacja rejestru przesuwnego
RGB <= RGB(1 downto 0) & RGB(2); shift_en <= ?1?; end if;
end if;
-- wyliczenie następnego pionowego położenia ?kulki?
Ball_Y_pos <= Ball_Y_pos + Ball_Y_motion;
-- odbicie od lewej i prawej krawędzi ekranu
if (Ball_X_pos + Size) >= CONV_STD_LOGIC_VECTOR(639,10) then
Ball_X_motion <= not CONV_STD_LOGIC_VECTOR(2,10);
elsif Ball_X_pos <= Size then
Ball_X_motion <= CONV_STD_LOGIC_VECTOR(2,10);
end if;
-- wyliczenie następnego poziomego położenia ?kulki?
Ball_X_pos <= Ball_X_pos + Ball_X_motion;
-- zmiana rozmiaru ?kulki?
if Size >= CONV_STD_LOGIC_VECTOR(24,10) then dir <= ?0?; end if;
if Size <= CONV_STD_LOGIC_VECTOR(2,10) then dir <= ?1?; end if;
if dir = ?1? then Size <= Size + 1;
else Size <= Size - 1; end if;
end process Move_Ball;
end behavior;
funkcją bieżącego stanu wejść tego układu).
Dla symulatora kod zawarty w bloku always
oraz instrukcji process wykonywany jest
wówczas, gdy nastąpi zmiana wartości sy-
gnału znajdującego się na liście wrażliwości.
Drugim istotnym warunkiem opisu układu
kombinacyjnego jest to, aby tak organizować
przypisania wartości do sygnału reprezentu-
jącego wyjście układu kombinacyjnego, aby
w każdej ścieżce procesu (bloku always) war-
tość tego wyjścia była określona (w układach
kombinacyjnych nic nie może być pamięta-
ne z poprzedniego stanu). Niespełnienie tego
warunku spowoduje, że narzędzia syntezy
wywnioskują z takiego opisu, dla danego
wyjścia, przerzutnik typu zatrzask.
Przykładem drugiego z wymienionych
sposobów opisu układu kombinacyjnego
w omawianym projekcie jest fragment kodu
w module animacji ruchu kulki, opisujący
sygnał Ball_on (proces RGB_Display w opi-
sie VHDL ? list. 2 i analogiczny blok always
w opisie Verilog ? list. 5). Należy zwrócić
uwagę na znaczenie poszczególnych opera-
torów w języku Verilog. Postać operatorów
jak i ich znaczenie są tutaj niemal identyczne
jak w języku C. W języku VHDL operator ?&?
jest operatorem sklejania (konkatenacji ? ten
sam operator w Verilogu ma postać ?{...}?).
W Verilogu ?&?, podobnie jak w języku C,
oznacza operator iloczynu bitowego. Użycie
operatora sklejania w kontekście takim jak
w procesie RGB_Display oznacza ?poszerze-
nie? sygnału (wektora) Ball_X_pos do 11 bi-
tów (?doklejany? bit MSB jest wyzerowany).
Taki zabieg nie jest konieczny, jeżeli zapew-
93ELEKTRONIKA PRAKTYCZNA 3/2011
Sterownik VGA w układzie FPGA
zegarowy (ogólnie: niezależnie od języka
HDL, dla opisu układów sekwencyjnych
lista wrażliwości może zawierać jedynie
sygnał zegarowy, zerujący lub ustawiający)
i pierwszą instrukcją procesu w postaci: if
(clock_25Mhz?event) and (clock_25Mhz =
?1?) then. W języku Verilog również można
modelować układy sekwencyjne wykorzy-
stując blok always z pustą listą wrażliwości.
Wówczas wewnątrz bloku always stosuje się
instrukcję (jedną lub więcej ? kod pomiędzy
tymi instrukcjami opisuje pojedynczy stan
automatu) w postaci: @(posedge clk), której
skutkiem jest zwieszenie wykonywania in-
strukcji bloku do momentu wystąpienia na-
rastającego zbocza sygnału clk (podobnie jak
wait until (clk?event) and (clk = ?1?) w VHDL).
Taki sposób opisu w Verilogu określa się jako
projektowanie z dokładnością cyklu (cycle
accurate design). Jednak ten sposób nie jest
wspierany przez niektóre narzędzia syntezy,
w tym również przez XST Xilinxa. Dlatego
też w projekcie zastosowano klasyczny spo-
sób opisu układu sekwencyjnego z blokiem
always w postaci: always @(posedge cloc-
k_25Mhz) begin ... end. W takim przypadku
instrukcje bloku wykonywane są za każdym
razem, gdy wystąpi narastające zbocze sy-
gnału clock_25Mhz.
W języku Verilog warto również zwrócić
uwagę na dwa rodzaje przypisań procedural-
nych: blokujące (operator ?=?) oraz nieblo-
kujące (operator ?<=?). Analogiczne typy
przypisań nie występują w VHDL, a począt-
kującym użytkownikom Veriloga sprawiają
pewne kłopoty. W dużym skrócie przypi-
sanie blokujące (?=?) możemy traktować
jako przypisanie natychmiastowe. Wartość
zmiennej, której przypisano wartość z uży-
ciem operatora ?=?, jest znana już w na-
stępnej linijce kodu (następnej instrukcji).
Przypisanie nieblokujące z operatorem ?<=?
używane jest w kontekście zdarzeń związa-
nych ze zboczem narastającym lub opadają-
cym wybranego sygnału (zazwyczaj zegara
lub sygnału zerującego) i odnosi skutek do-
piero wówczas gdy to zdarzenie wystąpi.
Podsumowanie
Tworzenie aplikacji z układami FPGA,
w których do wizualizacji danych wyko-
rzystuje się monitor VGA, jest stosunkowo
nieskomplikowane. Omawiany projekt nie-
wielkim nakładem pracy można uatrakcyj-
nić budując prostą grę zręcznościową na
wzór znanej gry komputerowej ?Arkanoid?.
Na przykładzie projektu pokazano
również możliwość użycia dwóch języków
opisu sprzętu: VHDL i Verilog. Przejście od
opisu układu w jednym języku do opisu
w drugim ? w typowych przypadkach ? nie
jest skomplikowane, ale wymaga jednak
pewnej wiedzy.
Zbigniew Hajduk
zhajduk@prz-rzeszow.pl
sujący układ sekwencyjny (VHDL ? list. 1,
Verilog ? list. 4). W języku VHDL mamy in-
strukcję process z pustą listą wrażliwości,
ale na początku znajduje się instrukcja wait.
Dalsze instrukcje procesu są wykonywane
tylko wówczas, gdy warunek po instrukcji
wait będzie prawdziwy ? w tym przypad-
ku, gdy wystąpi narastające zbocze sygnału
zegara (clock_25MHz). Alternatywny opis
w VHDL, dający w wyniku syntezy taki sam
układ, składałby się z instrukcji process z li-
stą wrażliwości zawierającą tylko sygnał
Listing 3. Kod opisujący moduł nadrzędny projektu
entity chipIO is
port(
pin_sysclk : in std_logic;
pin_vga_red : out std_logic;
pin_vga_green : out std_logic;
pin_vga_blue : out std_logic;
pin_vga_hsync_n : out std_logic;
pin_vga_vsync_n : out std_logic
);
end chipIO;
architecture arch of chipIO is
signal sysClk : std_logic;
component VGA_SYNC
port( clock_25Mhz : in std_logic;
red, green, blue : in std_logic;
red_out, green_out, blue_out : out std_logic;
horiz_sync_out, vert_sync_out : out std_logic;
pixel_row, pixel_column : out std_logic_vector(9 downto 0));
end component;
signal vga_pixel_row : std_logic_vector(9 downto 0);
signal vga_pixel_column : std_logic_vector(9 downto 0);
signal vga_vert_sync_out : std_logic;
component ball
port( signal red,green,blue : OUT std_logic;
signal vert_sync_out : in std_logic;
signal pixel_row, pixel_column : in std_logic_vector(9 downto 0));
end component;
component DCM
port(CLKFX: out std_logic;
CLKIN: in std_logic);
end component;
attribute CLKFX_MULTIPLY: integer;
attribute CLKFX_DIVIDE: integer;
attribute CLK_FEEDBACK: string;
attribute CLKIN_PERIOD: integer;
attribute CLK_FEEDBACK of VGA_clock: label is ?NONE?;
attribute CLKFX_MULTIPLY of VGA_clock: label is 27;
attribute CLKFX_DIVIDE of VGA_clock: label is 4;
attribute CLKIN_PERIOD of VGA_clock: label is 275;
signal ball_red : std_logic;
signal ball_green : std_logic;
signal ball_blue : std_logic;
begin
vgadriver:
VGA_SYNC
port map(
clock_25Mhz => sysClk,
red => ball_red,
green => ball_green,
blue => ball_blue,
red_out => pin_vga_red,
green_out => pin_vga_green,
blue_out => pin_vga_blue,
horiz_sync_out => pin_vga_hsync_n,
vert_sync_out => vga_vert_sync_out,
pixel_row => vga_pixel_row,
pixel_column => vga_pixel_column
);
pin_vga_vsync_n <= vga_vert_sync_out;
balldriver:
ball
port map(red => ball_red,
green => ball_green,
blue => ball_blue,
vert_sync_out => vga_vert_sync_out,
pixel_row => vga_pixel_row,
pixel_column => vga_pixel_column);
-- pin_sysclk=3.6864MHz, sysClk=24.88MHz
VGA_clock : DCM
port map(CLKIN => pin_sysclk,CLKFX => sysClk);
end arch;
nimy, że prawa strona operatora relacji nie
przekroczy rozmiaru 10 bitów. W omawia-
nym projekcie tak jest (maksymalna wartość
pixel_column + Size to 824 ? liczba, którą
można bez problemu zapisać na 10 bitach),
dlatego też w kodzie w języku Verilog, w tym
przypadku zrezygnowano z użycia operatora
sklejania.
Instrukcja process oraz blok always może
służyć również do opisu układów sekwen-
cyjnych. Rozważmy fragment kodu modułu
generatora impulsów synchronizacji opi-
Zobacz więcej w kategorii Notatnik konstruktora