Laboratorium Podstaw Techniki Cyfrowej
Ćwiczenie 5: Wprowadzenie do języków opisu sprzętu
1. Języki opisu sprzętu
Języki opisu sprzętu(HDL – Hardware Description Language) to języki służące do opisu ukła-
dów cyfrowych. Ich historia sięga lat pięćdziesiątych, jednakże pierwsze praktyczne implemen-
tacje powstały w latach siedemdziesiątych.
2. Języki HDL – wstęp do języka VERILOG
2.1. Stałe liczbowe
Stałe liczbowe w języku VERILOG zapisywane są w następującej postaci:
[ [<rozmiar>]
[<podstawa>] ] <wartość>
Zarówno rozmiar jak i podstawa są opcjonalne, niemniej jednak rozmiar nie może występować
bez podstawy. Rozmiar jest liczbą dziesiętną, która określa na ilu bitach ma zostać zapisana licz-
ba, natomiast podstawa jest stałą określającą podstawę liczby. Może ona przyjmować następują-
ce wartości:
• ’d – liczba dziesiętna (domyślnie),
• ’h – liczba szesnastkowa,
• ’o – liczba ósemkowa,
• ’b – liczba binarna.
2.2. Komentarze
W VERILOGu występują podobnie jak w języku C dwa sposoby komentowania kodu.
Pierwszym z nich są dwa ukośniki (
//
) służące do komentowania pojedynczych linii kodu. Dru-
gim dokładnie tak samo jak w języku C są:
• ciąg znaków
/*
- określa początek komentarza,
• ciąg znaków
*/
- określa koniec komentarza.
Wszystko co jest pomiędzy tymi dwoma ciągami znaków nie jest brane pod uwagę podczas syn-
tezy układu cyfrowego. Przykład wykorzystania pokazany został na kolejnym listingu.
// To jest komentarz w pojedynczej linii
/* Pierwsza linijka komentarza wielolinijkowego
Druga linijka komentarza wielolinijkowego
Trzecia linijka komentarza wielolinijkowego
*/
2.3. Obiekty
VERILOG w odróżnieniu od klasycznych języków programowania proceduralnego takich jak
Pascal czy C posiada zamiast zmiennych obiekty. Najważniejszymi z nich są:
• reg – (rejestr) obiekt pamiętający swoją wartość do momentu wpisania kolejnej wartości,
• net – (sieć) obiekt stanowiący połączenie pomiędzy dwoma blokami, nie posiada pamię-
ci, musi być ciągle wysterowywany,
• integer – liczba całkowita której rozmiar jest zwykle determinowany przez rozmiar słowa
na maszynie na której kod jest symulowany,
• real – liczba rzeczywista, zwykle podwójnej precyzji (64 bit),
• time – obiekt przeznaczony do przechowywania czasu symulacji.
Istnieje wiele rodzajów obiektów typu net. Niektóre z nich to:
• wire – zwykła sieć do której można dołączyć tylko jeden sterownik,
• wor – wyjścia sterowników są połączone do sieci poprzez wielowejściową bramkę OR,
• wand – wyjścia sterowników są połączone do sieci poprzez wielowejściową bramkę
AND (modeluje otwarty kolektor),
• wand – wyjścia sterowników połączonych
• tri – sieć trzystanowa, można do niej dołączyć więcej niż jeden sterownik,
• tri0 – sieć trzystanowa, podciągana do zera logicznego gdy źródła sterujące są w stanie
wysokiej impedancji,
• tri1 – sieć trzystanowa, podciągana do jedynki logicznej gdy źródła sterujące są w stanie
wysokiej impedancji,
• trireg – sieć trzystanowa, zachowuje poprzednią wartość gdy źródła sterujące są w stanie
wysokiej impedancji.
2.4. Deklaracja obiektów i wektorów obiektów
Obiekty w języku VERILOG można deklarować w sposób skalarny i wektorowy. Oba przypadki
nie różnią się znacznie od deklarowania zmiennych w języku C.
2.4.1. Deklaracja obiektów skalarnych
reg
<obiekt1, obiekt2, obiekt3,..., obiektN>
;
wire
<obiekt1, obiekt2, obiekt3,..., obiektN>
;
2.4.2. Deklaracja obiektów wektorowych
reg [
val1
:
val2
]
<obiekt1, obiekt2, obiekt3,..., obiektN>
;
wire [
val1
:
val2
]
<obiekt1, obiekt2, obiekt3,..., obiektN>
;
wire [
7
:
0
]
<obiekt1, obiekt2, obiekt3,..., obiektN>
;
wire [
0
:
7
]
<obiekt1, obiekt2, obiekt3,..., obiektN>
;
wire
[
7
:
0
]
data;
wire
[
15
:
0
]
addr;
Najważniejszą rzeczą na jaką należy zwrócić uwagę jest fakt, że val1 może być większe
lub mniejsze od val2. Jest to najzwyczajniej modelowanie sposobu zapisu. Należy pamiętać
aby w całym kodzie stosować tą samą notację zapisu. Niemniej jednak notacją, która się
przyjęła jest zapis dla którego val1 > val2.
2.5. Operatory stosowane w języku VERILOG
W niniejszej tablicy zestawiono operatory wykorzystywane w języku VERILOG w kolejności ro-
snącego priorytetu operacji. Ponieważ w tym miejscu jest jeszcze zbyt wcześnie by podawać
przykłady działania tych operatorów, zostaną one podane w dalszej części tej instrukcji.
Symbol operatora
Opis
?:
operator warunkowy
||
suma logiczna
&&
iloczyn logiczny
|
bitowe or
~|
bitowe nor
^
bitowe xor
^~
bitowe xnor
~^
bitowe xnor
&
bitowe and
~&
bitowe nand
==
relacja równości
!=
relacja nierówności
>
relacja - większy
<
relacja - mniejszy
>=
relacja - większy bądź równy
<=
relacja - mniejszy bądź równy
>>
przesunięcie bitowe w prawo
<<
przesunięcie bitowe w lewo
+
dodawanie
-
odejmowanie
*
mnożenie
/
dzielenie
%
modulo
2.6. Instrukcje sterujące języka VERILOG
Ponieważ VERILOG jest językiem opisu sprzętu należy się bardzo ostrożnie posługiwać instruk-
cjami sterującymi. Przy implementacji modułów w sprzęcie praktycznie wykorzystywane są tyl-
ko dwa typy instrukcji sterujących. Do pierwszego z nich należą instrukcje wykonywania wa-
runkowego if-else. Sposób wykorzystania instrukcji ilustruje następujący przykład:
if
(
enable
==
1
’b
1
)
begin
y
=
x
;
ny
=
!
x
;
end else begin
y
=
0
;
ny
=
1
;
end
Drugim typem instrukcji sterującej jest instrukcja case. Działa ona podobnie jak instrukcja swi-
tch w języku C. Ilustruje to kolejny przykład:
case
(
input
)
0
:
output
=
7
;
1
:
output
=
2
;
2
:
output
=
8
;
3
:
output
=
9
;
default
:
output
=
0
;
endcase
Jeśli obiekt input przyjmuje wartości 0, 1, 2, 3, to obiekt output przyjmuje odpowiednio wartości
7, 2, 8, 9. We wszystkich innych przypadkach obiekt output przyjmuje wartość 0.
Kolejne trzy instrukcje sterujące nie posiadają właściwie żadnego zastosowania przy implemen-
tacjach sprzętowych, natomiast wykorzystuje się je przy implementacji środowisk testowych.
Wszystkie są instrukcjami pętli. Pierwszą z nich jest instrukcja while. Jest ona analogiczna do in-
strukcji while w języku C. Dopóki oiekt ma wartość logiczną jeden (wartość większa od zera)
zawartość pętli jest wykonywana. Ilustruje to następujący przykład:
counter
=
10
;
#
10
;
while(
counter
)
begin
counter
=
counter
-
1
;
y
=
~
y;
$display( “
counter = %d, y = %b
”
,
counter, y
);
#
10
;
end
Wynik działania wygląda następująco:
# counter = 9, y = 1
# counter = 8, y = 0
# counter = 7, y = 1
# counter = 6, y = 0
# counter = 5, y = 1
# counter = 4, y = 0
# counter = 3, y = 1
# counter = 2, y = 0
# counter = 1, y = 1
# counter = 0, y = 0
Instrukcja
#
10
;
oznacza opóźnienie o 10 jednostek czasowych. Jest to typowe zastosowanie przy
symulacji układów cyfrowych.
Kolejna instrukcja sterująca to pętla for. Jest ona praktycznie identyczna jak w języku C,
poza faktem, że VERILOG nie posiada operatorów
++
i
--
. Wykorzystanie tej instrukcji zostało
pokazane w kolejnym przykładzie:
for(
i
=
0
;
i
<
10
;
i
=
i
+
1
)
begin
$display( “
i = %b
”
,
i
);
#
10
;
end
Wynik wygląda następująco:
# i = 0000
# i = 0001
# i = 0010
# i = 0011
# i = 0100
# i = 0101
# i = 0110
# i = 0111
# i = 1000
# i = 1001
Ostatnią z instrukcji sterujących jest pętla repeat. Ciało tej pętli jest wykonywane tyle razy ile
podano w argumencie. Ilustruje to następujący snippet kodu:
i
=
0
;
#
10
;
repeat (
5
)
begin
$display( “
i = %d
”
,
i
);
i
=
i
+
1
;
#
10
;
end
Wynik symulacji tego kodu wygląda następująco:
# i = 0
# i = 1
# i = 2
# i = 3
# i = 4
WAśNE !!!!
Jeśli przy opisie za pomocą instrukcji if-else lub case nie zostaną zdefiniowane wszystkie kom-
binacje (przy if brakuje else, a przy case brakuje default) narzędzie syntezujące stworzy zatrzask.
2.7. Bloki proceduralne i przypisania
W języku VERILOG wszystkie instrukcje są grupowane w blokach:
• assign,
• initial,
• always,
przy czym bloki initial i always są nazywane blokami proceduralnymi.
Blok assign służy do modelowania logiki kombinatorycznej używającej obiektów typu wi-
re jako wyjście czyli takich, które potrzebują ciągłego wysterowania. Blok ten wykonywany jest
asynchronicznie, równolegle z wszystkimi innymi blokami, dlatego też nazywany jest czasami
przypisanie ciągłe. Jego wykorzystanie ilustruje kolejny przykład:
wire
out
;
assign
out
=
(enable)
?
data : 1
'b
z
;
Jest to implementacja bufora trzystanowego. Jeśli obiekt enable ma wartość logiczną 1, to war-
tość obiektu data przekazywana jest do obiektu out. W przeciwnym przypadku obiekt out usta-
wiany jest w stan wysokiej impedancji.
Kolejnym z bloków jest blok initial który jest wykorzystywany tylko podczas symulacji,
w tzw. środowiskach testowych (nazywanych w języku angielskim testbench). Zawartość tego
bloku wykonywana jest tylko raz, na początku symulacji. Przykładowa implementacja takiego
bloku przedstawiona została w kolejnym listingu:
reg
x1, x2, sw
;
wire
y
;
initial begin
x1
=
0
;
x2
=
1
;
sw
=
0
;
#
10
;
$display(
"
x1: %b, x2: %b, sw: %b, y: %b
"
, x1, x2, sw, y
);
#
10
;
sw
=
1
;
$display(
"
x1: %b, x2: %b, sw: %b, y: %b
"
, x1, x2, sw, y
);
#
10
;
sw
=
0
;
$display(
"
x1: %b, x2: %b, sw: %b, y: %b
"
, x1, x2, sw, y
);
end
Ostatnim opisywanym tutaj blokiem jest always. Jak sama nazwa mówi, ciało tego bloku wyko-
nywane jest zawsze. No ale cóż to za blok wykonywany zawsze ?? Otóż blok ten w odróżnieniu
od bloku initial posiada tzw. listę wrażliwości. A jeśli tej nie posiada, to musi bynajmniej posia-
dać instrukcję opóźnienia. Lista wrażliwości, to lista zmiennych wyzwalająca działanie bloku.
Najlepiej zilustrować to na przykładzie:
always
@(
sel
or
in1
or
in2
or
in3
or
in4
) begin
out
=
0
;
case (
sel
)
0
:
out
=
in1
;
1
:
out
=
in2
;
2
:
out
=
in3
;
3
:
out
=
in4
;
endcase
end
Pokazany kod jest implementacją czterokanałowego multipleksera. Jak widać w linijce w której
znajduje się always umieszczona została lista pięciu zmiennych separowanych operatorem zda-
rzeniowym or. Przy zmianie każdej z tych zmiennych całe ciało always zostanie wykonane.
Przypisanie
out
=
0
;
przed instrukcją case powoduje, że program syntezujący nie tworzy prze-
rzutnika dla sygnału out.
Przykład ten pokazuje tylko jeden typ listy, a mianowicie listę wrażliwą na zmiany po-
ziomu obiektów (sygnałów). Istnieje drugi typ listy który jest wrażliwy na zbocza, czyli wyzwo-
lenie wykonania ciała always nastąpi np. przy zboczu narastającym danego sygnału. Jej działanie
ilustruje kolejny listing kodu:
always
@(
posedge
clk
or
negedge
clk
)
begin
case (
sel
)
0
:
out
=
in1
;
1
:
out
=
in2
;
2
:
out
=
in3
;
3
:
out
=
in4
;
endcase
end
Przedstawiony przykładowy kod to implementacja synchronicznego multipleksera czterokana-
łowego, który działa zarówno przy narastającym jak i opadającym zboczu zegara. Zmiany sygna-
łów sel, in1, in2, in3, in4 mają wpływ na wyjście out tylko i wyłącznie w chwilach, w których
zegar clk zmienia swój poziom (z zero na jeden i jeden na zero).
2.8. Przypisania proceduralne blokujące, nieblokujące i ciągłe
W VERIGLOGu istnieją trzy rodzaje przypisań proceduralnych (czyli takich, które są
wykorzystywane w blokach proceduralnych), blokujące, nieblokujące i ciągłe nazywane czasami
również kwaziciągłymi. Wszystkie te przypisania tyczą się obiektów typu reg, time, integer
i real. Przypisanie blokujące
=
cechuje się tym, że wykonywanie instrukcji w danym bloku pro-
ceduralnym jest opóźnione do momentu wykonania przypisania. Przypisanie nieblokujące
<=
na-
tomiast jak sama nazwa mówi nie blokuje wykonywania operacji w bloku. Można to bardzo ła-
two zilustrować na następujących przykładach:
Gdyby założyć wartości początkowe sygnałów jako: a=0 i b=1, to w przypadku pierwszego
przykładu otrzymamy wynik: a=1, b=1, natomiast w przypadku drugiego przykładu nastąpi za-
miana wartości zmiennych, tzn. a=1, b=0.
Kolejnym typem przypisania proceduralnego jest tzw. przypisanie ciągłe. Różni się ono
od bloku assign przedstawianego wcześniej tym, że tyczy się obiektów typu reg, time, integer i
real, a dla przypomnienia zwykłe przypisanie ciągłe tyczyło się obiektów typu net. Jest ono wy-
korzystywane przy modelowaniu części asynchronicznej elementów synchronicznych, tak jak
np. w przerzutniku typu D z wejściami zerującym i ustawiającym. Przykład implementacji poka-
zany został w kolejnymi listingu:
always @ ( posedge
clock
) begin
q
<=
d
;
q_bar
<= !
d
;
end
// przesłanianie
always @ (
set
or
reset
) begin
if(
set
) begin
assign
q
=
1
;
assign
q_bar
=
0
;
end else if(
reset
) begin
assign
q
=
0
;
assign
q_bar
=
1
;
end else begin
deassign
q
;
deassign
q_bar
;
end
end
Analizę przebiegów czasowych symulacji pozostawia się czytelnikowi.
// Przykład 1
always @ ( posedge
clk
) begin
a
=
b
;
b
=
a
;
end
// Przykład 2
always @ ( posedge
clk
) begin
a
<=
b
;
b
<=
a
;
end
Zadania do wykonania
1. Przeanalizuj poniższy kod przykładowego modułu w języku Verilog, w protokole
omów jaki będzie efekt działania modułu.
module funkcja(x1, x2, y);
input x1, x2;
output y;
reg y;
assign y = ~(x1 & x2);
endmodule
2. Zaprojektuj wektory testowe dla powyższego modułu a następnie sprawdź jego
działanie w środowisku ModelSim.
3. Zaimplementuj na płycie uruchomieniowej, funkcje logiczne NAND, NOR oraz
XOR, wejścia funkcji podłącz do przełączników SW0-SW1, natomiast wyjścia
powinny być podłączone do którejś z diod LED. Działanie modułu opisz w rapor-
cie.
4. Zaimplementować w języku Verilog poniższy układ kombinacyjny, przetestować
go symulacyjnie a następnie uruchomić na płycie uruchomieniowej.
Raport
Wykonany podczas zajęć protokół, oceniany będzie na zajęciach. Wszystkie programy
wraz z komentarzami należy przesłać do systemu LabPro przed zakończeniem zajęć.
Ocenianie
Maksymalnie z ćwiczenia można uzyskać 5 punktów za dowolnie wybrane zadania.
kartkówka ........1,0
punkt 1 .............0,5
punkt 2..............0,5
punkt 3..............1,5
punkt 4..............2,0
Literatura:
1. Spartan-3A/3AN FPGA Starter Kit Board User Guide
http://www.xilinx.com/support/documentation/boards_and_kits/ug334.pdf
2.
http://en.wikipedia.org/wiki/Hardware_description_language
3.
http://www.asic-world.com/verilog/veritut.html
4.
http://electrosofts.com/verilog/introduction.html
Opracowanie: Jan Staniek, Krzysztof Czyż, 2008