JĘZYK VHDL
• Geneza: komputerowa symulacja układu cyfrowego,
Departament Obrony USA
• opis skomplikowanego systemu w postaci schematu jest
nieczytelny, szybkie wprowadzanie zmian w opisie układu
• języki HDL: VHDL, Verilog, Abel
• zalety: więcej informacji niż w opisie schematowym,
krótszy cykl projektowy złożonych układów, niezależność
od technologii, opis na różnych poziomach abstrakcji,
symulacja na różnych etapach zaawansowania projektu,
standaryzacja
• wady: zmiana stylu projektowania, koszty narzędzi,
szkoleń, problem syntezowalności, jakość syntezy,
trudność oceny złożoności projektu
• VHDL- skrót: VHSIC (Very High Speed Integrated
Circuit) Hardware Description Language, standard
IEEE1076 -1987, IEEE1164-1993r
Poziomy abstrakcji (style opisu
układów):
• behawioralny (algorytmiczny, systemowy) -opis
działania układu bez szczegółów realizacyjnych
(procesy, pętle)
• RTL (przesłań międzyrejestrowych, Dataflow)
• logiczny (wyrażeń boolowskich, bramek)
• strukturalny (podział na podukłady, wielokrotne
wykorzystanie podukładów)
•zasady opisu strukturalnego:
•pełny układ można rozłożyć na komponenty,
•stopień złożoności komponentów jest dowolny
•brak ograniczeń co do liczby poziomów hierarchii
•najniższy poziom hierarchii musi mieć
zdefiniowany model działania (styl behawioralny,
RTL lub logiczny)
•elementy modelu działania:
•sygnały (odpowiedniki fizycznego połączenia,
fizycznych wartości logicznych w układzie)
•procesy (operacje na wartościach logicznych w
układzie)
•współbieżne wykonywanie operacji
poszczególne części układu
działają w sposób równoległy
•dwa rodzaje kodu VHDL:
•kod współbieżny
•kod sekwencyjny (procesy)
Elementy składni języka
* Przypisanie wartości sygnału:
sample <= “0000”
next_sample <= sample
* Operatory relacyjne: =, /=, >, <, <=, >=
* Operatory logiczne: and, xor, or, nand, nor, xnor, not
* Operatory arytmetyczne: +, -, *, /, mod, rem
* Operatory znaku: +, -
* Operator sklejania: &
*Wyrażenie “if -then -elsif -end if” (tylko procesy)
*Wyrażenie “case -when..-when.. -end case” (tylko
procesy)
*Wyrażenie “for” (pętle w procesach, generacje)
*Wyrażenia “when - else” oraz “with.. select”
(odpowiednik case poza procesem) tzn. przypisanie
warunkowe i selektywne
*Konstrukcja “wait”
*Przypisanie wartości zmiennej “:=“
*Pętla “while”
Zasady ogólne:
* VHDL nie rozróżnia dużych i małych liter (ale
należy mieć na uwadze tzw. dobry styl)
* średnik kończy wyrażenie (zakończenie linii
programu)
* każda linia komentarza rozpoczyna się od “--”
* identyfikatory muszą zaczynać się od litery i
zawierać na następnych pozycjach znaki
alfanumeryczne lub pokreślenie “_”
* język silnie zorientowany na typ, automatyczna
konwersja należy do rzadkości, możliwe jest
przeciążanie operatorów, funkcji, procedur
* syntezowalny jest jedynie podzbiór języka
* żaden z systemów EDA nie gwarantuje prawidłowej
syntezy modelowania algorytmicznego
* wielkość układu po syntezie nie ma nic wspólnego z
objętością kodu (należy zrezygnować z finezyjnego
zapisu!!!)
Opis układu/podukładu składa się z deklaracji
jednostki (ENTITY) i jednego lub więcej bloków
ARCHITECTURE opisującego działanie. ENTITY
zawiera listę zacisków wejściowych i wyjściowych
(PORTS), jest ekwiwalentem symbolu na schemacie
entity model_name is
port (
list of inputs and outputs
);
end model_name;
architecture arch_name of model_name is
begin
.....
VHDL concurrent statements
.....
end arch_name;
when -else
library IEEE;
use IEEE.std_logic_1164.all;
entity mux2to1 is
port (
signal
s
:in
std_logic;
signal
x0,x1 :in
std_logic_vector(7 downto 0);
signal
y
:out
std_logic_vector(7 downto 0) --brak średnika!!!
);
end mux2to1;
architecture a1 of mux2to1 is
begin
y <=
x1 when (s=‘1’)
else x0;
end a1;
ALTERA -nazwa pliku musi pokrywać się z nazwą entity (tutaj mux2to1.vhd)
* LIBRARY -biblioteka to zbiór zanalizowanych jednostek
projektowych przechowywany w danym systemie operacyjnym
(nazwa logiczna a nie katalog w systemie!)
* aby można się było odwoływać do elementów biblioteki należy
użyć klauzuli
USE (specyfikuje które pakiety -package będą wybrane z
biblioteki)
use IEEE.std_logic_1164.all
importuje procedury, funkcje i definicje pakietu std_logic_1164
* Pakiet std_logic_1164 definiuje system logiczny 9-wartościowy,
4 wartości są przydatne dla syntezy: ‘1’, ‘0’, ‘Z’ (high
impedance), ‘-’ (don’t care),
pozostałe wartości używane są dla celów symulacji:
‘U’ (uninitialized), ‘X’ (unknown-strong drive), ‘W’,’L’,’H’
(unknown, logic 0, logic 1 -weak drive).
Dwa typy danych: std_ulogic oraz std_logic -z dodatkową
funkcją rozstrzygającą konflikty połączeń (resolution function).
Zalecane jest używanie typu std_logic!
Przykład: sygnał y ma dwa sterowniki (driver’y)
y<=a;
y<=b;
* typ std_logic jest pojedynczym bitem, std_logic_vector jest
używany do definiowania magistral
* VHDL posiada wbudowany typ bit 2-wartościowy ‘1’ i ‘0’; jest on
niewystarczający do syntezy; istnieje też typ bit_vector
* na liście portów podajemy końcówki zewnętrzne układu w
konwencji:
nazwa tryb
typ
dla celów syntezy używa się trybów in, out, inout
słowo signal jest opcjonalne -najczęściej w deklaracji portów
opuszczane
* szerokość magistrali można definiować z indeksem rosnącym lub
malejącym
std_logic_vector (7 downto 0) lub std_logic_vector(0 to
7)
skutek: różny wynik operacji przypisania
np. y<=“11110000” (wektory piszemy w podwójnym
cudzysłowiu), jest równoważne operacjom:
y(7)<=‘1’; ... ; y(0)<=‘0’; lub y(0)<=‘1’; ... ; y(7)<=‘0’;
* indeks elementu magistrali może mieć dowolną wartość
całkowitą
* można użyć w opisie układu poszczególnych elementów
magistali
y(5 downto 2)<=“0110”;
UWAGA: kierunek indeksowania musi być zgodny z deklaracją
magistrali
y(5 downto 3)<=x(0 to 2);
oznacza przypisanie y(5)<=x(0); y(4)<=x(1); y(3)<=x(2);
* jeżeli przypisujemy wartość całej magistrali wówczas nie trzeba
specyfikować indeksów np. y<=x1; szerokość magistrali po obu
stronach musi być identyczna!!!
* operator sklejania & (concatenation), np. dla deklaracji:
signal a
:out
std_logic_vector(7 downto 0);
signal b
: in
std_logic_vector(3 downto 0);
możliwe jest przypisanie
a <= “100” & b & ‘1’;
* ogólna postać warunkowego przypisania sygnału:
signal_name <=
value1 when condition1
else value2 when condition2
....
else valueN when conditionN
else default_value;
warunki są wyrażeniami przyjmującymi wartości true/false
* wynik syntezy:
Przypisanie selektywne (selected signal
assignment)
* Ogólna postać:
with condition select
signal_name<=
value1 when cond_value1,
value2 when cond_value2,
....
valueN when cond_valueN,
valueX when others;
* warunki mogą być rozdzielone operatorem “or” w postaci |
* przykład zastosowania:
with sel select
a<=
d when “000” | “001”,
e when “101”,
‘Z’ when others;
* w niektórych przypadkach efekt działania identyczny z
warunkowym przypisaniem sygnału
* współbieżne przypisanie sygnału (concurrent signal
assignment), warunkowe (conditional..) i selektywne
(selected ...) są instrukcjami współbieżnymi,
wykonywanymi jednocześnie i kolejność ich umieszczenia
w bloku “architecture” nie ma znaczenia
Multiplekser 2/1 -metoda równań kombinacyjnych
library IEEE;
use IEEE.std_logic_1164.all;
entity mux2to1 is
port (
s
:in
std_logic;
x0,x1 :in
std_logic_vector(7 downto 0);
y
:out
std_logic_vector(7 downto 0)
);
end mux2to1;
architecture a2 of mux2to1 is
signal temp: std_logic_vector(7 downto 0);
begin
temp<=(others=>s);
y<=(temp and x1) or (not temp and x0);
end a2;
* sygnał s (port wejściowy) nie może być bezpośrednio użyty w
operacjach logicznych z sygnałami x0 i x1 ze względu na różny typ
(std_logic, std_logic_vector)
* zadeklarowano sygnał wewnętrzny -wektor/magistralę; deklaracje
sygnałów umieszcza się zawsze przed słowem kluczowym begin
* w deklaracji sygnału wewnętrznego nie określa się trybu (in, out,
inout)
* typ sygnału temp jest zgodny z x0 i x1 (ta sama szerokość
wektora!!!)
* każdemu bitowi wektora temp przypisano stan portu wejściowego
s
temp <= (others=>s);
--najprostszy zapis (aggregate)
temp<= (s,s,s,others=>s);
temp<= (4=>s, 7=>s, 2=>s, 5=>s, 3=>s, 1=>s, 6=>s,
0=>s);
temp<= s & s & s & s & s & s & s & s;
temp(0)<=s;
temp(1)<=s;
.....
temp(7)<=s;
temp(7 downto 4)<= (s,s,s,s);
temp(2 downto 0)<= (others=>s);
temp(3)<=s;
* zapis:
y<=(temp and x1) or (not temp and x0);
jest równoważny z przypisaniem odpowiedniego wyrażenia
każdemu elementowi wektora y
y(7)<= (temp(7) and x1(7)) or (not temp(7) and x0(7));
y(6)<= (temp(6) and x1(6)) or (not temp(6) and x0(6));
.....
y(0)<= (temp(0) and x1(0)) or (not temp(0) and x0(0));
* ponieważ każdy element wektora temp jest równy s, to
wykonywane jest działanie:
y(7)<= (s and x1(7)) or (not s and x0(7));
......
Multiplekser 2/1 -użycie procesu
kombinacyjnego/niezegarowanego
architecture a3 of mux2to1 is
begin
comb: process (s, x0, x1)
begin
if (s=‘1’) then
y<= x1;
else
y<=x0;
end if;
end process comb;
end a3;
* proces jest częścią kodu VHDL wykonywaną (analizowaną)
sekwencyjnie; proces traktowany jako całość wykonywany jest
współbieżnie z innymi procesami w architekturze oraz z innymi
instrukacjami współbieżnymi; w obrębie procesu dozwolone jest
stosowanie wyłącznie instrukcji sekwencyjnych
* zmiana wartości któregokolwiek sygnału umieszczonego na liście
czułości procesu powoduje analizę jego kodu przez symulator
* w procesach kombinacyjnych na liście czułości należy umieścić
wszystkie (!!!) sygnały pobudzające blok logiki kombinacyjnej,
który ma być efektem syntezy procesu
s
x0(7)
x1(7)
y(7)
* w zależności od stanu sygnału s wyjście y powtarza zmiany na liniach x0 lub x1
* proces musi być analizowany gdy:
(1) zachodzą zmiany wartości wektora x0 oraz s=0,
(2) zachodzą zmiany wartości wektora x1 oraz s=1,
(3) zmienia się wartość sygnału s.
* przypisanie nowej wartości do sygnału następuje w chwili
wyjścia symulatora z procesu;
*w trakcie analizy kodu procesu rozpisywana jest tzw. transakcja;
faktyczne przypisanie następuje z opóźnieniem delta,
odpowiadającym najmniejszemu kwantowi czasu symulacyjnego
(jeżeli nie wskazano inaczej!)
np:
czas
a b
c out1 out2 out3
process(a,b,c,out1,out2)
t1
0 1 1 0 1
1
begin
t2
1 1 1 0->1 1 1
out1<=a and b;
t2+d
1 1 1 1 1-
>0 1->0
out2<=out1 xor c;
t2+2d 1 1 1 1 0 0->1
out3<=out1 xor out2;
t2+3d 1 1 1 1 0
1
end process
* wniosek: pomimo że proces jest kodem sekwencyjnym, to
kolejność umieszczenia przypisań do sygnałów jest dowolna
* w procesach których zamierzonym efektem ma być logika
kombinacyjna konieczne jest zdefiniowanie sygnałów wyjściowych
procesu dla wszystkich możliwych kombinacji wartości sygnałów
wejściowych
* często, aby uniknąć pomyłki na początku kodu procesu umieszcza
się przypisanie wartości domyślnej
comb: process(s, x0, x1)
begin
y<=x0;
if (s=‘1’) then
y<=x1;
end if;
end process comb;
* w przypadku braku zdefiniowanego wyjścia dla pewnej grupy
wartości wejściowych generowane są w procesie syntezy
przerzutniki typu latch
* proces opisuje reakcje części układu na pobudzenia
(sygnały) bez wskazania sposobu realizacji
Enkoder priorytetowy
entity en_prior is
port( x
:in std_logic_vector(1 to 7);
enc
:out std_logic_vector(2 downto 0)
);
end en_prior;
architecture e1 of en_prior is
begin
process(x) -- każdy bit wektora x na liście czułości
begin
if (x(7)=‘1’) then enc<=“111”;
elsif (x(6)=‘1’) then enc<=“110”;
elsif (x(5)=‘1’) then enc<=“101”;
elsif (x(4)=‘1’) then enc<=“100”;
elsif (x(3)=‘1’) then enc<=“011”;
elsif (x(2)=‘1’) then enc<=“010”;
elsif (x(1)=‘1’) then enc<=“001”;
else enc<=“000”;
-- domyślna wartość wyjściowa
end if;
end process;
end e1;
* proces -kod sekwencyjny prowadzi do relizacji układowej czysto
kombinacyjnej
* kolejność sprawdzania warunków w łańcuchu wyrażeń if-elsif....
definiuje priorytet: najwyższy ma bit 7-my wektora x., bo jego
warunek sprawdzany jest w procesie jako pierwszy
x(6)
x(4)
x(5)
x(7)
x(2)
x(1)
x(3)
enc(2)
enc(0)
enc(1)
* w ramach procesu brane jest pod uwagę wyłącznie ostatnie
napotkane podczas analizy kodu przypisanie wartości do sygnału;
poniższy proces opisuje ten sam układ:
process(x)
begin
enc<=“000”;
--wartość domyślna
if (x(1)=‘1’) then enc<=“001”; end if;
if (x(2)=‘1’) then enc<=“010”; end if;
if (x(3)=‘1’) then enc<=“011”; end if;
if (x(4)=‘1’) then enc<=“100”; end if;
if (x(5)=‘1’) then enc<=“101”; end if;
if (x(6)=‘1’) then enc<=“110”; end if;
if (x(7)=‘1’) then enc<=“111”; end if;
end process;
* bit 7-my ma najwyższy priorytet bo jest testowany jako ostatni (!!!)
* ten sam efekt - przypisanie warunkowe, bez użycia procesu:
enc <=
“111” when x(7)=‘1’ else
“110” when x(6)=‘1’ else
“101” when x(5)=‘1’ else
....
“001” when x(1)=‘1’ else
“000”; --wartość domyślna
Przykład pętli -opis behawioralny enkodera priorytetowego
library IEEE;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
entity en_prior is
port( x
:in std_logic_vector(1 to 7);
enc
:out std_logic_vector(2 downto 0)
); end en_prior;
architecture eloop of en_prior is
begin
process(x)
variable i: integer; --definicja zmiennej
begin
i:=7;
--przypisanie wartości do zmiennej
while (i>0 and x(i)/=‘1’) loop
i:=i-1;
end loop;
enc<=conv_std_logic_vector(i,3); --na drugim miejscu podana liczba pozycji
end process;
--wektora wynikowego
end eloop;
* zdefiniowano zmienną typu integer, przypisania do zmiennej
dokonuje się przy pomocy operatora :=
* zmienne użyte w procesie są widoczne tylko w obrębie tego
procesu; przechowują swoje wartości w trakcie “uśpienia” procesu
* przypisanie nowej wartości do zmiennej następuje natychmiast -w
momencie analizy linii przez symulator, np:
process(a,b,c)
czas
a b
c out1 out2 out3
variable out1,out2:std_logic;
t1
0 1 1 0 1
1
begin
t2
1 1 1 1 0 1->0
out1:=a and b;
t2+d
1 1 1 1 0 0
out2:=out1 xor c;
t3
1 0 0 0 0 0
out3<=(not out1) and out2;
t4 1 0 1 0 1 0-
>1
end process;
t4+d
1 0 1 0 1 1
* istotna jest kolejność wykonania przypisań, nowa wartość zmiennej
jest uwzględniania w następnych liniach procesu
* użyto funkcji konwersji integer -> std_logic_vector zdefiniowanej w
pakiecie std_logic_arith
* UWAGA: konstrukcje z pętlą while są często niesyntezowalne
(Altera-Max+PlusII)
Synplicity-wynik syntezy jest identyczny jak dla poprzednich
architektur
Przykład pętli w układzie kombinacyjnym -opis
syntezowalny
ENTITY proc IS
PORT(
d
: IN BIT_VECTOR (2 DOWNTO 0);
q
: OUT INTEGER RANGE 0 TO 3
); END proc;
ARCHITECTURE maxpld OF proc IS
BEGIN
PROCESS (d)
--zliczanie jedynek logicznych w słowie
wejściowym
VARIABLE num_bits : INTEGER;
BEGIN
num_bits := 0;
FOR i IN d'RANGE LOOP --atrybut RANGE, zmienna i zdefiniow.
automatycznie
IF d(i) = '1' THEN
num_bits := num_bits + 1;
END IF;
END LOOP;
q <= num_bits;
END PROCESS;
END maxpld;
* użyto wbudowanego typu bit
* typ integer jest w układach
cyfrowych interpretowany jako
słowo binarne; może być
wprost użyty w definicji portu
bez potrzeby konwersji typu
* długość słowa zależy od
zakresu liczby integer - w
przykładzie 2 bity
* standardowo liczba integer
zapisywana jest na 32 bitach
* wskazówka: w kodzie
przeznaczonym do syntezy
należy używać jak
najmniejszych liczb integer
i zawsze ograniczać zakres
*
typy pętli:
prosta
z określonym schematem iteracji
L1:loop
L2:for I in 1 to 3 loop
do_something;
A(I)<=B(I);
end loop L1;
end loop L2;
L3:while condition loop
do_something;
end loop L3;
* instrukcje używane w połączeniu z pętlami:
next
np:
next L1 when I=J;
next when (B<10);
exit
np:
exit L2;
exit Loop3 when a>b;
*podstawowym warunkiem syntezowalności kodu z użyciem
pętli jest ustalona na etapie syntezy liczba wykonań pętli
* zaleca się stosowanie atrybutów do specyfikacji zakresu pętli
GENERATOR PARZYSTOŚCI
entity parity is
generic (bus_size : integer := 8 );
port ( input_bus : in std_logic_vector (bus_size-1 downto 0);
even_numbits, odd_numbits : out std_logic ) ;
end parity ;
architecture behave of parity is
begin
process (input_bus)
variable temp: std_logic;
begin
temp := '0';
for i in input_bus'low to input_bus'high loop
temp := temp xor input_bus( i ) ;
end loop ;
odd_numbits <= temp ; even_numbits <= not temp;
end process;
end behave;
* użyto parametru ogólnego generic; pozwala on na
parametryzowanie podprojektów
* statyczny (!) przepływ informacji pomiędzy modułami
* w szczególności zaleca się używanie tego typu stałej w
definiowaniu wymiarów magistral, tablic, parametrów czasowych
(w kodzie przeznaczonym do symulacji)
* wartość parametru generic może być ustalona przy korzystaniu z
danego komponentu na wyższym stopiniu hierarchii, łatwa
rekonfigurowalność projektu
KOMPARATOR
entity compare is
generic (size : integer := 16 );
port ( a,b
:in std_logic_vector(size-1 downto 0);
equal :out std_logic );
end compare;
architecture c1 of compare is
begin
equal<= ‘1’ when a=b
else ‘0’;
end c1;
Dekoder 3 na 8
entity dec3to8 is port(
sel
:in std_logic_vector(2 downto 0);
--wejścia
ena
:in std_logic;
--output enable
y
:out std_logic_vector(7 downto 0) --wyjścia aktywne
stanem niskim
); end dec3to8;
architecture d1 of dec3to8 is
begin
procd:process(sel,ena)
begin
y<=“1111_1111”; --wartość domyślna
if (ena=‘1’) then
case sel is
when “000”=> y(0)<=‘0’; when “001”=> y(1)<=‘0’;
when “010”=> y(2)<=‘0’; when “011”=> y(3)<=‘0’;
when “100”=> y(4)<=‘0’; when “101”=> y(5)<=‘0’;
when “110”=> y(6)<=‘0’; when “111”=> y(7)<=‘0’;
end case;
end if;
end process procd;
end d1;
* wersja prawidłowa
* wersja bez wartości domyślnej;
generowane są przerzutniki typu latch
* użyto wyrażenia sekwencyjnego case dozwolonego wyłącznie w
procesach
* w wyrażeniu case można przypisać wartość domyślną używając
konstrukcji
when OTHERS => .........
* wersja alternatywna z użyciem przypisania warunkowego osobno
dla każdego bitu
architecture d2 of dec3to8 is
begin
y(0)<=‘0’ when (ena=‘1’ and sel=“000”) else ‘1’;
y(1)<=‘0’ when (ena=‘1’ and sel=“001”) else ‘1’;
y(2)<=‘0’ when (ena=‘1’ and sel=“010”) else ‘1’;
y(3)<=‘0’ when (ena=‘1’ and sel=“011”) else ‘1’;
y(4)<=‘0’ when (ena=‘1’ and sel=“100”) else ‘1’;
y(5)<=‘0’ when (ena=‘1’ and sel=“101”) else ‘1’;
y(6)<=‘0’ when (ena=‘1’ and sel=“110”) else ‘1’;
y(7)<=‘0’ when (ena=‘1’ and sel=“111”) else ‘1’;
end d2;
* indeksowanie przez sygnał-niezbędna funkcja konwersji znajduje
się w pakiecie std_logic_unsigned;
process (sel, ena)
begin
y<=“11111111”; --wartość domyślna
if (ena=‘1’) then y(conv_integer(sel))<=‘0’; end if;
end process;
* wersja dekodera z pętlą w procesie
process(sel,ena)
begin
y<=“1111_1111”;
for i in y’range loop
if (ena=‘1’ and i=conv_integer(sel)) then
y(i)<=‘0’;
end if;
end loop;
end process;
funkcja konwersji conv_integer jest dostępna po zadeklarowaniu
use ieee.std_logic_unsigned.all;
* wersja może być łatwo sparametryzowana (z użyciem generic),
przypisanie wartości domyślnej należy wówczas zastąpić drugą
pętla:
for j in y’range loop
y(j)<=‘1’;
end loop;
Operatory przesunięcia (shift)
sll, srl, sla, sra, rol, ror -lewym argumentem jest bit_vector,
prawym liczba całkowita
sll, srl uzupełniają wektor wartością ‘0’
sla, sra powielają ostatni bit
rol, ror wykonują rotację
Uwaga: modelując rejestry przesuwne używa się operatora
podstawienia z przesunięciem indeksów w wektorach i operatora
sklejania &, zamiast operatorów przesunięcia
* wersja dekodera 3 na 8 z operatorem przesunięcia
architecture shift1 of dec3to8 is
begin
with ena select
y<= to_stdlogicvector(“1111_1110” rol conv_integer(sel))
when ‘1’,
“1111_1111” when others;
end shift1;
Pół-sumator i pełny sumator 1 bitowy
entity halfadder is port(
a,b
:in std_logic;
sum,cout
:out std_logic);
end halfadder;
architecture a1 of halfadder is
begin
process(a,b)
--OPIS BEHAWIORALNY
begin
if (a=‘1’ and b=‘1’) then
cout<=‘1’;
else cout<=‘0’;
--cout<=a and b;
end if;
if ((a=‘1’ and b=‘0’) or (a=‘0’ and b=‘1’)) then
sum<=‘1’;
else sum<=‘0’;
--sum<=a xor b;
end if;
end process;
end a1;
HALFADDER
RTL -proces
“Technology View”-Flex8000
RTL -równania logiczne
entity fulladder is port(
a,b,cin
:in std_logic;
sum,cout
:out std_logic);
end fulladder;
architecture a1 of fulladder is
-- OPIS DATAFLOW (poziom
bramek)
begin
sum<=a xor b xor cin;
cout<=(a and b) or (a and cin) or (b and cin); -- or (a and b and
cin)
end a1;
GENERACJA (generate)
* jest odpowiednikiem pętli we współbieżnej części kodu
* służy do automatycznej generacji struktur regularnych,
tworzonych na bazie struktury wzorcowej (fizyczny efekt =>
powielenie podukładów wzorcowych)
* wewnątrz generate może być powielana dowolna instrukcja
współbieżna łącznie z samą instrukcją generate
* dwa schematy generacji: generacja for dla układów w pełni
regularnych, generacja if gdy istnieją nieregularności w układzie
* wymagana jest etykieta (label) generacji
* przykład:
pełny sumator
wielobitowy
entity fulladder_n is
generic (size : integer := 4 );
port( a,b
:in std_logic_vector(size-1 downto 0);
sum
:out std_logic_vector(size-1 downto 0);
cin
: in std_logic;
cout
:out std_logic);
end fulladder_n;
architecture a1 of fulladder_n is
signal c
:std_logic_vector(size downto 0);
begin
c(0)<=cin;
G: for i in 0 to size-1 generate
sum(i)<=a(i) xor b(i) xor c(i);
c(i+1)<=(a(i) and b(i)) or (a(i) and c(i)) or (b(i) and c(i));
end generate;
cout<=c(size);
end a1;
* parametr generacji nie musi być deklarowany (tutaj i)
entity adder_n is
generic (size : integer := 4 );
port( a,b
:in std_logic_vector(size-1 downto 0);
sum
:out std_logic_vector(size-1 downto 0);
cout
:out std_logic);
end adder_n;
architecture a1 of adder_n is
signal c
:std_logic_vector(size downto 1);
begin
G1: for i in 0 to size-1 generate
G2:if i=0 generate
sum(i)<=a(i) xor b(i);
c(i+1)<=a(i) and b(i);
end generate;
--gałęzie else i elsif nie są dozwolone!!!
G3: if i>0 generate
sum(i)<=a(i) xor b(i) xor c(i);
c(i+1)<=(a(i) and b(i)) or (a(i) and c(i)) or (b(i) and c(i));
end generate;
end generate;
cout<=c(size);
end a1;
* ten sam efekt uzyskuje się stosując pętle w procesie
architecture a2 of fulladder_n is
begin
process(a,b,cin)
variable c: std_logic;
begin
c:=cin;
L: for i in 0 to size-1 loop
sum(i)<=a(i) xor b(i) xor c;
c:=(a(i) and b(i)) or (a(i) and c) or (b(i) and c);
end loop L;
cout<=c;
end process;
end a2;
* istotna jest kolejność przypisań w pętli (c jest zmienną!!!)
do obliczenia bieżącej wartości sum(i), oraz przeniesienia c brane
jest pod uwagę przeniesienie z poprzedniej pozycji (c wyliczone w
poprzednim wykonaniu pętli)
architecture a2 of adder_n is
begin
process(a,b)
variable c: std_logic;
begin
L: for i in 0 to size-1 loop
if i=0 then
sum(i)<=a(i) xor b(i);
c:=a(i) and b(i);
else
sum(i)<=a(i) xor b(i) xor c;
c:=(a(i) and b(i)) or (a(i) and c) or (b(i) and c);
end if;
end loop L;
cout<=c;
c:=‘0’;
end process;
L: for i in 0 to size-1 loop
end a2;
sum(i)<=a(i) xor b(i) xor c;
c:=(a(i) and b(i)) or (a(i) and c) or (b(i) and c);
end loop L;
cout<=c;
* wykorzystanie sumowania arytmetycznego zdefiniowanego w
pakiecie std_logic_unsigned:
architecture a3 of adder_n is
signal c
:std_logic_vector(size downto 0);
begin
c<=(‘0’&a)+(‘0’&b);
--przy zapisie c<=a+b;
sum<=c(size-1 downto 0);
--różna szerokość wektora, nie
ma gwarancji
cout<=c(size);
--prawidłowej syntezy !!!
end a3;
* operacja na liczbach całkowitych:
entity fulladder_int is port(
a,b
:in integer range 0 to 255;
sum
:out integer range 0 to 511;
cin
: in integer range 0 to 1);
end fulladder_int;
architecture a1 of fulladder_int is begin
sum<=a+b+cin;
end a1;
Projektowanie hierarchiczne (opis strukturalny)
* w opisie układu przywołujemy wcześniej zdefiniowane jednostki
projektowe, dołączając sygnały do odpowiednich portów tych
jednostek (tworzenie powiązań)
* typy i zakresy sygnałów aktualnych i lokalnych (w
przywoływanych jednostkach) muszą być zgodne
* przy tworzeniu powiązań między portami obowiązuje zgodność
kierunków (trybów)
* przywołanie komponentu może również ustalać wartości
parametrów ogólnych
entity adder_4 is
port( a,b
:in std_logic_vector(3 downto 0);
cin :in std_logic;
sum :out std_logic_vector(3 downto 0);
cout :out std_logic);
end adder_4;
architecture struct of adder_4 is
begin
component fulladder port(
fa0: fulladder port
map(a(0),b(0),cin,sum(0),c0);
a,b,cin
:in std_logic;
fa1: fulladder port
map(a(1),b(1),c0,sum(1),c1);
sum,cout :out std_logic);
fa2: fulladder port
map(a(2),b(2),c1,sum(2),c2);
end component;
fa3: fulladder port
map(a(3),b(3),c2,sum(3),cout);
signal c0,c1,c2: std_logic;
end struct;
* przyłączenia sygnałów do końcówek komponentu można
dokonywać w dowolnej kolejności, ale z przywołaniem nazwy
portu:
fa2: fulladder port
map(b=>b(2),a=>a(2),sum=>sum(2),cout=>c2,cin=>c1);
* specyfikacja parametrów ogólnych
-w deklaracji komponentu:
component bufor
generic (size:integer:=8);
port( a:in std_logic_vector(size-1 downto 0);
y:out std_logic_vector(size-1 downto 0) );
end component;
-w podstawieniu komponentu
buf1: bufor
generic map(size=>8)
port map(a=>port_in,y=>port_out);
-w konfiguracji
Układy sekwencyjne
Przerzutniki typu zatrzask (latch) aktywne poziomem
* zatrzask jest elementem pamiętającym
latch: process(enable,data)
process(enable,data)
begin
begin
if (enable=‘1’) then
if (enable=‘1’) then
q<=data;
q<=data;
end if;
else
end process latch;
q<=‘0’;
end if;
end process; -- q<=enable and
data;
enable
data
q
* na liście czułości procesu należy umieścić wejście zegarowe oraz
wejście danych(!)
* układ jest transparentny jeżeli warunek enable=‘1’ jest
spełniony; w przeciwnym wypadku proces nie zawiera żadnych
instrukcji -na wyjściu q pamiętana jest ostatnia wartość wejścia
data przy aktywnym zegarze
* proces z określonym sterownikiem dla warunku enable/=‘1’
będzie syntezowany jako bramka AND
* w technologiach w których przerzutniki typu latch nie są
dostępne, synteza prowadzi do ekwiwalentu złożonego z bramek
objętych sprzężeniem zwrotnym
* zatrzaski mogą być również generowane poza procesem w części
współbieżnej
b1: block (enable=‘1’) --warunek dozorujący bloku
begin
q<= GUARDED data; --sygnał dozorowany
end block;
zatrzask (latch) z resetem asynchronicznym i
synchronicznym
process(enable,data,rst)
process(enable,data,rst)
begin
begin
if (rst=‘1’) then
if (enable=‘1’) then
q1<=‘0’;
if (rst=‘1’) then
elsif (enable=‘1’) then
q2<=‘0’;
q1<=data;
else
end if;
q2<=data;
end process;
end if;
end if;
end process;
enable
rst
data
q
q1
q2
Przerzutniki aktywne zboczem (edge-sensitive flip-
flop)
* proces sterowany zegarem (clocked process): zawiera instrukcję
if uzależnioną od narastającego lub opadającego zbocza zegara,
albo instrukcję oczekiwania na zmianę stanu sygnału wait until
* w wyniku syntezy powstają przerzutniki aktywne zboczem
* atrybuty ‘event oraz not ...’stable często są używane do
wykrywania zbocza zegara, przy czym sygnał zegara musi być
jednobitowy (typu bit lub std_logic)
process
begin
wait until (clk’event and clk=‘1’ )
q<=data;
end process;
* proces w którym zastosowano instrukcję wait nie może posiadać
listy czułości
* instrukcja wait musi być pierwszą instrukcją procesu
* proces startuje wyłącznie przy zmianach sygnału clk, w związku z
tym warunek można skrócić do postaci:
wait until clk=‘1’
wait until clk=‘0’ --opadające zbocze
process(clk)
begin
if (clk’event and clk=‘1’) then
if (clk’event and
clk=‘0’) then
q<=data;
if (not clk’stable and
clk=‘1’) then
end if;
end process;
* po warunku testującym zbocze zegara nie może wystąpić żadna
gałąź else lub elsif
* dla większości kompilatorów wykrywanie zbocza zegarowego
musi być jedynym testowanym w danym wyrażeniu warunkiem
-niedopuszczalne jest łączenie z testowaniem innego sygnału
* jeżeli wykrywanie zbocza zegara następuje w wyrażeniu if... , to
sygnał zegarowy musi(!!!) być umieszczony na liście
wrażliwościowej procesu
* proces może opisywać układ reagujący na zbocze tylko jednego
sygnału (brak fizycznych elementów sterowanych zboczami wielu
sygnałów)
* możliwy jest opis w części współbieżnej kodu
b2:block (clk’event and clk=‘1’)
--warunek dozorujący
begin
q<= GUARDED data;
end block;
przerzutniki z synchronicznym lub asynchronicznym
zerowaniem/ustawianiem/ładowaniem
process(clk)
process(clk,clear)
begin
begin
if (clk’event and clk=‘1’) then
if (clear=‘1’) then
if (clear=‘1’) then
q2<=‘0’
q1<=‘0’;
elsif (clk’event and clk=‘1’)
then
else
q2<=data;
q1<=data;
end if;
end if;
end process;
end if;
end process;
* w przypadku równoczesnej realizacji funkcji synchronicznej i
asynchronicznej na liście wrażliwościowej należy umieścić
wszystkie sygnały testowane i używane w procesie, przed
warunkiem wykrywającym zbocze zegara
* rozwiązanie alternatywne:
b3:block (clk’event and clk=‘1’)
begin
q<= GUARDED ‘0’ when reset=‘1’ else data; --reset
synchroniczny
end block;
* przerzutnik aktywny
opadającym
zboczem zegara,
asynchroniczne ustawianie
(aktywne stanem niskim)
process(clk,preset)
begin
if (preset=‘0’) then
q<=‘1’
elsif (clk’event and clk=‘0’)
then
q<=data;
end if;
end process;
* przerzutnik aktywny
narastającym zboczem zegara,
asynchroniczne zerowanie
(wyższy priorytet) i ustawianie
process(clk,reset,preset)
begin
if (reset=‘1’) then
q<=‘0’;
elsif (preset=‘1’) then
q<=‘1’;
elsif (clk’event and clk=‘1’)
then
q<=data;
end if;
end process;
process(clk,load,data_lo)
begin
if (load=‘1’) then --asynchroniczne ładowanie nowej zawartości data_lo
q<=data_lo;
elsif (clk’event and clk=‘1’) then --testowanie narastającego zbocza clk
q<=data;
end if;
end process;
process(clk,reset)
begin
if (reset=‘0’) then --asynchroniczne zerowanie
q<=‘0’;
elsif (clk’event and clk=‘1’) then
if (load=‘1’) then --multiplekser przełączający wejścia data_lo i data
q<=data_lo;
else
q<=data;
end if;
end if;
end process;
* nie wszystkie kombinacje funkcji synchronicznych i
asynchronicznych są syntezowalne w dowolnej technologii
ALTERA: możliwy jest równocześnie asynchroniczny reset i preset
przy czym reset ma priorytet, lub można wykorzystać
asynchroniczne ładowanie wartości sygnału zewnętrznego
XILINX: w układach FPGA przerzutniki mogą mieć tylko
asynchroniczny reset lub preset
* nie ma ograniczeń technologicznych co do części synchronicznej,
złożony opis zostanie przekształcony w logikę kombinacyjną przed
wejściem danych przerzutnika
* wykorzystując pakiet std_logic_1164 można wykrywać zbocza przy
pomocy funkcji
if rising_edge(clk)
then .... lub if falling_edge(clk) then .....
* wykorzystanie wejścia clock enable (wybrane technologie) oraz
bramkowanie zegara (gated clock -uwaga, niebezpieczeństwo
powstania szpilek na linii zegara)
process(clk)
process(clk,gate)
begin
begin
if (clk’event and clk=‘1’) then
if (gate=‘1’) then
if (clk_enable=‘1’) then
if (clk’event and
clk=‘1’) then
q<=data;
q<=data;
end if;
end if;
end if;
end if;
end process;
end process;
Alternatywne opisy przerzutników
-w kodzie współbieżnym (przypisanie warunkowe)
* przerzutnik typu latch -metoda opisu zalecana przez syntezer
Synplify firmy Synplicity
q <= data when (clk=‘1’)
else q;
* przerzutnik reagujący na zbocze (system projektowy Synario)
q <= data when (clk’event and clk=‘1’)
else q;
-w procesie
wait until ((clk’event and clk=‘1’) or (reset=‘1’))
if (reset=‘1’) then
q<=‘0’;
else
q<=data;
end if;
*konstrukcja if..then..elsif.. zapewnia większe możliwości opisu
niż wyrażenie wait until
*należy unikać mało czytelnych, nietypowych konstrukcji języka,
generowany w ten sposób kod może okazać się niesyntezowalny
przy pomocy innych narzędzi
przerzutnik z wyjściem trójstanowym
process(THREESTATE, CLK)
begin
if (THREESTATE=‘0’) then
OUTPUT <= ‘Z’;
elsif (CLK’event and CLK=‘1’) then
if (CONDITION=‘1’) then
OUTPUT <= INPUT;
end if;
end if;
end process;
* opis nieprawidłowy: generowane są
dwa przerzutniki
architecture EXAMPLE of reg_three is
signal TEMP: std_logic;
begin
process(CLK)
begin
if (CLK’event and CLK=‘1’) then
if (CONDITION=‘1’) then
TEMP<=INPUT;
end if;
end if;
end process;
process (THREESTATE,TEMP)
begin
if (THREESTATE=‘0’) then
OUTPUT<=‘Z’;
else
OUTPUT<=TEMP;
end if;
end process;
end EXAMPLE;
wersja z nieprawidłowym opisem
procesy synchroniczne
* rozszerzenie zasad obowiązujących dla przerzutników na szerszą
klasę układów
* przykład: zatrzask magistrali z asynchronicznym
zerowaniem/ładowaniem
process(clk,reset)
process(clk,load,data_lo)
begin
begin
if reset=‘1’ then
if load=‘1’ then
data_out(7 downto 0)<=“00000000”;
data_out(0 to
7)<=data_lo(0 to 7);
elsif (clk’event and clk=‘1’) then
elsif (clk’event and
clk=‘1’) then
data_out(7 downto 0)<=data_in(7 downto 0); data_out(0 to
7)<=data_in(0 to 7);
end if;
end if;
end process;
end process;
* nie należy opisywać w obrębie jednego procesu logiki posiadającej
asynchroniczny reset/preset i nie posiadającej tej funkcji; może to
prowadzić do błędnej syntezy
* w procesie synchronicznym nie dopuszcza się przypisań poza
instrukcją if, z wyjątkiem przypisania sygnałowi wartości zmiennej
obliczonej wewnątrz instrukcji warunkowej if
* asynchroniczne zerowanie
* asynchroniczne
ładowanie data_lo
liczniki synchroniczne
entity counter is
port ( clk
:in bit;
clear, load, ena
:in bit; --asynchr. zerowanie, synch. ładowanie, zezwol. na zliczanie
up_ndown :in bit; --zliczanie w góre (1) lub w dół (0)
data_in
:in integer range 0 to 255;
count
:out integer range 0 to 255);
end counter;
architecture behavior of counter is
begin
process(clk, clear, up_ndown)
variable cnt :integer range 0 to 255;
variable dir :integer;
begin
if (up_ndown=‘1’) then
dir:=1;
else
dir:=-1;
end if;
if (clear=‘1’) then
cnt:=0;
elsif (clk’event and clk=‘1’) then
if (ena=‘1’) then
if (load=‘1’) then
cnt:=data_in;
else
cnt:=cnt+dir;
end if;
end if;
end if;
count <= cnt;
end process;
end behavior;
* kierunek zliczania =>sumator wartości bieżącej rejestru z liczbą
00000001 (+1) lub 11111111 (-1),
ena=‘1’, load=‘0’ => zwiększona lub zmniejszona wartość podana
na wejścia D przerzutników
* ena=‘0’ => przepisywana jest wartość bieżąca przerzutników
(brak modyfikacji)
* ena, load=‘1’ => na wejścia przerzutników podawana jest wartość
data_in
* asynchroniczne zerowanie - clear podane bezpośrednio do
przerzutników
ograniczenie zakresu zliczania
entity counter is
port( clk, preset
:in std_logic;
count
:out std_logic_vector(7 downto 0) );
end counter;
architecture behave of counter is
signal count_int
:std_logic_vector(7 downto 0);
constant load_value:std_logic_vector(7 downto 0):=X”80”;
constant high_value
:integer:=220;
begin
process(clk,preset)
begin
if (preset=‘1’) then
count_int<=load_value;
elsif rising_edge(clk) then
if (conv_integer(count_int)=high_value) then
count_int<=X”00”;
else
count_int<=count_int+’1’;
end if;
end if;
end process;
count<=count_int;
end behave;
* korzystanie z funkcji
konwersji conv_integer
oraz przeciążonego
operatora dodawania
wymaga użycia pakietu
std_logic_unsigned
* dodawanie wektorów:
count_int + ”00000001”
dzielniki częstotliwości
dziel11bit:process(clk16M)
begin
if (clk16M’event and clk16M = '1') then
counter<=unsigned(counter)- '1';
if (unsigned(counter)<=2 or unsigned(counter)=16#7FF#) then
pfs<='1';
-- 16#...# -zapis w kodzie heksalnym
else
pfs<='0';
end if;
if (unsigned(counter)<16#C# or unsigned(counter)>=16#7FE#) then
syp<='1';
else
syp<='0';
end if;
end if;
end process;
clk4M<=counter(1);
clk2M<=counter(2);
* clk16M-zegar wzorcowy-port wejściowy, clk4M, clk2M, pfs, syp -porty wyjściowe
counter -sygnał wewnętrzny; wszystkie sygnały typu std_logic
początek ramki
* w procesie, po warunku testującym zbocze zegara, następuje
opis logiki kombinacyjnej sterującej wejścia D przerzutników
TUTAJ: opis ten dotyczy następnego stanu przerzutników counter,
pfs, syp (po reakcji układu na wykryte zbocze zegara), i
uzależniony jest od bieżącego stanu przerzutników counter (przed
reakcją na zbocze)
* należy pamiętać, że sposób opisu układu zależy od wyboru
“nośnika informacji”: sygnał czy zmienna
* bity licznika są równocześnie wyjściami podzielnika
częstotliwości
rejestry przesuwne
* syntezowane jako łańcuch przerzutników
process(clk,reset)
begin
if (reset=‘1’) then
a<=‘0’; b<=‘0’; c<=‘0’; shift_out<=‘0’;
elsif (clk’event and clk=‘1’) then
a<=shift_in; b<=a; c<=b; shift_out<=c;
end if;
end process;
* operacja na typie signal data_in, shiftreg :std_logic_vector(7 downto 0);
process(clk)
begin
if (clk’event and clk=‘1’) then
if (load=‘1’) then
shiftreg<=data_in;
--wpis równoległy
else
shiftreg(0)<=shift_in;
shiftreg(7 downto 1)<=shiftreg(6 downto 0);
end if;
--shiftreg(7)=shift_out jest wyjściem
end if;
end process;
entity shiftregister is
generic (size: integer:=8);
port( shift_in
:in std_logic;
data_out
:out std_logic_vector(size-1 downto 0);
clear, enable :in std_logic;
clock
:in std_logic);
end shiftregister;
architecture a1 of shiftregister is
signal reg
:std_logic_vector(size-1 downto 0);
begin
process
--brak listy czułości!!!
begin
wait until clock=‘1’;
if (clear=‘1’) then
--zerowanie synchroniczne!!!
reg<=conv_std_logic_vector(0,size);
--use std_logic_arith
elsif (enable=‘1’) then
reg(0)<=shift_in; reg(size-1 downto 1)<=reg(size-2 downto 0);
end if;
--reg(size-1 downto 0)<=reg(size-2 downto 0) & shift_in;
end process;
data_out<=reg;
end a1;
process
--brak listy czułości!!!
begin
wait until clock=‘1’;
if (clear=‘1’) then
L1: for i in size-1 downto 0 loop
reg(i)<=‘0’;
end loop L1;
elsif (enable=‘1’) then
reg(0)<=shift_in;
L2: for j in size-1 downto 1 loop
reg(j)<=reg(j-1);
end loop L2;
end if;
end process;
entity shifter is
port(
data :inout std_logic_vector(7
downto 0);
read
:in std_logic;
sl,sr
:in std_logic;
clock,reset
:in std_logic;
mode:in std_logic_vector(1 downto
0));
end shifter;
architecture a1 of shifter is
signal qout:std_logic_vector(7
downto 0);
begin
process(clock)
begin
if (clock’event and clock=‘1’) then
if (reset=‘1’) then
qout<=“00000000”;
else
case mode is
when “01”=>qout<=sr&qout(7
downto 1);
when “10”=>qout<=qout(6
downto 0)&sl;
when “11”=>qout<=data;
when others=>null;
end case;
end if;
end if;
end process;
data<=qout when read=‘1’
else “ZZZZZZZZ”;
end a1;
* mode=01 -przesunięcie w
prawo
* mode=10 -przesunięcie w
lewo
* mode=11 -równoległy wpis
nowych danych z magistrali
* mode=00 -podtrzymanie
poprzednich wartości rejestru
przesuwnego
* read=1 -zawartość rejestru
przesuwnego wystawiana na
magistralę
automaty/maszyny stanów (FSM -Finite State Machine)
* złożony układ cyfrowy, który na podstawie własnego stanu
bieżącego oraz stanu wejść przechodzi do stanu następnego;
przejście to jest reakcją na zbocze zegara
* automat jawny => liczba stanów jest znana, każdy stan jest
nazwany, rejestry przechowują zakodowaną reprezentację stanu
* opis automatów zawiera sekcję kombinacyjną i sekcję
synchroniczną
* automat Moore’a: wyjścia automatu są jedynie funkcją stanu,
logika kombinacyjna służy do generowania sygnałów wyjściowych
na podstawie stanu automatu, oraz do określenia wejść
przerzutników na podstawie stanu automatu i wejść do układu
* automat Mealy’ego: zarówno następny stan jak i wektor wyjść są
funkcjami bieżącego stanu automatu oraz wektora wejść; automat
Mealy’ego synchroniczny wyposażony jest w przerzutniki
wyjściowe, automat asynchroniczny natychmiast reaguje na
zmianę sygnałów wejściowych
* kodowanie stanów:
-użycie typu wyliczeniowego
type state is (st0, st1, st2, st3,
st4);
signal present_state, next_state: state;
-użycie stałych
subtype state is
std_logic_vector(3 downto 0);
constant st0: state:=“0010”;
constant st1: state:=“1000”;
Current Next Output
state state (Z)
X=0 X=1
S0 S0 S2 0
S1 S0 S2 1
S2 S2 S3 1
S3 S3 S1 0
entity MOORE is
port (X, clock :in bit;
Z
:out bit);
end MOORE;
architecture b1 of MOORE is
type STATE_TYPE is (S0,S1,S2,S3);
signal CURRENT, NEXTS: STATE_TYPE;
begin
SYNCH:process(clock)
begin
if (clock’event and clock=‘1’) then
CURRENT<=NEXTS;
end if;
end process SYNCH;
COMB:process(CURRENT,X)
begin
case CURRENT is
when S0=> Z<=‘0’;
if (X=‘0’) then NEXTS<=S0;
else NEXTS<=S2;
end if;
when S1=> Z<=‘1’;
if (X=‘0’) then NEXTS<=S0;
else NEXTS<=S2;
end if;
when S2=> Z<=‘1’;
if (X=‘0’) then NEXTS<=S2;
else NEXTS<=S3;
end if;
when S3=> Z<=‘0’;
if (X=‘0’) then NEXTS<=S3;
else NEXTS<=S1;
end if;
end case;
end process COMB;
end b1;
* przypisania do sygnału Z
utworzą po syntezie logikę
kombinacyjną zależną od wyjść
przerzutników przechowujących
aktualny stan automatu
* przypisania do sygnału NEXTS
opisują logikę kombinacyjną
przygotowującą wejścia dla
przerzutników przechowujących
stan: następny stan (NEXTS) jest
funkcją aktualnego stanu
(CURRENT) i wejścia
* proces SYNCH opisuje zespół
przerzutników o wektorze
wejściowym NEXTS i
wyjściowym CURRENT
* liczba przerzutników jest
zależna od kodowania stanów; o
sposobie kodowania decyduje
narzędzie syntezy (sekwencyjne,
gray, one-hot, one-cold)
architecture b2 of MOORE is
type STATE_TYPE is (S0,S1,S2,S3);
signal CURRENT: STATE_TYPE;
begin
SYNCH:process(clock)
begin
if (clock’event and clock=‘1’) then
case CURRENT is
when S0=> if (X=‘1’) then CURRENT<=S2;
end if;
when S1=> if (X=‘0’) then CURRENT<=S0;
else CURRENT<=S2;
end if;
when S2=> if (X=‘1’) then CURRENT<=S3;
end if;
when S3=> if (X=‘1’) then CURRENT<=S1;
end if;
end case;
end if;
end process SYNCH;
COMB:process(CURRENT)
begin
case CURRENT is
when S0=> Z<=‘0’;
when S1=> Z<=‘1’;
when S2=> Z<=‘1’;
when S3=> Z<=‘0’;
end case;
end process COMB;
end b2;
* proces SYNCH opisuje
tylko przejścia między
stanami, jego rezultatem
jest zespół przerzutników
wraz z logiką sterującą ich
wejściami
* proces COMB opisuje
logikę kombinacyjną
sterującą wyjściem
automatu
architecture b3 of MOORE is
type STATE_TYPE is (S0,S1,S2,S3);
signal CURRENT: STATE_TYPE;
begin
SYNCH:process(clock)
begin
if (clock’event and clock=‘1’) then
case CURRENT is
when S0=> if (X=‘1’) then CURRENT<=S2; Z<=‘1’;
end if;
when S1=> if (X=‘0’) then CURRENT<=S0; Z<=‘0’;
else CURRENT<=S2; Z<=‘1’;
end if;
when S2=> if (X=‘1’) then CURRENT<=S3; Z<=‘0’;
end if;
when S3=> if (X=‘1’) then CURRENT<=S1; Z<=‘1’;
end if;
end case;
end if;
end process SYNCH;
end b3;
* wyjście Z jest synchronizowane
zegarem, zrealizowane za pomocą
logiki kombinacyjnej i przerzutnika
* cel zmiany: likwidacja stanów
nieustalonych (w poprzednich
rozwiązaniach możliwość powstania
szpilek przy zmianie stanu
automatu), uniezależnienie od
sposobu kodowania stanów
Current Next Output
state state (Z)
X=0 X=1 X=0 X=1
S0 S0 S2 0 1
S1 S0 S2 0 0
S2 S2 S3 1 0
S3 S3 S1 0 1
entity MEALY is
port (X, clock, reset :in bit;
Z :out bit); end MEALY;
architecture m1 of MEALY is
type STATE is array(3 downto 0) of bit;
signal CURRENT, NEXTS: STATE;
-- one hot encoding
constant S0: STATE:=“0001”;
constant S1: STATE:=“0010”;
constant S2: STATE:=“0100”;
constant S3: STATE:=“1000”;
signal Zint:bit;
begin
--Z<=Zint; --autom. Mealy’ego asynchron.
SYNCH:process(clock,reset)
begin
if (reset=‘1’) then CURRENT<=S0;Z<=‘0’;
elsif (clock’event and clock=‘1’) then
CURRENT<=NEXTS;
Z<=Zint; --autom. Mealy’ego synchr.
end if;
end process SYNCH;
COMB:process(CURRENT,X)
begin
case CURRENT is
when S0=> if (X=‘0’) then NEXTS<=S0; Zint<=‘0’;
else NEXTS<=S2; Zint<=‘1’;
end if;
when S1=> if (X=‘0’) then NEXTS<=S0; Zint<=‘0’;
else NEXTS<=S2; Zint<=‘0’;
end if;
when S2=> if (X=‘0’) then NEXTS<=S2; Zint<=‘1’;
else NEXTS<=S3; Zint<=‘0’;
end if;
when others=> if (X=‘0’) then NEXTS<=S3; Zint<=‘0’;
--
others obejmuje S3
else NEXTS<=S1; Zint<=‘1’;
--i
stany nielegalne
end if;
end case;
end process COMB;
end m1;
* zalecanym sposobem opisu automatów jest rozdzielenie
części kombinacyjnej i rejestrowej
MAGISTRALE
entity mag3st is port(
sel
:in std_logic_vector(1 downto 0);
nrd
:in std_logic;
data_out
:out std_logic_vector(7 downto 0);
data0,data1 :in std_logic_vector(3 downto 0);
data2
:in std_logic_vector(6 downto 0);
data3
:in std_logic );
end mag3st;
architecture a1 of mag3st is
begin
data_out <= data0 & "0000" when (nrd='0' and sel="00")
else "ZZZZZZZZ";
data_out <= "0000" & data1 when (nrd='0' and sel="01")
else "ZZZZZZZZ";
data_out <= '0' & data2 when (nrd='0' and sel="10")
else "ZZZZZZZZ";
data_out <= "0000000" & data3 when (nrd='0' and sel="11")
else "ZZZZZZZZ";
end a1;
* w architekturze opisano zestaw 4-ch bram trójstanowych
* linie trójstanowe są jedynym przypadkiem kodu syntezowalnego,
kiedy sygnał może mieć kilka sterowników
* w układach PLD realizacja przy pomocy multipleksera i jednej
bramy trójstanowej
* typowy opis z multiplekserem:
architecture a2 of mag3st is
begin
data_out <= (data0 & "0000”) when (nrd='0' and sel="00")
else ("0000" & data1) when (nrd='0' and sel="01")
else ('0' & data2) when (nrd='0' and sel="10")
else ("0000000" & data3) when (nrd='0' and
sel="11")
else "ZZZZZZZZ";
end a2;
* Altera Max+PlusII -preferowany jest opis, w którym najwyższy
priorytet ma stan wysokiej impedancji
architecture a3 of mag3st is
begin
data_out <= “ZZZZZZZZ” when nrd=‘1’
else (data0 & "0000”) when sel="00"
else ("0000" & data1) when sel="01"
else ('0' & data2) when sel="10"
else ("0000000" & data3);
end a3;
efektywne używanie przerzutników
entity and_or_xor is
port( clk,reset
:in std_logic;
and_bits,or_bits,xor_bits
:out std_logic);
end and_or_xor;
architecture rtl1 of and_or_xor is
begin
process
variable cnt: std_logic_vector(2 downto 0);
begin
wait until clk=‘1’; --wszystkie linie po tym wyrażeniu syntezowane są jako
if (reset=‘1’) then
--przerzutniki !!!
cnt := “000”;
else
cnt := cnt+”001”;
end if;
and_bits<=cnt(2) and cnt(1) and cnt(0);
--uwaga!!! cnt jest zmienną, do operacji
or_bits<=cnt(2) or cnt(1) or cnt(0);
--brane są nowe wartości bitów
xor_bits<=cnt(2) xor cnt(1) xor cnt(0);
--wyznaczone w danym przebiegu procesu
end process;
end rtl1;
architecture rtl2 of and_or_xor is
signal cnt:std_logic_vector(2 downto 0);
begin
REG:process
begin
wait until clk=‘1’;
if (reset=‘1’) then
cnt<=“000”;
else
cnt<=cnt+”001”;
end if;
end process;
COMB:process(cnt)
begin
and_bits<=cnt(2) and cnt(1) and cnt(0);
or_bits<=cnt(2) or cnt(1) or cnt(0);
xor_bits<=cnt(2) xor cnt(1) xor cnt(0);
end process;
end rtl2;
* niezależne procesy: zegarowany i
czysto kombinacyjny
* możliwość powstawania “szpilek”
na wyjściach, na zboczach sygnału
zegarowego clk
* poniższy przykład realizuje inną
funkcję:
architecture rtl3 of and_or_xor is
signal cnt: std_logic_vector(2
downto 0);
begin
process
begin
wait until clk=‘1’;
if (reset=‘1’) then
cnt <= “000”;
else
cnt <= cnt+”001”;
end if;
and_bits<=cnt(2) and cnt(1) and
cnt(0);
or_bits<=cnt(2) or cnt(1) or cnt(0);
xor_bits<=cnt(2) xor cnt(1) xor
cnt(0);
end process; --do operacji
logicznych brana
end rtl3;
--poprzednia
wartość licznika cnt
rtl1
rtl2
rtl3
* w architekturze rtl3 wartości wyjść opóźnione są o jeden takt
zegara w stosunku do stanu licznika, ze względu na zastosowanie
sygnału cnt w miejsce zmiennej
* CPLD/FPGA zawierają przerzutnik w każdej komórce;
bezpieczniej jest projektować układy w pełni synchroniczne
JAKICH OPERACJI NIE NALEŻY WYKONYWAĆ ?
X<= A nand (not(not(not A)));
* syntezer nie wygeneruje odwracającej linii opóźniającej i bramki
nand, ALE potraktuje zapis jako opis układu X<=A nand (not A);
tzn. X<=‘1’;
entity COMP is port(
a,b : in INTEGER range 0 to 7;
z : out INTEGER range 0 to 7 );
end COMP;
architecture a1 of COMP is
begin
process(a,b)
begin
if (a=b) then
z<=a;
end if;
end process;
end a1;
* opis funkcjonalny jest prawidłowy,
ALE układ nie będzie działać
* powód: opóźnienie na bloku
komparatora, generacja przez układ
kombinacyjny szpilek “niszczących”
prawidłową zawartość zatrzasku
TYPY OBIEKTÓW W VHDL -PODSUMOWANIE
* typy skalarne
-typ całkowity -definiowany do wykonywania działań
matematycznych, w standardzie zapisywany na 32 bitach: od -2
31
do
+(2
31
-1)
przykłady literałów całkowitych o podstawie 10: 0, 100, 1e6,
-700000, 1_945_690
binarnych: 2#1111# , 2#1110_1111#,
2#1111#e2 (60)
ósemkowych: 8#1234567#
heksalnych: 16#F0# , 16#1#E2 (256)
prawidłowe są przypisania do zmiennych lub sygnałów: x:=1; y<=-
7; d:=1e4
przypisanie nieprawidłowe:
z<=10.0;
-typ rzeczywisty - zakres zdefiniowany przez standard: -1.0e38 do
1.0e38
przykłady literałów rzeczywistych: 100.0, -1.765, 2.234_567_78,
2.71e-9
binarnych: 2#1.101#e3 (13.0), 2#11.00_01_11#
heksalnych: 16#F.F#e1 (255.0)
prawidłowe przypisania wartości:
x:=2.34; y<=3.14159;
z<=1.9e21;
przypisania nieprawidłowe:
x:=2; y<=-1;
-typ wyliczeniowy: definiujemy go poprzez wymienienie wszystkich
elementów, nazwa elementów jest dowolna
type meal is (breakfast, dinner, lunch, midnight_snack);
W systemie każdy z elementów typu wyliczeniowego otrzymuje
wewnętrzny identyfikator w postaci liczby całkowitej począwszy od
0 dla skrajnego lewego elementu. Wyjątek: kodowanie stanów one
hot lub one cold.
-typ fizyczny -niesyntezowalny
predefiniowanym w standardzie typem fizycznym jest TIME,
jednostką bazową jest fs (można używać jednostek pochodnych ps,
ns, us, ms, s)
przykład użycia:
t:=5 ns;
przykład nowej definicji
type weight is range 0 to 1e15
units
ug;
mg=1000 ug;
g=1000 mg;
kg=1000 g;
t=1000 kg;
end units;
* typy złożone
-typy tablicowe: grupują obiekty tego samego typu (dowolnego) w
jeden obiekt, w ogólności tablice mogą być wielowymiarowe
wymiar tablicy: typu całkowitego lub wyliczeniowego, indeks
rosnący (to) bądź malejący (downto)
typ tablicowy ograniczony: dokładnie podany jest zakres indeksów
typ nieograniczony: zakres nie jest podany, można definiować wiele
podtypów na bazie jednej definicji typu
type tab1 is array (0 to 7) of integer;
type tab2dim is array (0 to 7, 15 downto 0) of std_logic;
type std_logic_vector is array (NATURAL range<>) of std_logic;
-użyty zakres musi być podany w oddzielnej definicji
dostęp do tablicy poprzez indeks elementu: y<=x(6);
możliwe przypisanie pełnej tablicy przy pomocy agregatu
syntezowalne są wyłącznie tablice jednowymiarowe
-rekordy: grupują elementy różnych typów, mogą zawierać
wewnątrz tablice i inne rekordy; do elementu rekordu
odwołujemy się poprzez nazwę pola
type rec_type is record
val_int :integer;
val_log:std_logic;
end record;
signal x: rec_type;
.........
x.val_int<=1;
x.val_log<=‘0’;
możliwe przypisanie przy pomocy agregatu: x<=(1,’0’);
x<=(val_log=>’0’, val_int=>
1);
*typ wskaźnikowy
-niesyntezowalny!!!, przydatny przy modelowaniu kolejek,
ograniczony wyłącznie do zmiennych
type address is access memory;
* typ plikowy
typ niesyntezowalny, przydatny przy symulacji (wypełnianie
zawartości tablic, wektory testowe)
definicja typu:
type type_name is file of type;
np: type TEST_VEC_FILE is file of STD_LOGIC_VECTOR(15
downto 0);
automatycznie zdefiniowane są procedury: FILE_OPEN,
FILE_CLOSE, READ, WRITE, ENDFILE
elementy pliku nie mogą być typu wskaźnikowego, w przypadku
tablic- tylko jednowymiarowymi
PODTYPY
* użytkownik definiuje ograniczenie typu bazowego
subtype INTXX is integer range 0 to 1023; (liczba
reprezentowana na 10-ciu bitach)
subtype SLV8 is std_logic_vector(7 downto 0);
ALIAS
*nadanie nowej nazwy innemu obiektowi lub części innego obiektu-
tutaj 8 najstarszych
variable instr:bit_vector(31 downto 0);
bitów
alias op_code:bit_vector(7 downto 0) is instr(31 downto 24);
TYPY PREDEFINIOWANE -w pakietach STANDARD oraz
TEXTIO
* BOOLEAN -wyliczeniowy, dwie wartości false i true (false<true)
* BIT -wyliczeniowy, dwie wartości ‘0’ i ‘1’
* CHARACTER -wyliczeniowy, zawiera zbiór znaków ASCII, znaki w
pojedynczym cudzysłowiu (‘a’, ‘Y’, ‘x’)
* INTEGER -liczby całkowite
* NATURAL -zbiór liczb nieujemnych, podtyp INTEGER
* POSITIVE -zbiór liczb dodatnich, podtyp INTEGER
* BIT_VECTOR -tablica wartości typu BIT
łańcuchy bitów: “1110”, B”1111_1110”, X”FC0”, O”7677”
* STRING -tablica wartości typu CHARACTER
łańcuchy znaków: “ala ma kota”, “symulacja”
* REAL -zbiór liczb rzeczywistych
* TIME -typ fizyczny reprezentujący czas symulacji
TYPY PAKIETU STD_LOGIC_1164
* STD_ULOGIC, STD_LOGIC (z dodatkową funkcją arbitrażową)
type STD_ULOGIC is (‘U’, ‘X’, ‘0’, ‘1’, ‘Z’, ‘W’, ‘L’, ‘H’, ‘-’);
* STD_ULOGIC_VECTOR, STD_LOGIC_VECTOR -typy tablicowe
nieogr.
ATRYBUTY
* atrybut jest dodatkową informacją związaną z obiektem
* przykłady predefiniowanych atrybutów dla sygnałów:
clk’event, clk’stable
* przykłady atrybutów predefiniowanych dla typów tablicowych
oraz sygnałów i zmiennych typu tablicowego:
type ADDRESS_TYPE is std_logic_vector(31 downto 0);
signal address : ADDRESS_TYPE;
address’left
->31 -zwraca wartość lewego indeksu
address’right ->0
-zwraca wartość prawego indeksu
address’low ->0
-zwraca wartość najmniejszego indeksu
address’high ->31 -zwraca wartość największego indeksu
address’range
->zakres 0 do 31
-zwraca zakres zmian
indeksu
ADDRESS_TYPE’ascending - >false -zwraca true lub false w
zależności od kierunku zmian indeksów
* użytkownik może definiować własne atrybuty, które są stałymi