Pokročilé využití jazyka VHDL Pavel Lafata
Autor: Pavel Lafata Název díla: Pokročilé využití jazyka VHDL Zpracoval(a): České vysoké učení technické v Praze Fakulta elektrotechnická Kontaktní adresa: Technická 2, Praha 6
Inovace předmětů a studijních materiálů pro e-learningovou výuku v prezenční a kombinované formě studia
Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti
VYSVĚTLIVKY
Definice
Zajímavost
Poznámka
Příklad
Shrnutí
Výhody
Nevýhody
ANOTACE Tento modul seznamuje studenty s jazykem VHDL a některými jeho pokročilými funkcemi. V modulu je proveden návrh synchronního čítače pomocí jazyka VHDL a jsou zde diskutovány jeho další možné modifikace.
CÍLE Díky tomuto modulu získají studenti základní znalost jazyka VHDL, zejména postup při návrhu a realizaci konkrétního zadání, jeho syntaxi a další vlastnosti. Pro ukázku je proveden komplexní návrh a realizace synchronního čítače včetně rozboru jednotlivých kroků a jejich možných modifikací. Výsledný návrh čítače je podroben simulaci pro ověření správnosti jeho funkce.
LITERATURA [1]
HAVLAN, Martin. Konstrukce telekomunikačních zařízení: cvičení. Praha : ČVUT, 2004. 199 s. ISBN 80-01-03035-0.
[2]
PINKER, Jiří; POUPA, Martin. Číslicové systémy a jazyk VHDL. Praha : BEN technická literatura, 2006. 349 s. ISBN 80-7300-198-5.
[3]
DOUŠA, Jiří. Jazyk VHDL. Praha : ČVUT, 2003. 76 s. ISBN 80-01-02670-1.
[4]
KRÁL, Jiří. Řešené příklady ve VHDL: hradlová pole FPGA pro začátečníky. Praha : BEN - technická literatura, 2010. 127 s. ISBN 978-80-7300-257-2.
Obsah 1 Jazyk VHDL ......................................................................................................................... 6 1.1
Princip jazyka VHDL ................................................................................................. 6
1.2
Struktura VHDL kódu ................................................................................................ 7
1.3
Datové typy a proměnné (1/3) .................................................................................... 8
1.4
Datové typy a proměnné (2/3) .................................................................................... 9
1.5
Datové typy a proměnné (3/3) .................................................................................. 10
1.6
Základní bloky VHDL kódu, entita, architektura, komponenta ............................... 11
1.7
Zadání projektu a charakteristika čítače (1/2) .......................................................... 12
1.8
Zadání projektu a charakteristika čítače (2/2) .......................................................... 13
1.9
Realizace základního 4bitového synchronního čítače (1/5) ..................................... 14
1.10
Realizace základního 4bitového synchronního čítače (2/5) ..................................... 15
1.11
Realizace základního 4bitového synchronního čítače (3/5) ..................................... 16
1.12
Realizace základního 4bitového synchronního čítače (4/5) ..................................... 17
1.13
Realizace základního 4bitového synchronního čítače (5/5) ..................................... 18
1.14
Ověření funkčnosti čítače (1/2) ................................................................................ 19
1.15
Ověření funkčnosti čítače (2/2) ................................................................................ 20
1.16
Synchronní a asynchronní resetování čítače (1/3) .................................................... 21
1.17
Synchronní a asynchronní resetování čítače (2/3) .................................................... 22
1.18
Synchronní a asynchronní resetování čítače (3/3) .................................................... 23
1.19
Simulace synchronního a asynchronního způsobu resetování čítače ....................... 24
1.20
Možnost zastavení a spuštění procesu čítání (1/2) ................................................... 25
1.21
Možnost zastavení a spuštění procesu čítání (2/2) ................................................... 27
1.22
Čítač se změnou směru čítání (1/2) .......................................................................... 28
1.23
Čítač se změnou směru čítání (2/2) .......................................................................... 29
1.24
Shrnutí realizace čítače pomocí jazyka VHDL ........................................................ 30
1 Jazyk VHDL 1.1 Princip jazyka VHDL Jazyk VHDL původně vznikl primárně pro potřeby popisu a simulace rozsáhlých návrhů číslicových systémů v 80. letech 20. století [2]. Jeho název byl postupně odvozen ze slovního spojení VHSIC Hardware Description Language, kde další zkratka VHSIC představuje Very-High-Speed Integrated Circuits. V překladu celý název tedy znamená jazyk pro popis velmi rychlých integrovaných obvodů [2]. První varianta jazyka VHDL byla publikována v roce 1985 a na jejím vývoji se podílely firmy IBM, Texas Instruments a Intermetrics. Od té doby bylo publikováno mnoho standardů tohoto jazyka, kde poslední je z roku 2009 a nese označení IEEE 1076-2008. Jelikož je VHDL jazyk určen právě pro popis hardwaru a nepatří do skupiny standardních programovacích jazyků, platí zde určitá omezení daná jeho těsnou vazbou na použitý typ programovatelného logického pole [1]. Obecně lze o jazyce VHDL říci, že ačkoliv je univerzálním a obecným nástrojem pro libovolná hradlová pole FPGA či CPLD, mohou se při jeho využití pro konkrétní programovatelná pole uplatnit různá omezení, daná právě vlastnostmi a schopnostmi použitého hardwaru. Při vytváření programu pomocí jazyka VHDL či při popisu navrhovaného logického obvodu je nutné předem uvažovat, že výsledný program je vždy svázán s jeho hardwarovou realizací. VHDL jazyk se také využívá k simulaci jednotlivých obvodů a k psaní testovacích programů [3]. Testovací program (testbench) slouží ke generování vstupních signálů a k vyhodnocení výstupních signálů. Kód testovacího programu není třeba syntetizovat, protože se používá pouze pro simulaci.
1.2 Struktura VHDL kódu Při návrhu číslicových obvodů, stejně jako při řešení jiných komplexních problémů, je vhodné provést dekompozici dané úlohy na menší, snadněji řešitelné celky [1]. Modularita a rozdělení větších celků na menší elementární části, kdy každá vykonává jednu základní funkci a dohromady pak všechny tvoří celkový výsledný program, je jednou z klíčových vlastností jazyka VHDL. Celek se ve VHDL nazývá modul, někdy též komponenta, vstupy a výstup, které tvoří jeho rozhraní, se označují jako porty. Modul může být příklad popisu struktury obvodu, složeného z dalších modulů, které jsou propojeny navzájem pomocí signálů [2],[3]. Signálem je v prostředí jazyka VHDL myšlen propoj mezi různými bloky (částmi) výsledného programu. Signál může propojovat jednotlivé moduly, komponenty, nebo připojovat globální vstup či výstup s daným modulem.
Signál se ve slangu VHDL označuje též jako „drát“ či „vodič“, protože v podstatě nahrazuje funkci propojovacího prvku například v nepájivém kontaktním poli, na desce plošných spojů aj. Mimo signál existuje v prostředí VHDL ještě druhý prvek, který lze použít pro dočasné (i trvalé) ukládání mezivýsledků a jeho prostřednictvím předávat informace mezi jednotlivými bloky [3], a tím je proměnná (variable nebo shared variable). Zatímco signál reprezentuje fyzický propoj, proměnná v abstraktním pojetí představuje pouze určité místo v paměti, na které lze ukládat danou hodnotu, nebo ji odtud v jiné části VHDL programu číst.
7
1.3 Datové typy a proměnné (1/3) Dalším podstatným rozdílem mezi signálem a proměnnou v prostředí jazyka VHDL je i ve způsobu jejich vytváření, zápisu a čtení [2], [4]. Zatímco signál je vždy globálním prvkem, který je definován v rámci celé architektury a v dané architektuře na něho lze kdykoliv a kdekoliv přistupovat, je standardní proměnná (variable) deklarována jen v rámci daného procesu. Sdílená proměnná (shared variable) je pak rovněž dostupná z jakékoliv časti architektury programu, při jejím použití však mohou vzniknout komplikace a chyby v případě zápisu její hodnoty v rámci různých procesů a nedodržení časové konzistence programu [2]. Signály i proměnné lze v jazyce VHDL slučovat a tvořit sběrnicové struktury [4]. Ty mohou být s výhodou uplatněny zejména v situacích, kdy je potřeba propojit navzájem dvojici bloků pomocí více signálu a tyto signály přitom přenášejí stejný typ (druh) informace. Sběrnice se ve VHDL označuje jako vektor (vector) a usnadňuje práci s větším množstvím signálů, kdy není potřeba provádět danou operaci s každým signálem (proměnnou) zvlášť, ale pomocí jejich seskupení do sběrnice lze provést požadovaný úkon s celou sběrnicovou strukturou najednou. Sběrnice se též obvykle využívají, pokud je vstup či výstup daného modulu tvořen větším počtem logických signálů (typicky čítače, multiplexory, registry).
8
1.4 Datové typy a proměnné (2/3) Při deklaraci signálu, proměnné, či sběrnice (ze signálů či proměnných) je možné do nich vložit přímo již počáteční hodnotu a rovněž definovat jejich datový typ. Následující tabulka představuje několik vybraných nejčastějších datových typů, které se v jazyce VHDL objevují [3]. Příklad často využívaných datových typů v jazyce VHDL.
Název datového typu
VHDL zápis
Příklad
Celočíselný datový typ
integer
0, -1, 10
Standardní logický typ
std_logic
'0', '1', 'X'
Bitový typ
bit
'0', '1'
Pravdivostní typ
boolean
false, true
Typ řetězec
string
"Dobry den"
Pro názornost si uveďme příklady deklarace několika ukázek společně s definováním různých datových typů. signal test : std_logic :='1';
Takto jsme definovali signál s názvem test, který je standardního logického typu s hodnotou logická jednička. signal pokus : integer :=1;
Tímto způsobem jsme opět deklarovali signál, který je tentokrát celočíselného typu a jeho hodnota je jedna. V jazyce VHDL je potřeba důsledně rozlišovat, zda se jedná o logický datový typ obsahující logické hodnoty, či jde o číselný typ, kdy daná hodnota odpovídá matematickému pojetí čísla. V prostředí návrhového studia ISE Xilinx se logické hodnoty označují '0', '1', (případně "0", "1") zatímco číselné hodnoty pak 0, 1. Zatímco pro číselné datové typy jsou definovány všechny běžné matematické operace, lze tytéž operace provádět s logickými hodnotami jen v omezené míře. Naopak, logické hodnoty a logické datové typy jsou nezbytné v případě logických operací s danými signály či proměnnými.
9
1.5 Datové typy a proměnné (3/3) V případě zápisu hodnoty do již vytvořeného signálu je potřeba místo znaku := použít <=. Uveďme si jednoduchý příklad. signal test : std_logic :='1';
Při deklaraci signálu s názvem test byla do něho vložena hodnota logické jedničky. test <='0';
Tímto způsobem jsme v těle programu do signálu test vložili hodnotu logické nuly. V případě proměnných je využit vždy znak : pro vložení hodnoty, a to jak v okamžiku její deklarace, tak i v případě zapsání nové hodnoty. variable pokus : integer :=10; pokus :=11;
V prvním kroku jsme vytvořili proměnnou s názvem pokus celočíselného typu a vložili do ní hodnotu 10, v druhém kroku jsme pak její hodnotu změnili na 11. Na závěr uveďme ještě ukázku sběrnice (vektoru). signal sbernice : std_logic_vector (1 downto 0) :="10";
Při deklarace sběrnicového typu (vektoru) je potřeba definovat jeho velikost. V jazyce VHDL je obvyklé provádět číslování v sestupném směru a začínat číslování nejnižšího prvku od nuly, sběrnici o velikosti 2 proto očíslujeme obvykle 1, 0 [2]. Pokud bychom požadovali číslování provést v opačném pořadí, tedy 0, 1, realizovali bychom tuto jednoduchou úpravu následně. signal sbernice : std_logic_vector (0 to 1) :="10";
Nejnižší bit sběrnice na nejnižším řádovém místě se obvykle označuje jako LSB (Least Significant Bit), zatímco bit na nejvyšší řádové pozici se nazývá MSB (Most Significant Bit).
10
1.6 Základní bloky VHDL kódu, entita, architektura, komponenta Entita je jednou z hlavních návrhových jednotek a slouží k popisu určitého objektu. Objektem může být celý obvod nebo jen malý logický člen. Každý objekt má určité vstupy a výstupy, které jsou definované v entitě pomocí klíčového slova port [2], [3]. V definici je na prvním místě název portu a dále je třeba definovat režim přenosu dat (vstup/výstup/obousměrně) a typ dat, které lze daným portem přenášet. Obecnou deklaraci entity můžeme zapsat například takto. entity nazev_entity is [port ({nazev_portu : rezim_prenosu_dat typ_dat [:= hodnota];});] end [nazev_entity];
Architektura je další z hlavních jednotek ve VHDL kódu. Architektura slouží k popisu funkce (chování) daného obvodu (entity). Pro popis obvodu můžeme využít dva způsoby. Prvním je zápis v paralelním prostředí a druhý je zápis v sekvenčním prostředí. Funkce paralelního prostředí je specifická tím, že se zde všechny příkazy provádějí najednou, lépe řečeno na základě předpisu je vytvořeno pevné propojení jednotlivých vodičů a logických členů. Sekvenční prostředí zajišťuje provedení jednotlivých příkazů postupně v pořadí jejich zápisu a na základě změny definovaného vstupního (řídícího) signálu, obvykle se jedná o hodinový signál [4]. Obecně lze architekturu definovat následujícím způsobem. architecture nazev_architektury of nazev_entity is begin … end [nazev_architektury];
Komponenta také patří k základním blokům, které se ve VHDL běžně využívají. Účelem komponenty je možnost vytvoření hierarchické struktury za použití jednotlivých entit jako komponent v dalších entitách. Lze tedy říci, že jednu entitu můžeme využít několikrát pomocí komponenty a následně komponenty mezi sebou propojit. Obecný zápis komponenty může být například následující. component nazev_komponenty [port ({nazev_portu : rezim_prenosu_dat typ_dat [:= hodnota];});] end component [nazev_komponenty];
11
1.7 Zadání projektu a charakteristika čítače (1/2) Naším úkolem bude realizovat synchronní 4bitový čítač, u něhož se bude čítání spouštět vždy na vzestupnou hranu hodinového (taktovacího) signálu. Postupně budeme do návrhu čítače doplňovat různé modifikace a dodatečné funkce. Jak již z názvu vyplývá, primárním úkolem čítače je čítání (postupná inkrementace) předem definovaného stavu dané vstupní veličiny (pulzů), např. obvykle tedy čítání vzestupných hran hodinového pulzu, ale i libovolného jiného signálu, nebo naopak čítání sestupných hran (případně obou). Pro realizaci základních čítačů se využívají zejména klopné obvody (typu D, typu JK), které mohou být navíc doplněny např. základními logickými hradly (NAND). V základním přiblížení můžeme rozdělit čítače na synchronní a asynchronní, čítače čítající vpřed, vzad či obousměrně a také je můžeme rozlišit podle kódu použitého pro čítání.
12
1.8 Zadání projektu a charakteristika čítače (2/2) Jedním z nejdůležitějších kritérií charakterizujících čítač je způsob distribuce hodinového signálu pro překlápění jednotlivých klopných obvodů. Z tohoto pohledu rozlišujeme čítače synchronní, kdy je hodinový signál veden do všech klopných obvodů čítače, a asynchronní, kdy je na základě hodinového signálu překlápěn pouze první klopný obvod, zatímco hodinové vstupy všech dalších členů jsou vždy připojeny na výstup předchozího klopného obvodu v kaskádě. Zatímco v případě synchronního čítače dochází tedy k překlápění všech obvodů ve stejném okamžiku (v případě ideálního čítače), dochází u asynchronního čítače díky postupnému překlápění kaskádně spojených klopných obvodů ke kumulaci zpoždění v jednotlivých stupních [1]. V případě asynchronního čítače je tak nutno odečíst jeho hodnotu až v okamžiku ustálení všech klopných obvodů, což může v extrémních případech velmi dlouhých čítačů vést k celkové nestabilitě (než dojde k postupnému překlopení posledního členu, objeví se na vstupu prvního již nová vzestupná hrana hodinového signálu). Výhodou asynchronního čítače je jeho relativní jednoduchost, neboť při realizaci pomocí klopných obvodů typu D či JK již není potřeba žádných dalších prvků či hradel. Naopak nevýhodou je delší doba ustálení výstupních stavů, zmíněná možnost vzniku nestability a dalších nejistot a nebezpečných mezistavů. Z tohoto důvodu jsou již dnes aplikace asynchronních čítačů spíše výjimečné. Z principu funkce FPGA a CPLD obvodů a možností jazyka VHDL je zřejmé, že pomocí jazyka VHDL bude obvykle snazší realizovat synchronní čítač než čítač asynchronní.
13
1.9 Realizace základního 4bitového synchronního čítače (1/5) V první fázi nejprve provedeme realizaci jednoduchého 4bitové synchronního čítače, který bude čítat vzestupné hrany hodinového signálu. Neuvažujme prozatím žádné další funkce ani vlastnosti. Úvodní část každého programu v jazyce VHDL začíná vždy deklarací použitých knihoven [4]. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL;
Pro realizaci jednoduchého čítače vystačíme se standardním balíkem knihoven jazyka VHDL, jejichž výčet je uveden výše. V nápovědě k návrhovému studiu ISE lze najít seznam použitelných knihoven IEEE jazyka VHDL. Obsah jednotlivých knihoven je možné získat z příslušných doporučení IEEE, nebo knihovny jednoduše vyhledat v daném adresáři, kde je studio ISE od společnosti Xilinx nainstalováno, a otevřít je v textovém editoru. Dále je potřeba definovat základní entitu celého čítače, která bude obsahovat deklaraci globálních vstupů a výstupů čítače. Entitu pojmenujme jednoduše citac a jediným jejím vstupem bude hodinový signál, který se obvykle v jazyce VHDL označuje jako clk či clock. Výstup čítače označme vystup a vzhledem k tomu, že se jedná o 4bitový čítač, bude výstupem 4bitový vektor logických hodnot. Uvedená entita bude v jazyce VHDL vypadat následovně. entity citac is port (clk : in std_logic; vystup : out std_logic_vector (3 downto 0) :="0000"); end citac;
Při pojmenování proměnných, vstupů či výstupů v jazyce VHDL je potřeba vyhnout se používání diakritiky (háčků, čárek) a některých speciálních symbolů (?, !, * a podobně) v názvech.
14
1.10 Realizace základního 4bitového synchronního čítače (2/5) Dalším krokem je deklarace architektury, která bude popisovat chování vlastního čítače. Architekturu pojmenujme jednoduše kod_citac a její definice je vždy uvozena klíčovými výrazy architecture a end architecture s příslušnými názvy architektury a entity, kterou popisuje. V našem případě tak bude výsledek následující. architecture kod_citac of citac is … end kod_citac;
Nyní definujme pomocný signál, do kterého budeme zapisovat vlastní hodnotu (vnitřní stav) čítače a tento signál v závěru samotného VHDL programu budeme odesílat na globální výstup celého čítače. Protože se jedná o 4bitový čítač a i výstup čítače je 4bitový, deklarujeme pomocný signál citac stejným způsobem, jak ukazuje následující VHDL kód. signal citac : std_logic_vector (3 downto 0) :="0000";
Pro následující fázi využijeme další klíčové součásti jazyka VHDL a tou je proces. Proces je jednou ze základních struktur v jazyce VHDL. Navenek se proces chová jako paralelní celek, ale uvnitř procesu jsou jednotlivé příkazy vykonávány sekvenčně. Obecně lze definovat proces následovně. process [(citlivostni seznam)] -- deklarace promennych begin -- telo procesu end process;
Pokud proces obsahuje citlivostní seznam, je tento proces spuštěn při změně jakéhokoli signálu uvedeného v citlivostním seznamu. Není-li citlivostní seznam definován, je proces aktivován ihned a je neustále opakovaně vykonáván. V procesu můžeme deklarovat lokální proměnné. Vlastní tělo procesu pak obsahuje jednotlivé VHDL kódy, které jsou sekvenčně vykonávány [2].
15
1.11 Realizace základního 4bitového synchronního čítače (3/5) Vzhledem k tomu, že se návrh týká synchronního čítače, budeme proces vlastního čítání spouštět vždy v součinnosti se základním hodinovým taktem, konkrétně na jeho vzestupnou hranu. Umístíme proto hodinový vstup clk do citlivostního seznamu daného procesu. Dovnitř těla procesu definujeme vlastní podmínku pro čítání. Při každém příchodu vzestupné hrany hodinového taktu inkrementujeme stav čítače (přičteme jedničku). Nejsnáze uvedenou situaci můžeme realizovat pomocí podmínky typu if. Podmínkovou strukturu typu if můžeme definovat takto. if podminka 1 then {sekvence prikazu 1} [{elsif podminka 2 then {sekvence prikazu 2}}] [else {sekvence prikazu 3}] end if;
Podmínková struktura if umožňuje vykonat sekvenci příkazů na základě vyhodnocení pravdivosti podmínky typu boolean. Je-li podmínka podminka 1 splněna (true), provede se sekvence příkazů v sekci sekvence prikazu 1. Pokud podmínka splněna není, přejde se sekvenčně na podmínku podminka 2, opět se vyhodnotí její platnost a takto se pokračuje až na konec bloku. Podmínky se tedy vyhodnocují sekvenčně v pořadí zápisu. Na úplný závěr podmínkové struktury se obvykle stanoví sekvence příkazů, která se provede, pokud ani jedna z předchozích podmínek není pravdivá. Je rovněž možné využít navzájem vnořených podmínek typu if, kdy v prvním kroku se provede testování pravdivosti jedné podmínky a teprve pokud je platná, vyhodnocuje se pravdivost navazující vnořené podmínky. Toto lze obvykle vyřešit i spojením obou podmínek do jednoho celku.
16
1.12 Realizace základního 4bitového synchronního čítače (4/5) S využitím podmínkové konstrukce typu if, vytvoříme požadovaný čítač reagující na vzestupnou hranu hodinového taktu pomocí procesu následujícím způsobem. process (clk) begin if clk='1' and clk'event then citac <= citac + 1; end if; end process;
Proces je spouštěn vždy na vzestupnou hranu hodinového signálu, jedná se tedy o proces synchronní, neboť hodinový signál clk je obsažen v citlivostním seznamu. Při kladném vyhodnocení podmínky dojde k inkrementaci stavu čítače, což vyjadřuje jednoduchý zápis citac <= citac + 1. Povšimněme si důležité podmínky, která se nachází hned na počátku celého procesu. if clk='1' and clk'event then
První část této složené podmínky definuje, že další část procesu se bude provádět, pouze pokud bude hodinový signál ve stavu logická 1. Druhá část podmínky pak navíc specifikuje pomocí klíčového výrazu 'event, že se musí jednat o okamžik, kdy dochází ke změně stavu hodinového signálu (tzn. při vzestupné či sestupné hraně hodinového signálu). Z celé podmínky tak vyplývá, že daný proces bude spouštěn pouze při vzestupné hraně hodinového signálu. Kromě tohoto zápisu se lze setkat i s následující variantou definice sestupné či vzestupné hrany hodinového signálu. if rising_edge(clk) then if falling_edge(clk) then
Kromě podmínkové struktury typu if pro zajištění spouštění procesu na vzestupnou (sestupnou) hranu hodinového signálu lze využít rovněž klíčového výrazu wait until.
17
1.13 Realizace základního 4bitového synchronního čítače (5/5) Nyní již můžeme přistoupit k finálnímu dokončení čítače. V pomocném signálu s názvem citac je uložen počet aktuálně načítaných vzestupných hran hodinového taktu, proto postačí tento signál odeslat (propojit) na výstup označený vystup. Provedeme to jednoduchým příkazem pro přiřazení signálu citac na vystup. vystup <= citac;
Shrňme nyní celý VHDL kód pro jednoduchý 4bitový synchronní čítač. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity citac is port (clk : in std_logic; vystup : out std_logic_vector (3 downto 0) :="0000"); end citac; architecture kod_citac of citac is signal citac : std_logic_vector (3 downto 0) :="0000"; begin process (clk) begin if clk='1' and clk'event then citac <= citac + 1; end if; end process; vystup <= citac; end kod_citac;
První částí VHDL kódu je výčet použitých knihoven. Následuje deklarace hlavní entity čítače, která definuje hodinový vstup s názvem clk a výstup čítače v podobě 4bitového vektoru logických hodnot. Následuje architektura s vlastním popisem chování čítače, ve které je deklarován pomocný 4bitový signál s názvem citac typu logický vektor, do kterého se ukládá vlastní obsah čítače. Samotné čítání vzestupných hran hodinového taktu je realizováno prostřednictvím procesu, který obsahuje ve svém citlivostním seznamu vstup clk a pomocí podmínky typu if je vyhodnocována přítomnost vzestupných hran. Na základě toho probíhá inkrementace obsahu čítače, který je následně odeslán na celkový výstup čítače.
18
1.14 Ověření funkčnosti čítače (1/2) Proveďme nyní simulaci čítače, abychom ověřili jeho funkčnost. K tomu můžeme využít různé dostupné simulační programy pro jazyk VHDL, pro potřeby základního testování a simulací však dostačuje produkt ISim, který je standardní součástí balíku ISE od společnosti Xilinx. Do projektu musíme nejprve přidat vlastní testovací soubor. Ten je napsán rovněž v jazyce VHDL a označuje se jako testbench. V něm lze definovat požadovaný hodinový takt pro testování synchronních návrhů, deklarovat sekvenčně posloupnost testovacích signálů a podobně. V testbenchovém souboru je automaticky vytvořena základní struktura pro deklaraci hodinového taktu a dalších pomocných signálů, které jsou definovány v základním nastavení. Pro uživatelskou volbu a přidání vlastní testovací sekvence je vynecháno místo zejména ve spodní části testovacího programu tak, jak je uvedeno níže. stim_proc: process begin -- hold reset state for 100 ns. wait for 100 ns; -- insert stimulus here wait; end process;
Vzhledem k tomu, že v našem případě jednoduchého synchronního čítače nejsou potřeba kromě základního hodinového taktu žádné další testovací sekvence, můžeme použít tuto základní strukturu testbenchového programu bez dalších úprav.
19
1.15 Ověření funkčnosti čítače (2/2) Po spuštění simulace je otevřeno okno vlastního simulačního nástroje ISim. Pomocí ikon v horní nabídce (lupa, posun, vložení značek, nastavení měřítka) lze upravit zobrazení výsledku. V případě našeho čítače ilustruje výsledek simulace následující obrázek.
Výsledek simulace základního 4bitového čítače.
Vlastní simulace začíná v čase 0 ns a hodinový takt má v tomto případě periodu 10 ns. Počáteční stav čítače je 0000 tak, jak je vidět na ukázce simulace. V čase 5 ns se objeví první vzestupná hrana hodinového taktu, na kterou čítač zareaguje a jeho stav se změní na 0001. S příchodem další vzestupné hrany v čase 15 ns se hodnota čítače opět inkrementuje a výsledkem je tak stav 0010. Tímto způsobem čítač čítá postupně přicházející vzestupné hrany hodinového taktu, až do okamžiku, kdy je v něm uložena hodnota 1111. Při další vzestupné hraně přeteče obsah čítače nastavené 4 bity a čítač se tedy v podstatě vrátí do výchozího stavu 0000. Tento přechod je dobře patrný na dalším obrázku simulace.
Přechod čítače ze stavu 1111 do stavu 0000.
20
1.16 Synchronní a asynchronní resetování čítače (1/3) Častým rozšířením jednoduchého čítače pro jeho využití v praktických aplikacích je funkce pro jeho resetování. To provede vymazání obsahu čítače a uvede čítač do výchozího stavu, kdy obsahuje typicky samé nuly. Z funkčního hlediska rozlišujeme synchronní a asynchronní způsob resetování čítače. Zatímco při asynchronním způsobu je resetování čítače (uvedení čítače do výchozího stavu) provedeno okamžitě bez ohledu na stav hodinového taktu, synchronní způsob resetování je prováděn vždy v součinnosti s hodinovým signálem a je proveden při jeho nejbližší následující vzestupné hraně. Zatímco čítač s asynchronním resetem je tedy resetován okamžitě v momentě detekce stavu resetovacího vstupu na úrovni logické jedničky, je stejná procedura provedena v případě synchronního resetování až při nejbližší další vzestupné hraně základního hodinového signálu. Oba dva způsoby mají v praxi svá využití pro konkrétní aplikace. Zatímco synchronní způsob umožňuje zachovat časovou souslednost operací v součinnosti s hodinovým taktem, asynchronní způsob dovoluje provést resetování čítače okamžitě bez dalších prodlev.
21
1.17 Synchronní a asynchronní resetování čítače (2/3) Jako první navrhneme synchronní způsob resetování. V rámci entity je potřeba deklarovat další vstup, který nazveme reset a definujeme ho jako logický vstup. Upravená entita včetně všech vstupů a výstupů tak bude vypadat následovně. entity citac is port (clk : in std_logic; vystup : out std_logic_vector (3 downto 0) :="0000"; reset : in std_logic); end citac;
Podstatná změna se pak bude týkat vlastního procesu čítače. Proces bude opět synchronní (čítání i resetování čítače), proto v citlivostním seznamu bude obsažen pouze hodinový vstup clk. Obě operace jsou synchronní, tedy spouštěny opět při podmínce detekce vzestupné hrany hodinového taktu. Nově je však potřeba doplnit vnořenou podmínku, kdy v případě aktivního resetu bude provedeno vymazání obsahu čítače a jeho nastavení na hodnotu samých nul. Upravený proces čítání můžeme zapsat například takto. process (clk) begin if clk='1' and clk'event then if reset='1' then citac <= "0000"; else citac <= citac + 1; end if; end if; end process;
Kromě takto zapsaného způsobu vložení samých nul do obsahu čítače je možné použít i následující zápis s využitím klíčového výrazu others. citac <= (others =>'0');
I v tomto případě bude výsledkem vložení posloupnosti samých nul do signálu citac.
22
1.18 Synchronní a asynchronní resetování čítače (3/3) V případě asynchronního způsobu resetování čítače využijeme stejnou entitu, jako v předchozím případě (vstupy clk a reset a výstup vystup). Asynchronní metoda resetování čítače vyžaduje navíc umístění vstupu reset do citlivostního seznamu procesu. Změní se však i struktura samotného procesu (if podmínek). Zatímco v případě synchronního způsobu je nadřazenou podmínkou detekce vzestupné hrany hodinového taktu a pak až následně se vyhodnocuje stav vstupu reset, je v případě asynchronní metody resetování nutné nejprve testovat stav reset a následně až pak přítomnost vzestupné hrany hodinového signálu. Takto modifikovaný proces asynchronního spouštění resetu čítače lze v jazyce VHDL vyjádřit například následujícím způsobem. process (reset,clk) begin if reset='1' then citac <= (others =>'0'); elsif clk='1' and clk'event then citac <= citac + 1; end if; end process;
Pro vynulování obsahu čítače bylo opět využito klíčové slovo others. Kromě něho můžeme pro vložení hodnoty čtyř logických nul do signálu citac využít ještě následující ekvivalentní způsoby. signal citac : std_logic_vector (3 downto 0); citac <= "0000"; citac <= ('0', '0', '0', '0'); citac <= (others => '0'); -- vyse uvedene vyjadreni citac(3) <= '0'; citac(2) <= '0'; citac(1) <= '0'; citac(0) <= '0';
moznosti
23
predstavuji
zhusteny
zapis
tohoto
1.19 Simulace synchronního a asynchronního způsobu resetování čítače Proveďme nyní simulace obou navržených způsobů resetování čítače. Vzhledem k tomu, že jsme v entitě čítače definovali nový vstup s názvem reset, je potřeba vytvořit nový testovací soubor typu VHDL testbench. V části určené pro deklaraci uživatelské sekvence testovacích příkazů definujme například čekání po dobu 50 ns a po uplynutí této doby bude provedeno resetování čítače. stim_proc: process begin -- insert stimulus here wait for 50 ns; reset <= '1'; wait; end process;
Jako první spustíme simulaci pro synchronní čítač. Její výsledek je zobrazen na následujícím obrázku.
Výsledek simulace čítače se synchronním způsobem resetování.
Z ní je patrné, že v okamžiku 50 ns od počátku simulace se objeví na vstupu reset úroveň odpovídající logické jedničce. Synchronní způsob je však vázaný na hodinový takt, proto reset čítače proběhne až při nejbližší následující vzestupné hraně hodinového signálu, což nastane v čase 55 ns. V tomto okamžiku se obsah čítače změní na samé nuly. Stejnou simulaci proveďme i pro čítač s asynchronním způsobem resetování.
Výsledek simulace pro čítač s asynchronní metodou resetování.
V případě asynchronního způsobu resetování čítače dojde k vymazání jeho obsahu okamžitě při detekci úrovně odpovídající logické jedničce na vstupu reset. Na rozdíl od předchozí metody se synchronním způsobem tak není potřeba vyčkávat vzestupné hrany hodinového taktu.
24
1.20 Možnost zastavení a spuštění procesu čítání (1/2) V praxi se čítač často doplňuje kromě funkce reset rovněž možností pro spuštění či zastavení čítání [4]. Ta se obvykle označuje jako enable či count enable – CE a slouží v případech, kdy je potřeba dočasně či trvale pozastavit proces čítání, ačkoliv do čítače stále přicházejí další vzestupné hrany hodinového taktu. Rozšiřme proto předchozí jednoduchý čítač s asynchronním způsobem resetování o další řídicí vstup enable. V praxi se obvykle v případě řídícího vstupu enable uvažuje pozitivní logika, tedy čítač čítá, pokud je na vstupu enable hodnota logické jedničky, naopak pokud hodnota na řídícím vstupu enable odpovídá úrovni logické nuly, proces čítání je zastaven. Uveďme nejprve úplný VHDL kód navrženého čítače. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity citac is port (clk : in std_logic; vystup : out std_logic_vector (3 downto 0) :="0000"; reset : in std_logic; enable : in std_logic); end citac; architecture kod_citac of citac is signal citac : std_logic_vector (3 downto 0) :="0000"; begin process (reset,clk) begin if reset='1' then citac <= (others => '0'); elsif clk='1' and clk'event then if enable='1' then citac <= citac + 1; else null; end if; end if; end process; vystup <= citac; end kod_citac;
V rámci entity byl definovaný nový řídící vstup s názvem enable. Do procesu vlastního čítání byla doplněna vnořená podmínka, která vyhodnocuje stav vstupu enable. Při jejím splnění (if enable='1' then) se hodnota čítače inkrementuje, v opačném případě se provede null.
25
Výraz null se v jazyce VHDL používá často právě ve spojení s definováním podmínek (typu if, case, when) a jak již z jeho názvu vyplývá, slouží pro definování stavu programu, kdy se nemá provést žádná změna. V našem případě odpovídá požadavku, kdy je na řídícím vstupu enable hodnota logické nuly (nebo nedefinovaný stav) a hodnota čítače se v takovém případě nemění.
26
1.21 Možnost zastavení a spuštění procesu čítání (2/2) Správnost návrhu čítače ověříme nejlépe jeho simulací. Do souboru typu testbench doplníme sekvenci příkazů pro otestování funkčnosti vstupu enable. Definujme například posloupnost příkazů, kdy na počátku simulace bude vstup enable ve stavu logické jedničky a v čase 40 ns se dostane do stavu odpovídající úrovni logické nuly. V čase 80 ns se pak následně opět vrátí do stavu logické jedničky. VHDL zápis takovéto sekvence bude vypadat následovně. stim_proc: process begin enable <= '1'; wait for 40 ns; enable <= '0'; wait for 40 ns; enable <= '1'; wait; end process;
Výsledek simulace pak představuje následující obrázek.
Simulace funkce enable synchronního čítače.
Na počátku simulace je stav řídícího vstupu enable na úrovni logické jedničky, proto se obsah čítače při každé vzestupné hraně hodinového taktu inkrementuje. V čase 40 ns se jeho stav změní na logickou nulu a další čítání je tak zastaveno. V čítači byla mezitím načítána hodnota 0100. V čase 80 ns se stav enable opět dostane na úroveň logické jedničky a čítač pokračuje v čítání následujících vzestupných hran hodinového signálu.
27
1.22 Čítač se změnou směru čítání (1/2) Jiným možným rozšířením základního synchronního čítače je implementace řízení směru čítání. V anglické literatuře se takovéto čítače obvykle označují jako updown. V rámci entity proto definujme nový řídící vstup s názvem smer, kterým budeme ovládat směr čítání. entity citac is port (clk : in std_logic; vystup : out std_logic_vector (3 downto 0) :="0000"; reset : in std_logic; smer : in std_logic); end citac;
Pro vyhodnocení stavu vstupu pro směr čítání je potřeba definovat další vnořenou podmínku typu if. Vyjděme proto například z předchozí realizace čítače s asynchronním způsobem resetování. Do těla procesu doplníme podmínku, kdy v případě, že vstup smer je ve stavu logické jedničky bude čítač čítat směrem nahoru (inkrementovat), zatímco pokud bude vstup smer ve stavu logické nuly, bude se provádět dekrementace obsahu čítače. Představený návrh čítače lze realizovat například takto. architecture kod_citac of citac is signal citac : std_logic_vector (3 downto 0) :="0000"; begin process (reset,clk) begin if reset='1' then citac <= "0000"; elsif clk='1' and clk'event then if smer='1' then citac <= citac + 1; else citac <= citac - 1; end if; end if; end process; vystup <= citac; end kod_citac;
28
1.23 Čítač se změnou směru čítání (2/2) Pro ověření funkčnosti čítače provedeme opět jeho simulaci pomocí programu ISim. Nejprve je však potřeba definovat sekvenci testovacích příkazů do souboru typu testbench. Zvolíme například situaci, kdy od začátku simulace až do okamžiku 60 ns bude vstup smer ve stavu logické jedničky, v čase 60 ns se na tomto vstupu objeví hodnota odpovídající úrovni logické nuly. VHDL kód pro takto definovanou posloupnost bude následující. stim_proc: process begin smer <= '1'; wait for 60 ns; smer <= '0'; wait; end process;
Simulace čítače se změnou směru čítání.
Na začátku je vstup smer ve stavu logické jedničky až do okamžiku 60 ns. Čítač tak čítá směrem nahoru a inkrementuje svůj stav s každou následující vzestupnou hranou hodinového taktu. Čítač se postupně dostane až do stavu 0110. V tomto okamžiku se změní stav vstupu smer na hodnotu logické nuly a čítač začne od tohoto okamžiku odečítat, což je dobře patrné z vlastního obsahu čítače. Vzhledem k tomu, že se jedná o kruhový typ čítače, přejde čítač ze stavu 0000 při odečtení jedničky do stavu 1111.
29
1.24 Shrnutí realizace čítače pomocí jazyka VHDL Z hlediska distribuce taktovacího (hodinového) signálu rozlišujeme čítače synchronní a čítače asynchronní. Jejich realizace pomocí jazyka VHDL je možná různými způsoby, jedna z nejčastějších je pomocí podmínkové struktury typu if. V případě synchronního čítače je potřeba detekovat vzestupné hrany hodinového taktu, pro což v jazyce VHDL slouží parametr 'event. Resetování čítače je možné řešit synchronně, které probíhá v součinnosti s hodinovým taktem, nebo asynchronně, kdy k němu dochází okamžitě bez nutnosti čekání na nejbližší následující vzestupnou hranou hodinového signálu. Základní čítač lze dále rozšířit například o řízení směru čítání, zastavení a spouštění čítání, nebo čítání v různých binárních kódech. 1. Z jakých slov je složená zkratka VHDL? a) Verilog Hardware Description Language b) VHSIC Hardware Description Language c) Very High Integrity Description Language d) VH Data Logic správné řešení: b
2. K čemu slouží jazyk VHDL? a) K otestování správnosti navržené logické funkce. b) K návrhu logických funkcí a naprogramování programovatelných logických polí. c) K definování okrajových podmínek pro napájení FPGA polí. d) K objektovému řízení pamětí v FPGA a CPLD polích. správné řešení: a, b
3. Celek, který tvoří samostatný ucelený blok se v jazyce VHDL nazývá. a) Architektura b) Komponenta c) Entita d) Signál správné řešení: c
30
4. Část, která ve VHDL definuje chování (popis) daného celku se označuje. a) Modul b) Entita c) Komponenta d) Architektura správné řešení: d
5. Propojení jednotlivých bloků ve VHDL obstarávají. a) Signály b) Skaláry c) Vodiče d) Kabely správné řešení: a
6. Sběrnice (seskupení většího počtu signálů a proměnných) se v jazyce VHDL nazývá. a) Skalár b) Vektor c) Bus d) Kabel správné řešení: b
7. Datový typ označující standardní logické hodnoty je v jazyce VHDL definován a) logic_type b) logic c) type_logic d) std_logic správné řešení: d
31
8. V těle procesu v jazyce VHDL slouží k vložení hodnoty do signálu znak. a) := b) == c) <= d) = správné řešení: c
9. Pro vložení hodnoty do proměnné slouží v jazyce VHDL operátor. a) := b) << c) == d) = správné řešení: a
10. Deklaraci vektoru očíslovaného sestupně od 3 do 0 v jazyce VHDL zapíšeme (od MSB k LSB). a) 3 to 0 b) 0 downto 3 c) 0 to 3 d) 3 downto 0 správné řešení: d
11. Vektor v jazyce VHDL očíslovaný vzestupně od 0 do 3 zapíšeme (od LSB k MSB). a) 3 downto 0 b) 0 to 3 c) 3 to 0 d) 0 downto 3 správné řešení: b
32
12. Parametr pro detekci vzestupné (sestupné) hrany signálu je v jazyce VHDL. a) edge b) detect c) event d) rise správné řešení: c
13. Testovací soubor pro simulaci navrženého VHDL programu se označuje. a) testbench b) testsequence c) probetest d) testcode správné řešení: a
14. Příkaz pro definici konkrétního času pro čekání je ve VHDL. a) stop for b) hold for c) hold off d) wait for správné řešení: d
33