Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Verze 1.0 ze dne 13. září 2013
Příkladný úvod do VHDL
Richard Šusta
Katedra řídicí techniky
ČVUT-FEL v Praze
2
Obsah
0. Úvod ..................................................................................................................................................... 4
1. VHDL stylem "Dataflow" .................................................................................................................... 5
1-1 Majorita ze tří — úvod do VHDL a prostředí Quartus II .......................................................... 5
1-1.a) Schéma obvodu v symbolickém editoru ........................................................................... 5
1-1.b) Vytvoření VHDL kódu ..................................................................................................... 5
1-1.c) Jak VHDL program použijeme v grafickém editoru schémat? ........................................ 6
1-1.d) Vysvětlení VHDL majority: knihovny na řádku 2 až 3.................................................... 7
1-1.e) Vysvětlení VHDL majority: Blok entita — řádky 5 až 12 ............................................... 8
1-1.f) Vysvětlení VHDL majority: Definice port — řádky 6 až 9 .............................................. 8
1-1.g) Vysvětlení VHDL majority: Architektura — řádky 12 až 15 ........................................ 10
1-1.h) Vysvětlení VHDL majority: Příkazový blok— řádka 14 ............................................... 11
1-2 Rozšířená majorita ze tří — VHDL signály ........................................................................... 11
1-2.a) Postup rozšíření VHDL majority o další výstup ............................................................. 11
1-2.b) O definici "signal" na řádce 13 ....................................................................................... 13
1-2.c) Více o <= "concurrent assignment" ................................................................................ 14
1-2.d) Otestování upravené majority ......................................................................................... 14
1-2.e) Co když potřebujeme nerozšířenou verzi majority? ....................................................... 15
1-3 Dekodér pro ukazatel — příkazy with…select jazyka VHDL ................................................ 16
1-3.a) Příkaz with…select ......................................................................................................... 16
1-3.b) Datový typ std_logic versus std_logic_vector ................................................................ 17
1-3.c) Tvorba vlastních typů a atributy VHDL ......................................................................... 18
1-3.d) Dekodér pro lineární ukazatel s výstupy na jednotlivé vodiče ....................................... 19
1-4 Prioritní dekodér — VHDL příkaz when…else ..................................................................... 20
1-4.a) Operátory porovnání a logické operátory ve VHDL ...................................................... 21
1-4.b) Pohled do nitra překladače přes RTL Viewer ................................................................ 22
1-5 Tříbitový prioritní inhibitor — cyklus for-generate ................................................................ 23
1-5.a) Jak vložit do výjimky do VHDL — příkazy generic a assert ........................................ 25
1-5.b) Co programu ještě chybí? ............................................................................................... 26
2. VHDL stylem "Structural" ................................................................................................................ 27
2-1 Použití prioritního inhibitoru ve VHDL ................................................................................. 27
2-1.a) Sekce port map a generic map ......................................................................................... 28
2-1.c) Sekce map a schéma ........................................................................................................ 29
2-1.c) Mapování vstupů a výstupů pro desku DE2 .................................................................... 30
2-2 Rozšíření prioritního inhibitoru o enable ............................................................................... 30
2-2.a) Inicializace proměnných vektorů asociací others=> ....................................................... 31
2-3 Vytvoření vlastní knihovny ("package") ................................................................................. 32
2-3.a) Dekodér pro lineární ukazatel s výstupy na jednotlivé bity pomocí knihovny .............. 33
2-4 Zapojení prioritního dekodéru a lineárním ukazatelem .......................................................... 33
Závěr ....................................................................................................................................................... 35
Příloha A: Testbench pro ModelSim ..................................................................................................... 36
A-1 Vytvoření programu typu Testbench ...................................................................................... 36
A-2 Spuštění Testbench v ModelSim Altera .................................................................................. 39
3
Seznam programů
Program 1 - Majorita ze tří ................................................................................................................... 6
Program 2 - Pořadí vstupů a výstupů v entitě a ve schematické značce ............................................ 10
Program 3 - Majorita s pomocným výstupem jednoho signálu ......................................................... 12
Program 4 - Dekodér pro lineární ukazatel ........................................................................................ 16
Program 5 - Dekodér pro ukazatel s oddělenými výstupy ................................................................. 19
Program 6 - Prioritní dekodér ............................................................................................................. 20
Program 7 - Zbytečné podmínky v příkazu when...else ..................................................................... 21
Program 8 - Program pro demonstraci práce s proměnnými datového typu Boolean ....................... 21
Program 9 - Minimalizovaná kaskáda multiplexorů .......................................................................... 22
Program 10 - Java metoda pro vyzkoušení prioritního inhibitoru ..................................................... 23
Program 11 - Prioritní inhibitor pro 18 vstupů a výstupů .................................................................. 24
Program 12 - Prioritní inhibitor s generic .......................................................................................... 25
Program 13 - Prioritní inhibitor ve strukturálním popisu ................................................................... 27
Program 14 - Nedoporučené použití neúplných definic v bloku "component"! ................................ 28
Program 15 - Automaticky generovaný VHDL kód .......................................................................... 29
Program 16 - Prioritní inhibitor s enable vstupem ............................................................................. 30
Program 17 - Demonstrace použití others pro inicializaci ................................................................. 31
Program 18 - Knihovna kódů vytvořených v úvodu do VHDL ......................................................... 32
Program 19 - Dekodér pro lineární ukazatel s výstupy na jednotlivé bity pomocí knihovny ............ 33
Program 20 - Prioritní dekodér s výstupem na lineární ukazatel ....................................................... 34
Program 21 - VHDL testbech pro dekoder dekodér s výstupem na lineární ukazatel ....................... 37
Seznam obrázků
Obrázek 1 - Majorita ze tří .................................................................................................................. 5
Obrázek 2 - Realizace módů v obvodu ................................................................................................ 9
Obrázek 3 - Schéma odpovídající přiřazení y <= a AND b OR a AND c OR b AND c; ......... 11
Obrázek 4 - Upravená majorita .......................................................................................................... 13
Obrázek 5 - Důsledek chybného dvojího přiřazení hodnot do signálu ytmp ..................................... 13
Obrázek 6 - Aktualizace změněného symbolu ................................................................................... 14
Obrázek 7 - Výsledné schéma v test.bdf ............................................................................................ 15
Obrázek 8 - Rozšířená majorita s nezapojeným výstupem y1 ........................................................... 15
Obrázek 9 - Operace s std_logic_vector ............................................................................................ 17
Obrázek 10 - Dekodér pro lineární ukazatel s bitovými I/O .............................................................. 19
Obrázek 11 - Prioritní dekodér z multiplexorů .................................................................................. 20
Obrázek 12 - Vyvolání RTL Viewer z top-level entity ...................................................................... 22
Obrázek 14 - Ukázka použití prioritního inhibitoru ve schématu ...................................................... 25
Obrázek 13 - Příklad použití prioritního inhibitoru ........................................................................... 25
Obrázek 15 - Příkaz "Create HDL Design File" ................................................................................ 29
Obrázek 16 - Test inhibitoru 2 vložený do schématu ......................................................................... 30
Obrázek 17 - Prioritní dekodér s lineárním ukazatelem ..................................................................... 33
Obrázek 18 - Breakpoint při simulaci ................................................................................................ 42
4
0. Úvod
Proč zrovna VHDL?
VHDL patří mezi HDL programovací jazyky ("Hardware Description Languages") určené pro ná-
vrhy obvodů. V USA se převážně používá HDL jazyk Verilog, se syntaxí podobnou jazyku C, zatímco
v Evropě se více programuje ve VHDL ("Very-high-speed integrated circuits Hardware Description Lan-
guage"), vzdáleně se podobajícímu PASCALu. (Přesněji — VHDL si vypůjčilo mnoho své syntaxe z jazy-
ka ADA, u nás méně známého, který se mírně podobá PASCALu.)
Když jsme se v roce 2009 rozhodovali, zda studenty učit VHDL či Verilog, mnozí profesionální
návrháři, kteří léta používají oba jazyky, se klonili k VHDL. Jde o jazyk sice upovídaný a typově ještě
striktnější než PASCAL, avšak návrhy v něm mají lepší šanci fungovat. Jazyk Verilog má ve své podob-
nosti s jazykem C jen zdánlivou výhodu; postupy osvojené z C programů se nehodí pro obvody.
Zde si nutno poznamenat, že HDL jazyky mají trojí použití: 1/ pro specifikaci obvodů, 2/ pro jejich
simulaci a 3/ pro jejich fyzickou syntézu. Kvůli tomu obsahují široké spektrum programových konstrukcí,
ale pro syntézu obvodů se hodí jen některé z nich.
Účel učebního textu
Následující text vznikl na základě dlouholetých zkušeností s výukou kurzu SPS (Struktury počíta-
čových systémů) na Katedře řídicí techniky Elektrotechnické fakultě v Praze. Používá se v něm VHDL
jako pomocný nástroj pro laboratorní experimenty s počítačovými systémy a paralelním chováním jejich
obvodů — bez HDL jazyků se dnes již nedá rozumně pracovat. Studenti ale nebudou většinou profesio-
nálně navrhovat hardware a potřebují jen jeho základy, ale i ty by měli znát na technické úrovni odpoví-
dající vysoké škole. Při psaní dobrých VHDL programů je třeba občas pomyslet na strukturu vytvářeného
zapojení a nelze jen mechanicky "bušit" jakési programové příkazy.
V češtině existuje několik učebnic VHDL i webových stránek s jeho popisy, avšak často bývají pří-
liš programátorské, soustředěné na samotný jazyk, a navíc postrádají vazby k vhodnému vývojovému
prostředí, ve kterém by se daly návrhy obratem vyzkoušet.
Organizace textu
Výklad bude vycházet z příkladů cílených na FPGA vývojovou desku DE2 firmy Altera. Začneme
od lehké majority ze tří, kterou použijeme nejen k úvodu do VHDL, ale současně i k návodu jak pracovat
s VHDL v prostředí Altera Quartus II. Kvůli tomu doporučuji i pokročilejším čtenářům, aby si úvodní
příklad udělali úplně celý. Další části pak budou věnované více jazyku VHDL.
Prerekvizity
V úvodu do VHDL budeme přepokládat, že čtenáři
mají instalované programovací prostředí Quartus II od firmy Altera, jehož bezplatnou verzi Quartus II
Web Edition Software si lze stáhnout ze stránek firmy po registraci či z webu SPS. Umožní práci jen
s některými FPGA, ale se všemi potřebnými v kurzu SPS, a nepovoluje dílčí překlady částí obvodů;
vždy překládá vše, což u menších návrhů nevadí. Jinak nabízí stejnou funkčnost jako komerční verze.
mají správně vytvořený projekt v Quartus II určený pro desku DE2, a to včetně přiřazení odpoví-
dajících "Pin Assignments" desky DE2 ze souboru "DE2_pin_assignments.csv" ;
ovládají práci ve vývojovém prostředí Quartus II na úrovni symbolického editoru.
Pokud někomu uvedené znalosti chybí, může je získat z materiálů na stránce předmětu SPS:
https://moodle.dce.fel.cvut.cz/course/view.php?id=5
Rada: Text obsahuje krátké příklady. Úmyslně nepřikládám jejich zdrojové kódy. Nekopírujte si je z ob-
razovky. Prostudujte si vždy celý příklad a pak si ho vytvořte zpaměti od začátku — a pokud možno
s minimem pohledů na text. Metodou "Copy-Paste" se ještě nikdo nic nenaučil !
5
1. VHDL stylem "Dataflow" Popis stylem "dataflow", tedy pomocí toku dat, se hodí
pro části popsané kombinačními obvody, v nichž závislost výstupů na vstupech lze popsat logický-
mi operacemi.
1-1 Majorita ze tří — úvod do VHDL a prostředí Quartus II Zadání: Navrhněte logický obvod se třemi vstupy A, B a C a jedním výstupem Y, který bude v lo-
gické ´1´, pokud dva nebo tři vstupy budou v logické ´1´, jinak bude v logické ´0´.
1-1.a) Schéma obvodu v symbolickém editoru
Řešení:
Výstup má být v logické ´1´ při libovolných dvou vstupech
v ´1´. Ze tří vstupů lze vytvořit tři dvojice A-B, A-C a B-C.
Pokud jejich členy spojíme v AND-termy (oba vstupy
v dvojici jsou v ´1´) a ty sloučíme OR operacemi (aspoň jedna
dvojice musí být v ´1´), pak dostaneme
Y = (A and B) or (A and C) or (B and C). Stejný výsledek dá i SoP (Sum-of-Products) pokrytí Kar-
naughovy mapy.
Navržený obvod lze vytvořit v grafickém editoru sou-
borů *.bdf (Block Diagram/Schematic File) ze tří hradel AND
a jednoho hradla OR.
1-1.b) Vytvoření VHDL kódu
Lze použít jakýkoliv textový editor. Výborně se hodí interní VHDL editor v Quartus II, který už od
verze 11 nabízí zvýraznění syntaxe a automatické doplňování kódu po napsání prvních tří písmen.
Vytvoříme nový soubor VHDL z hlavního menu Quartusu (File->New -> VHDL file) a uložíme ho
(File->Save As) pod názvem vytvářeného obvodu — tím bude název v jeho bloku entity, viz dále.
Zvolíme-li pro entitu název majorita, pak VHDL soubor musíme nazvat majorita.vhd.
Poznámka: Ve VHDL se každý obvod (entita) ukládá do stejnojmenného souboru, jelikož překladač
právě tohle předpokládá.
o Alternativní možností je komerční editor Sigasi (http://www.sigasi.com). Jeho bezplatná verze
nabízí vše pro menší soubory a pro větší se přepne na omezené možnosti. Obsahuje i trochu
pokročilejší ediční funkce; jako třeba přejmenování proměnných ("Refactor") a zvýraznění
chyb při psaní. V budově FEL na Karlově náměstí lze používat i jeho plnou verzi jako pomoc-
ný editor — Quartus II detekuje externě změněné soubory a nabídne jejich aktualizaci.
Komentáře ve VHDL začínají dvojicí pomlček "- - " a končí s koncem řádku. Neexistuje sice mož-
nost jak komentovat celé bloky, třeba označit každý řádek zvlášť, ale Quartus II i Sigasi dovedou vlo-
žit komentářové pomlčky na začátky všech řádek ve vybraných blocích a případně je zas odebrat.
Raději nepoužívejte diakritiku, a to ani v komentářích. Překladače ji někdy vezmou, jindy ne. Spo-
lehlivě rozumí jen čistému ASCII. Pozn. VHDL programy nebudete překládat jen v Quartusu, ale pro
simulace i v ModelSim, a tak se hodí dodržet kompatibilitu pro oba překladače.
VHDL nerozlišuje malá a velká písmena. Nicméně bývá dobrým zvykem psát proměnné a klíčová
slova pořád stejně. I když "entity", "Entity", "ENTITY" i "EnTiTy" bere překladač jako stejné slovo,
program bude mnohem přehlednější, budeme-li psát vždy "entity".
Y
Majorita ze tří A
B
C
0 0
0 1
1 0
1 1C
A
A.B + A.C + B.C
B
Obrázek 1 - Majorita ze tří
6
VHDL kód uvedeme napřed celý, poté ukážeme jeho použití v editoru schémat, což nám posléze umožní
lépe vysvětlit jeho části:
-- Majorita ze tri -- 1 library ieee; -- 2 use ieee.std_logic_1164.all; -- 3 -- 4 entity majorita is -- 5 port -- 6 ( a, b, c : in std_logic; -- 7 y : out std_logic -- 8 ); -- 9 end; -- 10 -- 11 architecture dataflow of majorita is -- 12 begin -- 13 y <= (a AND b) OR (a AND c) OR (b AND c); -- 14 end; -- 15
Program 1 - Majorita ze tří
1-1.c) Jak VHDL program použijeme v grafickém editoru schémat?
Pro začlenění do schématu volíme následující postup. VHDL program napřed otestujeme, zda ne-
obsahuje syntaktickou chybu (ikona nad T šipkou) v obrázku dole, či ho přeložíme; označíme majorita.vhd
za hlavní entitu a spustíme buď částečný překlad ikonou pod C šipkou či úplný ikonou pod P šipkou.
Symbol (tj. schematickou značku) pro majoritu vytvoříme buď: 1/ v okně "Project Navigator" pře-
pnutém na záložku "Files" vybereme soubor majorita.vhd, pravou myší otevřeme jeho kontextové menu
a volíme "Create Symbol File for Current File." (S-šipka); nebo 2/ při VHDL souboru otevřeném
v editoru volíme z hlavního menu Quartusu: "File-> Create/Update ->Symbol Files for Current Files".
Nyní můžeme program vyzkoušet. Vytvoříme nový "Block Diagram/Schematic File" (File->New),
a nazveme ho třeba jako test.bdf a otevřeme v něm "Symbol Tool" buď z lišty nástrojů editoru schémat
(viz dole obr. a), nebo alternativně pravým dvojklikem myši kamkoliv na prázdné místo grafické plochy.
7
a) Vložení bloku přes Symbol Tool b) Vygenerování vstupů a výstupů
V dialogovém okně "Symbol" se nám přidala knihovnu "Project", z níž vybereme entitu majorita. Vlo-
žíme ji do schématu. Připojíme k ní vstupy a výstupy pro její vyzkoušení, a to buď manuálně, nebo si
můžeme ušetřit trochu práce, když pravou myší vyvoláme kontextové menu vložené schematické značky
majorita (viz předchozí obr. b) a necháme si pro ni automaticky vygenerovat vstupy a výstupy volbou
"Generate Pins for Symbol Ports". Vložené "pins" jen přejmenujeme podle "assignments" desky DE2.
Nyní zvolíme test.bdf za hlavní entitu, přelo-
žíme ji a nahrajeme do desky DE2. Přepínači SW
vyzkoušíme, že výstup je v ´1´ jen tehdy, když
nejméně dva SW jsou v pozici nahoru, tj. v ´1´.
Nemáte-li desku, alternativní možností je využití
v simulátoru, viz materiály na stránce kurzu.
c) Schéma po přejmenování podle definic desky DE2
Překladači nezáleží na tom, zda jsme entitu vytvořili ve VHDL nebo v symbolickém editoru. Oba
přístupy jsou v mnohých operacích skoro rovnocenné, ovšem ne v pracnosti, složitější obvody se rychleji
navrhnou ve VHDL, kde se dají i snáze porovnávat verze programu než u schémat.
1-1.d) Vysvětlení VHDL majority: knihovny na řádku 2 až 3
Probírání strukturu kódu VHDL začneme od knihoven. -- Majorita ze tri -- 1
library ieee; -- 2 use ieee.std_logic_1164.all; -- 3 -- 4 entity majorita is -- 5 port -- 6 ( a, b, c : in std_logic; -- 7 y : out std_logic -- 8 ); -- 9 end; -- 10 -- 11 architecture dataflow of majorita is -- 12 begin -- 13 y <= (a AND b) OR (a AND c) OR (b AND c); -- 14 end; -- 15
Příkaz "library" (řádek 2) aktivuje knihovnu, zde standardní ieee navrženou v "Institute of Elec-
trical and Electronics Engineers (IEEE)". Na řádku 3 se z ní vybírá balíček ("package") deklarující stan-
dardní logiku "std_logic_1164" a z něho vezmeme vše (all). Pozn. Volba "all" je nejčastější, ale
z knihovny lze též vybrat i jeden prvek, což se někdy hodí pro řešení konfliktů u složitějších projektů.
Řádky 2 a 3 jsou v úvodu téměř každého VHDL programu napsaného pro standardní logické
obvody. Můžete je mechanicky vkládat na začátek každého nového souboru, který vytvoříte v kurzu SPS.
8
Poznámka: Snadná práce s knihovnami představuje výhodu VHDL oproti Verilogu, který zde nabízí
mnohem omezenější možnosti.
1-1.e) Vysvětlení VHDL majority: Blok entita — řádky 5 až 12
Návrh obvodu povinně obsahuje deklaraci bloku entity, v našem programu na řádku 5:
entity majorita is
kde entity je klíčové slovo a "majorita" představuje námi přiřazený název obvodu shodný se jménem
VHDL souboru (majorita.vhd). Blok entity může obsahovat několik definic (ale nemusí mít ani jednu,
může být i prázdný). Náš příklad má jedinou definici, a to "port", proměnných vstupů a výstupů.
Na řádku 10 blok entity končí klíčovým slovem
end;
Zakončení lze napsat zkráceně, tak jako v našem příkladu, nebo případně za end zopakovat
i klíčové slovo entity nebo také název, což se povoluje od verze VHDL-93 (používané v kurzu SPS).
Konec našeho bloku entity může mít tedy některý z tvarů:
entity majorita is -- deklarace end;
entity majorita is -- deklarace end entity;
entity majorita is -- deklarace end majorita;
entity majorita is -- deklarace end entity majorita;
Tabulka 1 - možnosti zakončení deklarace entity ve VHDL-93 a vyšším
Zkracování ukončujících "end" se povoluje i u některých dalších deklarací, například také u bloku
architecture na rádcích 12 až 15, kde možno uplatnit stejný princip. Volba zakončení závisí jen na progra-
mátorovi. U kratších bloků se volí jen end, u delších deklarací bývá lepší pro zvýšení přehlednosti pro-
gramu zopakovat klíčové slovo úvodní deklarace bloku, zde entity, nebo přidat i název bloku či obojí.
Blok entity určuje vnější vzhled obvodu, čili jak obvod uvidíme po vložení jeho instance do sché-
matu, viz ukázka použití v editoru schémat; kvůli tomu jsme ji uvedli před rozborem programu.
VHDL blok entity se obecně chová úplně jinak než třídy známé z C++, Javy a C#, nicméně může-
me říct, že lze najít nepatrnou analogie v tom, že název uvedený v úvodu bloku entity odpovídá jménu
třídy — s odvoláním na něj vytváříme instance třídy, ve VHDL obvodu.
1-1.f) Vysvětlení VHDL majority: Definice port — řádky 6 až 9
Definice port se může objevit jen uvnitř deklara-
ce entity a nejvýše jen jednou. Specifikuje proměnné
vstupů a výstupů, tedy jakési analogie prvků "public"
tříd viditelných zvnějšku. Obsahuje seznamy proměn-
ných spolu se seznamy jejich názvů (signal-names),
datovými typy (signal-type) a módy (mode) určujícími
druh vstupu či výstupu. Pozn. Všimněte si, že za po-
sledním signal-type chybí středník — jeho napsání
bývá častou syntaktickou chybou začátečníků.
-- Majorita ze tri -- 1 library ieee; -- 2 use ieee.std_logic_1164.all; -- 3 -- 4 entity majorita is -- 5
port -- 6
( a, b, c : in std_logic; -- 7
y : out std_logic -- 8 !! ZA POSLEDNI DEFINICI NENI ; !!
); -- 9
end; -- 10 -- 11 architecture dataflow of majorita is -- 12 begin -- 13 y <= (a AND b) OR (a AND c) OR (b AND c); -- 14 end; -- 15
9
• Názvy proměnných a bloků: VHDL názvy začínají písmenem A-Z a pokračují písmeny a číslicemi
0-9. Malá a velká písmena se nerozlišují, ale doporučuje se psát názvy pořád stejně. Název nesmí být
klíčovým slovem. Může obsahovat znaky podtrhávače _, avšak nikoliv na začátku nebo konci názvu,
nebo dva podtrhávače za sebou. Povolené názvy: A1, A_1, A12_b7_Z
Nepovolené názvy: A1_ (podtrhávač na konci), A__1 (dva podtrhávače), end (klíčové slovo),
A$B (nepovolený znak $.).
• VHDL od verze 93 dovoluje i rozšířené názvy ohraničené znaky \ \ — v těch lze použít i zakázané
konstrukce. Například dovolené rozšířené názvy jsou \123.45\ , \END\ či \A$B\. Slouží pro speciál-
ní operace a kódy automaticky generované překladači. V běžných programech se z důvodu kompati-
bility doporučuje používat normální názvy.
• V delších programech volíme názvy vždy výstižné, specifikující účel či použití.
• Mód proměnné: Ve většině návrhů se používají jen módy in a out. Mód in označuje vstup, jehož
hodnotu lze zvnitřku obvodu jen číst, ale nelze do něho zapisovat. Naproti tomu mód out specifikuje
výstup, jemuž můžeme zvnitřku obvodu přiřadit hodnotu, ale nelze ho číst.
• Pokud musíme hodnotu výstupu i číst, lze použít mód
buffer, ovšem pro něj musí překladač vyhradit trochu
obsažnější element, jelikož z výstupu vede drát zpět do
vnitřku obvodu.
• Posledním módem je typ inout, který dovoluje data
jak číst tak do nich zapisovat. Vyžaduje ovšem složitý
prvek obousměrného budiče, a tak se používá jedině
výjimečně, například pro obousměrné směrnice jako
třeba sběrnice I2C (na desce DE2 je přes ni připojený
audio výstup).
Obrázek 2 - Realizace módů v obvodu
• V návrzích určených pro syntézu obvodů se doporučuje používat přednostně módy in a out.
Pozn. O portech se zmíníme ještě v sekci "1-2.b) O definici "signal" na řádce 13" na str. 13.
• Datové typy: Pro vstupy a výstupy se používají typy odvozené od std_logic, což je proměnná výčto-
vého typu obsahující 9 hodnot: '1', '0', 'X', 'Z','U', '-', 'L', 'H', 'W' vícehodnotové logiky zvané MVL-9
(Multi Value Logic 9). Pomocí ní lze dobře popsat i neurčitost při návrhu a simulacích obvodů.
Datové typy odvozené od std_logic se při VHDL syntéze volí pro další proměnné spojené v toku dat
se vstupy a výstupy, neboť překladače je mohou lépe syntetizovat.
Unitialized ´U´ objeví se zpravidla jen při simulaci obvodu, kdy díky nezadané inici-
alizaci není možné stanovit hodnotu výstupu
Don’t Care ´-´ v návrhu dosud nezadaná hodnota
Forcing 1 ´1´ logická 1
Forcing 0 ´0´ logická 0
Forcing Unknown ´X´ neznámá hodnota, při simulaci ji nelze stanovit
Weak 1, Weak 0, Weak unknown
´H´,´L´, ´W´
vhodné pro obvody s otevřeným kolektorem, FPGA řady Cyclone je
ale neobsahují, a tak u nich nelze použít
High Impedance ´Z´ odpojení výstupu, třetí stav vysoké impedance
Tabulka 2 - Typ std_logic
• Pozn. VHDL obsahuje i jednobitové datové typy "Boolean" a "Bit", oba opět realizované jako výčto-
vé typy. Typ "Boolean" s hodnotami TRUE a FALSE lze použít jedině pro podmínky, viz dále. Typ
"Bit" s hodnotami ´0´ a ´1´ se zase hodí jen pro generátory simulací či podobné pomocné výpočetní
části. Striktně se nedoporučuje používat ho v programech pro syntézu obvodů, protože překladač pak
nedetekuje některé obvodové chyby, jako například dva zdroje signálu ve zkratu.
10
Pozor —datové typy std_logic a bit nejsou převoditelné žádným přetypováním.
• Pořadí definic v bloku port je sice libovolné, ale určujeme tím pořadí vstupů a výstupů na schema-
tické značky, a proto ji pro schéma musíme po každé změně definice port znovu vygenerovat pomocí
již zmíněného "Create Symbol File for Current File". V definicích port lze použít buď seznam pro-
měnných od sebe oddělených čárkami, tak jako v Program 1 na str. 6, nebo každou proměnnou psát
na samostatný řádek, což dovoluje přidat k ní komentář jejího významu. Komentáře zde vynecháváme
jen pro zkrácení délek řádek, ale v dobrém kódu by je měly být u každého vstupu a výstupu.
entity majorita is
port ( b : in std_logic;
y : out std_logic; c : in std_logic;
a : in std_logic );
end;
Program 2 - Pořadí vstupů a výstupů v entitě a ve schematické značce
1-1.g) Vysvětlení VHDL majority: Architektura — řádky 12 až 15
Blok architektury popisuje vlastní obvod. V programu majorita začíná na řádku 12, a to klíčovým
slovem architecture, za nímž následuje jméno vytvářené architektury a název entity, pro niž je archi-
tektura určená. Poté následuje příkazový blok begin end; připomínající bloky v Pascalu.
Blok entity se v některých publikacích připodobňuje k interface v Pascalu a blok architektury zase
k vlastní implementaci. Lépe by se možná hodila jiná podobnost přirovnávající obvod ke krabičce s elek-
tronickým přístrojem, třeba se zesilovačem. Entita pak představuje ovládací prvky vyvedené na povrch
krabičky, jako třeba vstupy signálu z mikrofonu, kytary nebo přehrávače, zdířky pro připojení výstupů na
reproduktory či sluchátka a voliče zesílení. Architektura se zde podobá vnitřnímu zapojení zesilovače,
skrytému před vnějším pozorovatelem uvnitř krabičky. Lze měnit ovládací prvky na povrchu krabičky (tj.
entitu) a přizpůsobit jim vnitřní zapojení (tj. architekturu), nebo naopak měnit jen vnitřní zapojení pro
vylepšení funkce a plně zachovat ovládací prvky, či změnit oboje. -- Majorita ze tri -- 1 library ieee; -- 2 use ieee.std_logic_1164.all; -- 3 -- 4 entity majorita is -- 5 port -- 6 ( a, b, c : in std_logic; -- 7 y : out std_logic -- 8 ); -- 9 end; -- 10 -- 11
architecture dataflow of majorita is -- 12
begin -- 13
y <= (a AND b) OR (a AND c) OR (b AND c); -- 14
end; -- 15
Název architektury dataflow byl zvolený podle stylu programu. Naši entitu majorita popisuje-
me architekturou ve stylu "dataflow", a tak jsme ji tak nazvali. Každý název architektury patří jen do
jmenného prostoru identifikátorů své entity. Jinými slovy — u dalších entit můžeme jejich architektury
opět nazvat dataflow, budou-li popsané tímto stylem.
Pozn. Povinný název architektury slouží pro případy, kdy entita má několik různých architektur, tj.
více bloků architecture pro jednu entitu. Víc různých architektur se hodí například tehdy, když potře-
bujeme jednu funkci obvodu pro jeho samotnou realizaci a další pro jeho urychlení při simulacích. Pou-
žívání několika architektur, samozřejmě odlišených různými názvy, představuje už pokročilejší téma —
v kurzu SPS vystačíte s jednou architekturou pro každou entitu.
b
c
a
y
majorita
inst
11
Pro zakončení bloku architektury klíčovým slovem end platí podobné možnosti jako pro zakon-
čení bloku entity. Za klíčovým slovem end možno zopakovat i klíčové slovo architecture i její název
dataflow, podobně jako u zakončení bloku entity, viz Tabulka 1 na straně 8.
1-1.h) Vysvětlení VHDL majority: Příkazový blok— řádka 14
Vlastní implementace architektury zaujímá jedinou řádku 14. Obsahuje logický výraz a přiřazovací
příkaz <= označovaný jako "concurrent assignment", tedy souběžné, resp. paralelní přiřazení. Všechna
taková přiřazení se provádějí současně, bez ohledu na jejich pořadí. V našem jednoduchém příkladu má-
me však jen jedno, a tak ho více rozebereme v další části, kde si náš příklad rozšíříme o další výstup.
Logický výraz obsahuje operátory AND a OR. VHDL jazyk definuje následující operátory se jmé-
ny odpovídajícími běžným názvům hradel, ale rozeznává pouze dvě priority. Nejvyšší má unární operátor
negace NOT, zbylé operátory mají stejnou prioritu, závorky jsou ve výrazech nezbytností.
Vyšší priorita not Nižší priorita and or nand nor xor xnor
Tabulka 3 - Logické operátory jazyka VHDL
Pokud bychom v našem výrazu
y <= (a AND b) OR (a AND c) OR (b AND c);
vynechali závorky a napsali ho jako
y <= a AND b OR a AND c OR b AND c;
pak by se ve skutečnosti realizovalo zapojení odpovídající výrazu
y <= (((((a AND b) OR a) AND c ) OR b) AND c);
a schématu s úplně jinou funkcí:
Obrázek 3 - Schéma odpovídající přiřazení y <= a AND b OR a AND c OR b AND c;
1-2 Rozšířená majorita ze tří — VHDL signály Zadání: Rozšiřte majoritu o další výstup Y1, který bude signalizovat, že jen jeden vstup je ve stavu
logické ´1´ a ostatní jsou v ´0´.
Řešení: Máme dvě možnosti.
1) Přidáme rovnici pro další výstup Y1, ten bude v ´1´ pouze při jednom vstupu v ´1´, tzn. za některé
kombinace vstupů ABC = "100", "010" a "001". Podmínku můžeme vyjádřit logickou rovnicí:
Y1 <= (A AND not B AND not C) or (not A AND B AND not C) OR (not A AND not B AND C);
2) Uplatníme skupinovou minimalizaci, tj. využijeme již stávající rovnice pro Y. Výstup Y je v ´1´
jen při dvou nebo třech vstupech v ´1´, ve zbývajících případech bude v ´0´, tedy při všech vstupech v ´0´
a právě při jednom vstupu v ´1´. Stačí tedy selektivně vybrat poslední podmínku, k čemuž můžeme použít
OR-term všech vstupů a ten následně spojit AND s negací Y.
Y1 <= not Y AND (A OR B OR C);
Rovnice nám říká, výstup Y1 bude v ´1´ právě tehdy, pokud nebude splněna podmínka majority (not Y)
a aspoň jeden vstup bude v ´1´, což spolehlivě vyloučí nechtěný případ, kdy všechny vstupy jsou v ´0´.
1-2.a) Postup rozšíření VHDL majority o další výstup
V deklaraci entity přidáme pouze výstup Y1.
-- Majorita ze tri s výstupem jednoho signálu -- 1
library ieee; -- 2
a b c
y
12
V architektuře nelze napsat přímo y1 <= not y AND (a OR b OR c); protože překladač by ohlásil chybu,
že výstup y nelze číst, protože je módu out, viz část "1-1.f) Vysvětlení VHDL majority: Definice port —
řádky 6 až 9" na straně 8.
Museli bychom buď změnit mód výstupu y na buffer, čímž ale zbytečně ztížíme implementaci kó-
du. Lepší řešení nabízí možnost vložení pomocného vodiče; ten se ve VHDL nazývá signal. Pojmenu-
jeme ho ytmp a bude stejného datového typu jako proměnná y, tedy datového typu std_logic. Definici
signálu "signal ytmp : std_logic;" vložíme před příkazový blok begin end architektury
Původní výraz y <= (a AND b) OR (a AND c) OR (b AND c); změníme. Jeho pravou stranu přiřadíme
do signálu ytmp zápisem: ytmp <= (a AND b) OR (a AND c) OR (b AND c); a poté ytmp přiřadíme do y.
Signál ytmp již smíme číst a lze ho použít ve výrazu pro y1.
Celý program bude nyní vypadat takto:
use ieee.std_logic_1164.all; -- 3
-- 4
entity majorita is -- 5
port -- 6
( a, b, c : in std_logic; -- 7
y, y1 : out std_logic -- 8
); -- 9
end; -- 10
-- Majorita ze tri s výstupem jednoho signálu -- 1
library ieee; -- 2
use ieee.std_logic_1164.all; -- 3
-- 4
entity majorita is -- 5
port -- 6
( a, b, c : in std_logic; -- 7
y, y1 : out std_logic -- 8
); -- 9
end; -- 10
-- 11
architecture dataflow of majorita is -- 12
signal ytmp : std_logic; -- 13
begin -- 14
ytmp <= (a AND b) OR (a AND c) OR (b AND c); -- 15
y <=ytmp; -- 16
y1 <= not ytmp AND (a OR b OR c); -- 17
end; -- 18 Program 3 - Majorita s pomocným výstupem jednoho signálu
13
1-2.b) O definici "signal" na řádce 13
Signály jsou ve skutečnosti pojmenované vodiče neboli propojky. Můžeme je libovolněkrát číst dle
potřeby. Čtení jejich hodnoty vlastně znamená, že z vodiče uděláme odbočku, čili na něj připojíme.
Upravený program majorita vnáší změnu, která je zvýrazněna na obrázku dole. Zakroužkované
hradlo už nemá k sobě připojené přímo Y, (viz dřívější obrázek na str. 5), ale pojmenovaný vodič ytmp
(signal ytmp : std_logic;), připojený <= příkazem: ytmp<=(a AND b) OR (a AND c) OR (b AND c);
Vodič ytmp vede na výstup y, a to příkazem "y <= ytmp;" . Hodnota vodiče (signálu) ytmp se
připojuje i na vstup hradla not (invertoru), a to čtením hodnoty ytmp v y1 <= not ytmp AND (a OR b OR c);
Jelikož signály představují vodiče, můžeme jim hodnotu přiřadit pouze jednou v celém obvodu, ji-
nými slovy lze je připojit jen na jeden zdroj signálu. Dvojí přiřazení by vlastně znamenalo, že vodič bu-
díme ze dvou zdrojů najednou, například máme spojené výstupy dvou hradel do zkratu a ty se přetahují o
výslednou hodnotu.
architecture dataflow of majorita is
signal ytmp : std_logic;
begin
ytmp <= (a AND b) OR (a AND c) OR (b AND c);
y <=ytmp;
ytmp<= ytmp AND (a OR b OR c); -- chybné dvojí přiřazení do ytmp
y1 <= ytmp;
end;
Pokus o dvojí přiřazení, které je v předchozím špatném programu, vyvolá chybu překladače "Can't re-
solve multiple constant drivers for net "ytmp" at majorita.vhd". Co vlastně teď od překladače chceme, to
nám přibližuje obrázek dole, v němž je hypotetická, neproveditelná spojka zvýrazněná červeně.
Pozn. Spojení dvou výstupů se obecně povoluje pouze ve výjimečných případech, například u již
zmíněných obvodů s otevřeným kolektorem, tzv. Wired-Or. DE2 deska s FPGA Cyclone neobsahuje spe-
ciality umožňující zkratovat výstupy, a tak podobné "krkolomnosti" jsou pro ni nepřeložitelné.
Signály a definice vstupů a výstupů sekci v port
I vstupy a výstupy definované v sekci port uvnitř bloku entity jsou ve skutečnosti pojmenované vo-
diče, ovšem vedoucí vně našeho obvodu. Z toho vyplývají všechna jejich omezení
Vodič portu vstupu módu "in" smíme zvnitřku obvodu jen číst, protože bude buzený ze zdroje signálu
přivedeného zvnějšku, tedy ze zapojení, které se připojí k našemu obvodu.
Obrázek 5 - Důsledek chybného dvojího přiřazení hodnot do signálu ytmp
y
y1
ytmp
důsledek chybného dvojího
přiřazení hodnot
do signálu ytmp
c
b
a
y
y1
ytmp b
a
c
Obrázek 4 - Upravená majorita
14
Naopak do vodiče portu výstupu módu "out" lze zvnitřku obvodu přiřadit hodnotu jenom jedenkrát,
podobně jako do signálů. Jeho hodnota bude k dispozici pro jiná vnější zařízení. Jelikož z výstupu ne-
vede spojka zpět do obvodu, nemůžeme ji zvnitřku obvodu číst.
Do vodiče portu výstupu módu "buffer" se opět smí také zapsat jenom jedenkrát hodnota, ale jelikož
od něho vede spojka dovnitř, smíme i číst jeho hodnotu. Pokud něco takového potřebujeme, pak je
mnohem lepší použít pomocný signál tak, jako v příkladu upravené majority. Definicí buffer bychom
jenom zbytečně komplikovali vnitřní implementaci obvodu.
1-2.c) Více o <= "concurrent assignment"
Všechny příkazy <= "concurrent assignment" se provádějí paralelně bez ohledu, kde a v jakém po-
řadí se v obvodu vyskytují. Například, pokud příkazy z předchozího programu proházíme, třeba úplně
obráceně, pak program bude dál pracovat stejně jako prve bez jakéhokoliv snížení efektivity:
y1 <= not ytmp AND (a OR b OR c);
y <=ytmp;
ytmp <= (a AND b) OR (a AND c) OR (b AND c);
Příkazy definují zapojení a každé zapojení v obvodu pracuje současně se všemi ostatními. Zkuste si
představit, že máte tři lampy či jiná elektrická zařízení. Všechna budou pracovat pořád stejně bez ohledu
na to, v jakém pořadí je připojíte k přívodům přívodů elektrické energie. Důležité je jenom to, že jsou
k němu připojené. Přesně takhle pracují příkazy <= "concurrent assignment".
1-2.d) Otestování upravené majority
Chceme-li upravenou majoritu vyzkoušet ve schématu, musíme provést následující kroky:
a) Napřed potřebujeme vygenerovat nový symbol obvodu majorita, protože jsme změnili definici port
v deklaraci jeho entity. Postup se probíral v podkapitole "1-1.c) Jak VHDL program použijeme v gra-
fickém editoru schémat?" na straně 6.
b) Poté musíme aktualizovat stávající schéma obvodu v souboru test.bdf, což nejsnáze provedeme, když
test.bdf otevřeme v grafickém editoru, pravou myší vyvoláme kontextové menu symbolu majority.
Volíme "Update symbol or Block…". Aktualizaci schématu můžeme samozřejmě alternativně provést
i tím, že starý symbol smažeme a na jeho místo vložíme nový.
Obrázek 6 - Aktualizace změněného symbolu
c) Do aktualizovaného schématu přidáme výstup na port y1 pojmenovaný třeba LEDR[1]
d) Nakonec přeložíme test.bdf úplným překladem a nahrajeme výsledek do desky DE2.
15
Obrázek 7 - Výsledné schéma v test.bdf
1-2.e) Co když potřebujeme nerozšířenou verzi majority?
Dvě různé majority s odlišnými výstupy v jednom projektu by si žádaly i odlišné názvy souborů
a jejich entit. V našem případě jsme upřednostnili jen jeden název, tj. nechali si jen rozšířený obvod. Bu-
deme-li někdy potřebovat obyčejnou majoritu, tj. bez rozšíření o výstup y1 aktivace právě jednoho signá-
lu, tak y1 jednoduše nepoužijeme, tj. k ničemu ho nepřipojíme, viz obrázek dole:
Z protokolu překladače (Quartus okno "Compilation Report") plynou následující skutečnosti:
Rozšířená majorita, Obrázek 7, se všemi zapojenými výstupy má složitost 2 LE, tedy dva logické
elementy použitého FPGA.
Zapojení, u něhož není zapojený výstup y1, Obrázek 8, má složitost 1 LE, tedy stejnou složitost jako
původní nerozšířená majorita, viz Obrázek 1 na straně 5. Nezapojené rozšíření si překladač Quartus II
automaticky vystřihl.
Liší-li se obvody jen v přidaném rozšíření a zachovávají přitom svoji původní funkčnost, pak nemusíme
nutně mít několik verzí pod různými názvy, ale stačí nám jen jeden univerzální obvod.
VCCSW[0] INPUT
VCCSW[1] INPUT
VCCSW[2] INPUT
LEDR[0]OUTPUT
LEDR[1]OUTPUT
a
b
c
y
y 1
majorita
inst
Obrázek 8 - Rozšířená majorita s nezapojeným výstupem y1
VCC SW[0] INPUT
VCC SW[1] INPUT
VCC SW[2] INPUT
LEDR[0] OUTPUT a b c
y
majorita
inst
y1
16
1-3 Dekodér pro ukazatel — příkazy with…select jazyka VHDL Zadání:
Navrhněte dekodér pro lineární ukazatel hodnoty.
V jeho tříbitovém výstupu bude tolik bitů v logické ´1´,
kolik má adresa hodnotu, jak ukazuje pravdivostní ta-
bulka vpravo.
Pravdivostní tabulka dekodéru
Vstup adresy Výstupy
Hodnota A1 A0 Q2 Q1 Q0
0 0 0 0 0 0
1 0 1 0 0 1
2 1 0 0 1 1
3 1 1 1 1 1
Řešení:
Můžeme snadno napsat tři logické rovnice, pro každý výstup jednu. Podobný postup není však uni-
verzální a ani přehledný. Nehodil se pro delší ukazatele hodnoty, např. u dekodéru se čtyřbitovou adresou
by měl patnáct výstupů a potřeboval by 15 rovnic. Hledáme jiné, mnohem přehlednější řešení.
Vhodné řešení nabízí přepínač. Poloha jeho jezdce je určena adresou a na kontakty jsou připojené
požadované výstupní kombinace hodnot bitů. K přepínači existuje analogie v podobě multiplexoru pro-
pouštějícímu na výstup jen hodnotu ze vstupu vybraného adresou.
Ve VHDL se multiplexor definuje příkazovou konstrukcí with...select.
Řešení ve VHDL:
Opět uvedeme napřed celý VHDL kód a pak rozebereme jeho zajímavé části odděleně.
-- Dekoder pro linearni ukazatel -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 -- 3 entity dekoder2linearni is -- 4 port -- 5 ( A : in std_logic_vector(1 downto 0); -- 6 Q : out std_logic_vector(2 downto 0) -- 7 ); -- 8 end; -- 9 -- 10
architecture dataflow of dekoder2linearni is -- 11 begin -- 12 with A select -- 13 Q <= "000" when "00", -- 14 "001" when "01", -- 15 "011" when "10", -- 16 "111" when others; -- 17 end; -- 18
Program 4 - Dekodér pro lineární ukazatel
1-3.a) Příkaz with…select
Vstup A i výstup Q jsou definované jako vektory, kterým v obvodech odpovídají sběrnice neboli
skupiny číslovaných vodičů — A má dva vodiče A(1) a A(0); Q tři vodiče Q(2), Q(1) a Q(0). Začátek pří-
kazu with A select specifikuje adresní vstup multiplexoru. Do Q se přiřazují hodnoty vybrané A. Jejich
mapování určí konstanta za klíčovým slovem when rovná hodnotě vstupu adresy. Musí se vždy vyčerpat
všechny možnosti — přepínač (multiplexor) pokaždé vrací nějakou hodnotu. Kvůli tomu poslední volba
obsahuje klíčové slovo others, což znamená všechny dosud neuvedených možností.
1
0
3
2
A1 A0
Q2Q1Q0
"000"
"001"
"011"
"111"
Q2Q1Q0
A1 A0
"000"
"001"
"011"
"111"
17
1-3.b) Datový typ std_logic versus std_logic_vector
Proměnné typu std_logic představují výčtový typ, jehož hodnoty byly uvedené na straně 9. Bu-
deme-li mít definici signal t, x, y, z :std_logic;, pak do těchto proměnných můžeme přiřadit logický výraz
nebo konstanty jeho výčtového typu, např.
t<=´Z´; x<=´1´; y<=´0´; z <= x AND y; Konstanty ´1´ a ´0´ jsou v uvozovkách, nejde o čísla, ale o hodnoty z definice std_logic výčtového typu
v knihovně ieee.std_logic_1164 seznamem hodnot, tj. analogicky k typům enum v Java či C#:
type std_logic is ( 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-');
Datový typ std_logic_vector představuje naproti tomu jednorozměrné polem složené z prvků
datového typu std_logic a standardní knihovna ieee.std_logic_1164 ho definuje jako
type std_logic_vector is array (natural range<>) of std_logic ;
kde
type je klíčové slovo definice typu a std_logic_vector název vytvořeného typu;
natural znamená datový subtyp specifikující číslo typu integer omezené na rozsah 0 až ma-
ximální celé číslo definované v typu integer; (to můžeme zjistit atributem integer´HIGH, viz dá-
le na str. 18 ). Knihovna ieee definuje 32bitvové integer, tj. v rozsahu od -2-31
do 231
-1;
range je klíčové slovo specifikující interval. Ve spojení s natural určuje interval popsaný ce-
lými čísly od 0 do maximálního čísla integer .
range < > znamená nespecifikovaný rozsah, jeho hodnota bude doplněna při použití typu.
Budeme-li mít v architektuře například definice: pak jim odpovídá následující uspořádání:
signal A, B, C: std_logic_vector (7 downto 0);
signal Z: std_logic_vector (1 to 16)
Interval (range) u A, B a C směřuje od 7 do 0, tj. odshora dolů, tedy shodně s číslováním bitů v bi-
nárních číslech, což se upřednostňuje. Směr "to" se používá zpravidla jen pro interní proměnné.
Konstanty typu std_logic jsou v ´ ´ apostrofech, zatímco u typu std_logic_vector jsou v " " uvozov-
kách, a to i v případě, když vektor má délku jen jeden člen. Vektorům totiž odpovídají řetězce. Dlouhé
řetězce, pokud mají počet členů rovný přesně mocnině 2, můžeme zkráceně zapsat hexadecimálním řetěz-
cem, v němž je znak X před první uvozovkou.
Vše nejlépe přiblíží příklad použití výše uvedených proměnných:
A<="10101100"; B<=X"5F"; C<=A AND B; Z(2 to 9)<=C; Z(14 to 15)<= C(4 downto 3);
Obrázek 9 - Operace s std_logic_vector
POZOR, proměnné polí (vektorů) se musí pořád používat se směrem číslování určeným v jejich definici,
tj. s downto nebo s to. Směr se nesmí nikdy obrátit. Napíšeme-li Z(15 downto 14), překladač ohlásí chy-
bu obsahující text: "…range direction of object slice must be same as range direction of object…".
A, B a C:
Z: 1 2 3 4 5 6 7 8 9
7 6 5 4 3 2 1 0
10 11 12 13 14 15 16
preferované
pořadí downto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Z
7 6 5 4 3 2 1 0
1 0 0 0 1 1 0 0A
7 6 5 4 3 2 1 0
0 1 0 1 1 1 1 1B
7 6 5 4 3 2 1 0
0 0 0 0 1 1 0 0C
0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0
Z(2 to 9)<=C;
C<=A and B;
A<="10001100";
B<=X"5F";
Z(14 to 15)<=C(4 downto 3);
18
Z vektoru (pole) můžeme zvolit jeden člen nebo několik prvků, tedy podvektor. Napíšeme-li definice:
signal A: std_logic_vector (7 downto 0); signal xv1, yv1: std_logic_vector (0 downto 0);
signal x, y: std_logic;
pak můžeme použít následující příkazy:
x <= A(0); A(0) vybere z pole prvek na indexu 0, ten bude datového typu std_logic.
A(1) <= '0'; Inicializace prvku datového typu std_logic v poli A na indexu 1.
yv1 <= "0"; Inicializace vektoru std_logic_vector , který má délku 1 člen.
xv1 <= A(2 downto 2); Z vektoru A se vybere vektor datového typu std_logic_vector o délce 1 člen;
ten bude rovný prvku na indexu 2 vektoru A.
Pozn. Editor schémat (*.bdf) v Quartus II používá pro rozsahy polí stručné zápisy stylu Pascal. Schématu
s popisy signálů A[2..0], Z[1..7] a A[1] odpovídají ve VHDL zápisy: A(2 downto 0), Z(1 to 7) a A(1).
1-3.c) Tvorba vlastních typů a atributy VHDL
VHDL je typově velmi striktní. Definujeme-li si dva vlastní typy se shodnou strukturou, budou
z hlediska překladače různé. Mějme například:
type std_logic_vector_A is array (7 downto 0) of std_logic; type std_logic_vector_B is array (7 downto 0) of std_logic; signal s lvA1, s lvA2 : std_logic_vector_A ;
signal s lvB1, slvB2 : std_logic_vector_B ;
Inicializace proběhne v pořádku; ta pracuje s členy vektorů a ty jsou shodného datového typu std_logic.
slvA1 <= X"12"; slvB1 <= X"34"; -- OK, lze
Pokud se pokusíme vzájemně přiřadit proměnné obou skupin, nepůjde to:
slvB2<=slvA1; slvA2<=slvB1; -- Chyba při překladu - typy nesouhlasi
U silně typových jazyků, mezi které patří i VHDL, se shoda struktury datového typu netestuje, důležitá
jsou výhradně jména typů, a ta se liší. Existuje však možnost definovat si subtyp zužující originální typ.
Přepíšeme-li výše uvedené definice na subtypy:
subtype std_logic_vector_A is std_logic_vector (7 downto 0); subtype std_logic_vector_B is std_logic_vector (7 downto 0);
signal slvA1, slvA2 : std_logic_vector_A ;
signal slvB1, slvB2 : std_logic_vector_B ;
pak se příkazy předchozího programu přeloží bez chyby. Subtypy se pokládají za plně kompatibilní s vý-
chozím datovým typem a všemi jeho subtypy:
slvA1 <= X"12"; slvB1 <= X"34"; slvB2<=slvA1; slvA2<=slvB1;
U neznámých typů se lze dotazovat na parametry jejich definic pomocí vlastností nazývaných ve
VHDL atributy, které mají stejné užití jako "property" známé z tříd v Java či v C#, jen se neoddělují teč-
kou, ale apostrofem. Pro typy odvozené od std_logic_vector se hodí následující atributy:
Příklad použití Integer hodnota výsledku Popis
slvA1´left 7 levá hodnota indexu rozsahu
slvA1´right 0 pravá hodnota indexu rozsahu
slvA1´low 0 nižní číselná hodnota indexu rozsahu
slvA1´high 7 vyšší číselná hodnota indexu rozsahu
slvA1´length 8 počet prvku Tabulka 4 - Vybrané atributy
V definicích nových proměnných či v příkazech lze použít také atributy rozsahu:
signal X : std_logic_vector(31 downto 0);
signal novy1 : std_logic_vector(X'range); -- (31 downto 0)
signal novy2 : std_logic_vector(X'reverse_range); -- (0 to 31)
19
1-3.d) Dekodér pro lineární ukazatel s výstupy na jednotlivé vodiče
Může se stát, že předchozí dekodér
nevyhovuje a chceme jiný, kde vstupy a
výstupy nejsou sběrnice, ale jednotlivé
vodiče, jak ukazuje požadované schéma.
Můžeme využít velkou část před-
chozího kódu, ve kterém provedeme
drobné změny, v programu dole vyzna-
čené zvýrazněním:
--Dekoder pro linearni ukazatel s oddelenymi vystupy -- 1
library ieee; use ieee.std_logic_1164.all; -- 2 -- 3 entity dekoder2linearniB is -- 4 port -- 5 ( A1, A0 : in std_logic; -- 6 Q2, Q1, Q0 : out std_logic -- 7 ); -- 8 end; -- 9 -- 10 architecture dataflow of dekoder2linearniB is -- 11 signal A : std_logic_vector(1 downto 0); -- 12 signal Q : std_logic_vector(2 downto 0); -- 13 begin -- 14 A <= A1 & A0; -- 15 with A select -- 16 Q <= "000" when "00", -- 17 "001" when "01", -- 18 "011" when "10", -- 19 "111" when others; -- 20 Q2<=Q(2); Q1<=Q(1); Q0 <= Q(0); -- 21 end; -- 22
Program 5 - Dekodér pro ukazatel s oddělenými výstupy
Postup:
a) Napřed jsme program uložili jako zcela nový soubor pod názvem dekoder2linearniB.vhd a blok entity
přejmenovali na dekoder2linearniB, abychom mohli v projektu stále používat obě entity.
b) Původní definice vstupů a výstupů v sekci port jsme přesunuli do architektury a udělali z nich signály,
řádky 12 a 13, abychom mohli použít stávající kód, který se na ně odvolává.
c) Do bloku port jsme vložili nové definice vstupů a výstupů jako jednotlivých signálů, řádky 6 a 7.
d) Vektorový signál A jsme naplnili hodnotami vstupů A1 a A0 pomocí operátoru & jejich spojení do
vektorové proměnné typu std_logic_vector, řádka 15. Lze samozřejmě použít i příkazy A(1)<=A1;
A(0)<=A0; , ale operátor & je stručnějším zápisem.
e) Výstupy žel musíme rozdělit po jednotlivých prvcích vektoru na řádce 21. Zde neexistuje žádná
zkratka — operátor & nemůže stát jen na levé straně <= přiřazení. Pozn. V obvodech se spíše snažíme
vodiče sjednocovat do vektorů (sběrnic), aby se s nimi lépe a rychleji pracovalo než z nich udělat
mnoho samostatných drátků.
f) Vygenerujeme-li symbolu dekoder2linearniB a vložíme-li ho do schématu, můžeme obvod vyzkoušet.
Nevýhoda naší úpravy spočívá v duplicitě — kód pro lineární ukazatel máme nyní nakopírovaný do
dvou entit. Bude-li potřeba opravit jednu kopii, nesmíme zapomenout provést změnu i ve druhé. V části
o strukturálním popisu si ukážeme jinou metodu, a to jednodušší a mnohem bezpečnější.
VCC SW[1] INPUT
VCC SW[0] INPUT LEDR[1] OUTPUT LEDR[2] OUTPUT
LEDR[0] OUTPUT
A1 A0
Q2 Q1 Q0
dekoder2linearniB
inst
Obrázek 10 - Dekodér pro lineární ukazatel s bitovými I/O
20
1-4 Prioritní dekodér — VHDL příkaz when…else Zadání:
Pro tři vstupy žádostí o přerušení procesoru Int3
až Int1 navrhněte prioritní dekodér, který posílá na
sběrnici číslo periférie žádající o přerušení. Při aktivním
vstupu Int3 s nejvyšší prioritou bude na výstupu číslo 3,
ostatní vstupy se ignorují. Int1 má nejnižší prioritu
a posílá číslo 1, jsou-li zbývající vstupy v ´0´. Nežádá-li
nikdo se o přerušení, výstup bude číslo 0, viz zkrácená
pravdivostní tabulka.
Prioritní dekodér
Vstupy Výstupy
Int3 Int2 Int1 Q1 Q0 Číslo
1 - - 1 1 3
0 1 - 1 0 2
0 0 1 0 1 1
0 0 0 0 0 0
Řešení:
Mohli bychom samozřejmě sestavit logické rovnice, což by lehce šlo u takhle malého obvodu, ale
takový postup se nedá snadno rozšířit například na případ pro 15 vstupů se čtyřbitovým výstupem. Opět
budeme hledat univerzálnější řešení.
Minule jsme si pomohli přepínačem, zde
se ho uplatníme také, ale nikoliv jako jeden
přepínač s mnoha pozicemi. Použijeme několik
dvoupólových přepínačů řazených do kaskády,
čím přirozeně vytvoříme prioritu.
Bude-li sepnutý první přepínač Int3, pak
na výstup Q1 Q0 bude přivedena kombinace
"11", tj. číslo 3, bez ohledu na pozice ostatních
přepínačů. Nebude-li sepnutý Int3, teprve pak
se uplatní další přepínače dle pořadí (priority).
Ve schématu přepínače nahradíme multi-
plexory dvou sběrnic o šířce dva bity; ty ozna-
čují dvojité čáry. Multiplexory budou řazené za
sebou stejně jako přepínače a jejich jednobitové
adresní vstupy budou vstupy přerušení INT[x],
a to v pořadí jejich požadované priority.
Podobné konstrukci odpovídá ve VHDL podmíněné přiřazeni when …else. Po jeho vložení do-
staneme kód:
--Prioritni dekoder -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 -- 3
entity prioritni_dekoder is -- 4 port ( INT : in std_logic_vector(3 downto 1); -- 5 Q : out std_logic_vector(1 downto 0) ); -- 6 end; -- 7 architecture dataflow of prioritni_dekoder is -- 8 begin -- 9 Q<= "11" when INT(3)='1' else -- 10 "10" when INT(2)='1' else -- 11 "01" when INT(1)='1' else -- 12 "00"; -- 13 end; -- 14
Program 6 - Prioritní dekodér
Chování příkazu when .. else, řádky 10 až 13, vyplývá z jeho implementace pomocí kaskády
dvouvstupových multiplexorů dvoubitových sběrnic. Pokud bude nějaká podmínka splněna, její multi-
plexor se přepne a stav všech dalších multiplexorů ležící za ním, nemá již vliv na výsledek. Kvůli tomu
"01"
Int2=0
"10" Q1Q0
Int3=1
"11"
"00"
1
1
1
Int1=1
0
"11"
"01"
Int2=0
"10" Q1Q0
Int3=0
"11"
"00"
1
1
1
Int1=0 0
0
"00"
0
1
0
1
0
1
0
"11"
"01"
"00"
"10"
INT[3]
INT[2] INT[1]
0
Q[1..0]
Obrázek 11 - Prioritní dekodér z multiplexorů
21
v dalších podmínkách ležících níže již nemusíme opakovat případné nesplnění předchozích podmínek.
Bylo by zbytečné a matoucí mít na řádcích 10 až 13 v Program 6 něco takového jako:
Q<= "11" when INT(3)='1' else -- 10 "10" when INT(3)='0' and INT(2)='1' else -- 11 "01" when INT(3)='0' and INT(2)='0' and INT(1)='1' else -- 12 "00"; -- 13
Program 7 - Zbytečné podmínky v příkazu when...else
Pokud by někdo napsal příkaz takhle, nedopustil by se chyby, ale jen by zbytečnými testy opomíjel prio-
ritní strukturu příkazu when…else, viz podtržené části. Např. hodnota na řádku 11 se použije jen při
nesplnění podmínky na řádku 10; tak proč zde testovat INT(3)='0'? Kód ztrácí na přehlednosti vnuco-
váním zbytečné otázky: "Kvůli čemu je tam tohle? — Aha, už vím; jen mňamka pro klávesu delete!"
1-4.a) Operátory porovnání a logické operátory ve VHDL
Priorita Datový typ výsledku Operátory
Nejvyšší stejný jako operandy*) not
Boolean (true, false) = /= < <= > >= Nejnižší stejný jako operandy*) and or nand nor xor xnor
*) Logické operátory jsou v ieee.std_logic_1164 definované jak pro typ std_logic , kdy vracejí výsle-
dek typu std_logic, tak pro std_logic_vector kde dávají výsledek typu std_logic_vector, a také
pro typ Boolean; zde je jejich výsledkem opět Boolean. Tabulka 5 - Porovnávací operátory a logické operátory
Porovnávací operátory mají obvyklou syntaxi, až méně běžný zápis /= pro nerovnost, a prioritně le-
ží mezi operátorem not a ostatními logickými operátory. U složitějších výrazů se proto vyplatí používat
závorky, aby se not aplikovalo správně. Datový typ Boolean, který vracejí porovnávací operátory, se dá
použít jedině jako parametr podmínky; z příkazů s podmínkami zatím známe jen when..else.
Opakované podmínky lze případně uložit do proměnné typu Boolean — ta nebude ale přiřaditelná
do typů std_logic; zde nutno použít konstrukci when..else, viz řádky 11 a 17 dole.
--Prioritni dekoder -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 entity prioritni_dekoder is -- 3 port ( INT : in std_logic_vector(3 downto 1); -- 4 Q : out std_logic_vector(1 downto 0); -- 5 NoInt : out std_logic ); -- 6 end; -- 7 architecture dataflow of prioritni_dekoder is -- 8 signal jeInt3, zadnyInt :Boolean; -- 9 begin -- 10 jeInt3 <= INT(3)='1'; -- 11 Q<= "11" when jeInt3 else -- 12 "10" when INT(2)='1' else -- 13 "01" when INT(1)='1' else -- 14 "00"; -- 15 zadnyInt <= not jeInt3 and INT(2)='0' and INT(1)='0'; -- 16 NoInt <= '0' when zadnyInt else '1'; -- 17 end; -- 18 Program 8 - Program pro demonstraci práce s proměnnými datového typu Boolean
Konstrukce na řádcích 16 a 17 je samozřejmě krajně krkolomná a slouží pouze jako demonstrativní
ukázka práce s podmínkami. Dala by se samozřejmě nahradit:
NoInt <= not INT(3) and not INT(2) and not INT(1);
Pozn. Jako podmínku nelze použít samotný logický výraz proměnných typu std_logic, např. napsat
v programu nahoře na řádku 13 … when INT(2) else…; protože INT(2) není datového typu Boolean; ten
nám vrátí až operátor porovnání. Musíme psát …when INT(2)='1' else….
22
1-4.b) Pohled do nitra překladače přes RTL Viewer
Tato podkapitola probírá mírně pokročilejší témata. Začátečníkům ji doporučuji přeskočit a vrátit se
k ní později. Není zde uvedeno cokoliv, na co by se navazovalo v dalším textu.
Prioritní dekodér jsme si začali tvořit jako schéma z multiplexorů, viz Obrázek 11 na straně 20. Bu-
de ho překladač skutečně tak i implementovat? Poví nám to meziprodukty překladu; ty uvidíme až po
přeložení top-level entity programu. Za tu vybereme buď 1/ soubor prioritni_dekoder.vhd, nebo 2/ schéma,
do kterého jsme vložili prioritni_dekoder. Při způsobu 1/ najedeme myší na priority_dekoder v bloku entity,
pravou myší vyvoláme kontextové menu, z něhož zvolíme Locate->Locate in RTL Viewer. Zvolili-li jsme
cestu 2, vybereme ve schématu vloženou instanci a pravou myší vyvoláme její kontextové menu, kde
provedeme stejnou volbu a poté ješte levou myší otevřeme zobrazený blok instance prioritni_dekoder.
Obrázek 12 - Vyvolání RTL Viewer z top-level entity
Ukáže se "RTL Viewer" (Register Transfer Logic
Viewer) schéma ukazující, jak překladač pochopil
náš program a jaký jeho model si sestavil po mini-
malizaci — opravdu použil kaskádu, ale jen ze
dvou multiplexorů, ne tří jako na schématu
"Obrázek 11" na straně 20. Vytvořeno v Quartus II verze 13.0sp1
Vyvoláváním nápověd u vodičů (najetím na ně myší), zjistíme, že schéma je zapojeno zjednodušeně. Na
obrázku dole vlevo je překreslené RTL vnitřní schéma překladače a vpravo část hypotetického VHDL pro-
gramu, který by mu logicky odpovídal.
architecture dataflow_RTL_Viewer of prioritni_dekoder is begin Q(1)<= '1' when INT(3)='1' else INT(2); Q(0)<= '1' when INT(3)='1' else '0' when INT(2)='1' else INT(1); end;
Program 9 - Minimalizovaná kaskáda multiplexorů
Pokud se dobře podíváme na Obrázek 11 na straně 20, uvidíme, že překladač má pravdu. Levý multi-
plexor lze nahradit ´0´ & INT(1) — posílá "01" při INT1(1), jinak "00". Postřední multiplexor se zase dá
zmenšit na jednobitový. Nicméně určitě můžeme označit za přehlednější úplný příkaz when…else v "Program 6 - Prioritní dekodér" na straně 20 než jeho super-minimalizovanou verzi v Program 9.
I ve VHDL platí zásada, že programy píšeme především přehledně a srozumitelně. V drtivé vět-
šině případů můžeme dokonalé minimalizace nechat překladači — umí je lépe a taky rychleji než my .
Q[0]
Q[1] ´1´
INT[1]
INT[2]
´1´ 1bitový
2bitový
INT[3]
1
0
1
0
1
0
´0´
23
1-5 Tříbitový prioritní inhibitor — cyklus for-generate Zadání: Navrhněte obvod s osmnácti vstupy A[17..0] a výstupy Q[17..0] propouštějící pouze nej-
vyšší vstup ve stavu ´1´ na jemu číselně odpovídající výstup, takže nejvýše jen jeden výstup je v ´1´ —
kvůli tomu jsme obvod nazvali prioritní inhibitor. Pozn. Osmnáct vstupů a výstupů volíme kvůli desce
DE2, která obsahuje osmnáct přepínačů SW a červených led diod LEDR.
Kde bychom takový obvod použili? Představte si, že máte 18 přepínačů ovládajících nějaké funkce,
přičemž se žádá, aby se najednou zapnul jen jeden přepínač. Uživatel může nechtěně, či úmyslně ("poku-
šitel jeden"), provést i několik voleb najednou. Pokud se za přepínače zapojí náš prioritní inhibitor, dupli-
citní zapnutí se potlačí podle priorit přepínačů a máme jistotu volby jen jedné funkce.
Řešení: Příklad představuje prioritní úlohu a šel by řešit pomocí kaskády multiplexorů, stejně jako před-
chozí prioritní dekodér, tedy pomocí konstrukce when…else. Nedal by se ale lehce modifikovat pro jinou
délku vektorů bez dlouhavého přepisování. Zkusíme najít elegantnější řešení.
Napřed si napíšeme logickou tabulku pro dekodér se třemi vstupy a třemi výstupy, abychom zjistili,
zda v ní najdeme možné zákonitosti:
Úplná pravdivostní tabulka
Vstupy Výstupy
A2 A1 A0 Q2 Q1 Q0
0 0 0 0 0 0
0 0 1 0 0 1
0 1 0 0 1 0
0 1 1 0 1 0
1 0 0 1 0 0
1 0 1 1 0 0
1 1 0 1 0 0
1 1 1 1 0 0
Zkrácená pravdivostní tabulka
Vstupy Výstupy
A2 A1 A0 Q2 Q1 Q0
0 0 0 0 0 0
0 0 1 0 0 1
0 1 - 0 1 0
1 - - 1 0 0
Z pravdivostní tabulky, a to především z její zkrácené verze, vidíme, že
Výstup Q2 je evidentně kopií A2, čili Q2 = A2.
Q1 bude v ´1´ jedině tehdy, pokud A2=´0´ a A1=´1´, což lze napsat rovnicí: Q1 = not A2 and A1.
Výstup Q0 bude v ´1´ pouze při vstupech A2=´0´, A1=´0´ a A0=´1´, což zapíšeme logickou rovnicí
Q0 = not A2 and not A1 and A0.
Vidíme, že výstup Qi bude v ´1´ jedině tehdy, když všechny odpovídající vyšší bity A (tj. Aj pro j>i) jsou
v ´0´. Označíme-li N počet bitů a názvem enable pole N proměnných, můžeme napsat rekurzivní vztah:
QN-1 := AN-1; enableN-1 := not AN-1;
Qi := enable i+1 and Ai; enablei := enable i+1 and not Ai; pro i = N-2 až 0
Proč jsme místo jedné proměnné enable použili pole? Program budeme přepisovat do VHDL, kde signá-
lům můžeme hodnotu přiřadit maximálně jen jednou, a tak jsme rekurzivní vztah záměrně vytvořili tak,
aby respektoval podmínku jednoho zápisu do proměnné.
Správnost našeho rekurzivního vztahu si můžete vyzkoušet třeba v Java metodě třídy:
public void PriotritniInhibitor(boolean A[], boolean Q[]) //1
{ boolean[] enable = new boolean[Q.length]; //2
if (A.length != Q.length || A.length < 2) //3
throw new IllegalArgumentException("Obe pole musi mit stejnou delku>1"); //4
Q[Q.length - 1] = A[Q.length - 1]; enable[Q.length-1] = ! (A[Q.length - 1]); //5
for (int i = Q.length - 2; i >= 0; i--) //6
{ Q[i] = enable[i+1] && A[i]; enable[i] = enable[i+1] && !A[i]; //7
} //8
} //9
} //10 Program 10 - Java metoda pro vyzkoušení prioritního inhibitoru
24
Pole proměnných Java typu boolean nahradíme doporučeným typem std_logic_vector s číslování
odshora dolů (downto).
Cyklům for bude v hardwaru odpovídat opakování příkazů. Podobným způsobem se někdy i optima-
lizuje kód na rychlost; kratší cykly se nahradí rozbalením jejich příkazů. Například, mělo-li by pole Q
z Java kódu Program 10 délku 3 prvky, tj. Q.length = 3, pak cyklus bude:
for (int i = 2; i >= 0; i--) //6 { Q[i] = enable[i+1] && A[i]; enable[i] = enable[i+1] && !A[i]; } //7
a při jeho optimalizaci na rychlost by se nahradil sérií příkazů:
Q[2] = enable[3] && A[2]; enable[2] = enable[3] && !A[2]; //7-i=2 Q[1] = enable[2] && A[1]; enable[1] = enable[2] && !A[1]; //7-i=1 Q[0] = enable[1] && A[0]; enable[0] = enable[1] && !A[0]; //7-i=1
Výsledek má sice větší délku, ale ušetřilo se načítání a testování parametru cyklu a výpočty adres polí;
jejich indexy jsou teď konstanty. Podobnou expanzi provede i VHDL pomocí generace. Výsledkem
bude VHDL kód:
-Prioritni inhinitor -- 1
library ieee; use ieee.std_logic_1164.all; -- 2 entity prioritni_inhibitor is -- 3 port ( A : in std_logic_vector(17 downto 0); -- 4 Q : out std_logic_vector(17 downto 0) ); -- 5 end; -- 6 architecture dataflow of prioritni_inhibitor is -- 7 signal enable: std_logic_vector(Q'range); -- 8 begin -- 9 Q(Q'LENGTH-1)<=A(Q'LENGTH-1); -- 10 enable(Q'LENGTH-1)<= not A(Q'LENGTH-1); -- 11
cyklus : for i in Q'LENGTH-2 downto 0 generate -- 12
Q(i)<=enable(i+1) and A(i); -- 13 enable(i)<= enable(i+1) and not A(i); -- 14 end generate; -- 15 end; -- 16
Program 11 - Prioritní inhibitor pro 18 vstupů a výstupů
Generační cyklus začíná na řádku 12. Jeho obecná syntaxe je
label : for <parameter> in <range> generate <concurrent-statements>
end generate [label] ;
kde
label je povinné návěští oddělené dvojtečkou. Musíme ho zadat, i když ho nikde dál již nepouži-
jeme. Zvolili jsme název "cyklus", ale mohli i jiný, třeba "opakuj" apod.
<parameter> představuje parametr cyklu, který bude nabývat hodnot v rozsahu <range>;
<range> udává rozsah hodnot; ten má stejný tvar jako rozsahy polí, je buď downto nebo to ;
end generate [label] ; — cyklus se musí zakončit buď end generate nebo end generate label;
Klíčové slovo generate je povinné, zde ho nelze vynechat;
<concurrent-statements> označuje blok příkazů řazených mezi "concurrent", tj. probíhajících
paralelně. Sem patří přiřazení <= a konstrukce with..select , when..else, dále také for-generate
(cykly lze vnořit) a lze použít i podmíněnou generaci if-generate a příkaz port map; ten probere-
me ve strukturálním stylu VHDL. Příkazy musí být vždy úplné; nelze tedy vkládat jen jejich dílčí
části, jako třeba jednotlivé řádky podmínek ve "when..else" či přidávat členy do výrazů.
Pozn. Příkaz for-generate lze použít jedině v architektuře, v entitě ne. Dále může i své mít lokální pro-
měnné. Zájemci najdou podrobnější informace na webu ve VHDL referenčních příručkách.
25
1-5.a) Jak vložit do výjimky do VHDL — příkazy generic a assert
Java metoda Program 10 na str. 23, ve které jsme testovali rekurzivní vztah pro prioritní inhibitor,
obsahovala příkaz "throw" pro metání výjimky v kódu. Ve VHDL programu jsme nic takového nepoužili;
nemuseli jsme, vektory měly zatím pevné délky. Lze však mít i pole variabilní délky určené externím
generic parametrem, který se zadává zvnějšku, a tak každá instance obvodu může jeho hodnotu mít od-
lišnou. Parametr definujeme v bloku entity , viz řádek 4 dole:
- -Pr ior itn i inhin itor -- 1 l ibrary ieee; use ieee.std_log ic_1164. a l l ; -- 2
entity prioritni_ inhibitor is -- 3 generic( N: natural := 18); -- delka vektoru > 1 -- 4 port ( A : in std_logic_vector(N-1 downto 0); -- 5 Q : out std_logic_vector(N-1 downto 0)); -- 6 end; -- 7 architecture dataflow of prior itni_ inhibitor is -- 8 s ignal enable: std_logic_vector(Q'range); -- 9 begin -- 10 assert N>1 report "Chybny parametr N. Pozadovano N>1" -- 11 severity fai lure; -- 12 Q(Q'LENGTH-1)<=A(Q'LENGTH-1); -- 13 enable(Q'LENGTH-1)<= not A(Q'LENGTH-1); -- 14 cyklus : for i in Q'LENGTH-2 downto 0 generate -- 15 Q(i)<=enable( i+1) and A(i) ; -- 16 enable(i)<= enable(i+1) and not A( i) ; -- 17 end generate; -- 18 end; -- 19
Program 12 - Prioritní inhibitor s generic
Definice generic může obsahovat i několik různých parametrů:
generic( name1: type1 := default_value1; name2: type2 := default_value2;
. . .
nameN: typeN := default_valueN );
Zadané parametry se v programu vždy chovají jako konstanty. Quartus II u nich požaduje výchozí hodno-
tu, která se přiřazuje pomocí := , nikoliv <= "concurrent assignment", protože nejde o propojení; všech-
ny parametry se v době překladu nahradí hodnotami uvedenými v jejich instancích entity.
Konstantu N jsme v našem programu použili u definic rozsahu vstupů a výstupů. Zbytek kódu jsme
nemuseli přepisovat, jelikož jsme "prozíravě" použili atributy, což je obecně doporučovaný postup.
Obrázek 14 - Ukázka použití prioritního inhibitoru ve schématu
Pozn. 1/ Všimněte si, že stejně jako u bloku port se ani v generic nepíše ; za poslední prvkem seznamu.
2/ V editoru schémat lze zapnout/vypnout zobrazování hodnot parametrů u instancí v kontextovém menu
editoru (pravou myší kamkoliv na volnou plochu a "Show->Show Parameter Assignments").
VCCSW[17..0] INPUT LEDR[17..0]OUTPUT
N 18 Signed Integer
Parameter Value Type
A[n-1..0] Q[n-1..0]
prioritni_inhibitor
inst
Obrázek 13 - Příklad použití prioritního inhibitoru
VCC SW[17..0] INPUT LEDR[17..0] OUTPUT A[17..1] Q[17..0]
prioritni_inhibitor
inst
26
Příkaz assert použitý v programu na řádcích 11 a 12 není přesnou analogií výjimky throw v Java, proto-
že se nevyvolává při činnosti obvodu; to by ani nešlo. Při syntéze obvodu se vyhodnocuje už v době pře-
kladu a odpovídá stejnojmenným příkazům či metodám realizovaným různými formami i v Java, C#
a C++. Ve VHDL má příkaz assert syntaxi:
assert <condition> report <string> severity <severity_level>;
kde
<condition> je jakýkoliv výraz vracející datový typ Boolean ;
<string> označuje řetězec, který překladač vypíše uživateli, pokud nebude podmínka splněná, tj. při
podmínce s hodnotou false;
<severity_level> specifikuje požadovanou reakci překladače, ta je buď note, warning, error, failure,
note — text se vypíše mezi informacemi;
warning — vypíše se varováními a bude se pokračovat v sestavování obvodu;
error — udává chybu zcela bránící v sestavení obvodu;
failure — znamená inkonsistenci, nesmyslné zadání, které by nikdy nemělo nastat.
Pozn. V příkazu assert lze vynechat část "severity <severity_level>" , poté se pro <severity_level> automaticky zvolí výchozí hodnota error.
Příklad vypsaných chyb při nedovolené hodnotě parametru ve schématu dole [Quartus II 13.0sp1]:
Error (10652): VHDL Assertion Statement at prioritni_inhibitor.vhd(11): assertion is false -
report "Parametr N musi byt vetsi jak 1" (FAILURE or ERROR)
Error (12152): Can't elaborate user hierarchy "prioritni_inhibitor:inst"
V hlášení se uvádí odkaz "prioritni_inhibitor.vhd(11)" na soubor prioritni_inhibitor.vhd řádek 11, kde se
nachází příkaz assert , u něhož se nesplnila požadovaná podmínka.
1-5.b) Co programu ještě chybí?
Poslední program se již blíží skutečným VHDL programům pro svou vysokou univerzalitu. Můžeme ho
beze změny aplikovat na různé šířky sběrnic, ale přesto není příliš dobrý.
Chybí zde především komentáře, jak jsme již uvedli zde úmyslně vynechané pro zkrácení řádek. Při
nejmenším by se v dobrém programu měly pečlivě komentovat definice v bloku entity a hlavních
proměnných, aby se nemusel luštit jejich význam. Hodí se i vkládat komentářové řádky před celky
kódu specifikující, co se v nich vlastně provádí. Pozn. Hodně programátorů časem zjistí, že komentá-
ře psali hlavně kvůli sobě, aby i dnes pořád věděli, co tehdy dávno napsali a proč právě takhle.
První řádky programu by měly obsahovat více informací než název --Prioritni inhibitor; tohle ví-
me už ze jména souboru! Hodilo by se přidat hlavně stručný popis, k čemu program vlastně slouží
a případně i jméno jeho autora.
Vhodná bývá i specifikace licence, pod níž je kód distribuován, aby ostatní věděli, jak ho mohou bez
obav používat. U akademických programů se často volí "GNU General Public License".
Jazyk – čeština může zůstat základem pro naše lokální programy v kurzu SPS. Chceme-li ale VHDL
kód zpřístupnit více lidem, pak bývá lepší zvolit si angličtinu, a to nejen pro komentáře, ale také od ní
odvozovat všechna jména proměnných.
VCCSW[0] INPUT LEDR[0]OUTPUT
N 1 Signed Integer
Parameter Value Type
A[n-1..0] Q[n-1..0]
prioritni_inhibitor
inst
27
2. VHDL stylem "Structural" VHDL umožňuje popsat návrhy i hierarchickou strukturou — můžeme obvod rozdělit na dílčí jed-
nodušší entity, které samostatně odladíme, a poté popíšeme jejich propojení, podobně jako v editoru
schémat. Musíme zde poznamenat, že pro menší celky nenabízí VHDL vždy takovou přehlednost jako
grafická schéma. Jelikož opět použijeme jednoduché příklady, a tak výhody strukturálního popisu zde
příliš nevyniknou. Nicméně hodí se ho znát; často ušetří hodně práce. Textový program přece jen:
dovoluje lehčí porovnávání jednotlivých verzí než grafická schémata;
s počtem prvků (entit) roste i množství potřebných propojek. Od určité složitosti se grafická
schémata stávají nepřehledná a nepřinášejí už výhody, spíš naopak;
VHDL program lze lépe procházet ladicími nástroji, jak ukazuje příloha o simulacích.
2-1 Použití prioritního inhibitoru ve VHDL V předchozí kapitole "1-5 Tříbitový prioritní inhibitor — cyklus for-generate" jsme si vytvořili
program prioritní inhibitor. Umíme ho vložit do schématu a otestovat s deskou DE2. Lze ho však použít
i samostatně, přímo z VHDL, což demonstrujeme na jednoduchém programu, který plně nahrazuje sché-
ma "Obrázek 14" na straně 25:
--Test prioritniho inhibitoru -- 1
library ieee; use ieee.std_logic_1164.all; -- 2
entity test_inhibitoru is -- 3
port ( SW : in std_logic_vector(17 downto 0); -- 4
LEDR : out std_logic_vector(17 downto 0) ); -- 5
end; -- 6
architecture structural of test_inhibitoru is -- 7
component prioritni_inhibitor is -- 8
generic( N: natural := 18); -- 9
port ( A : in std_logic_vector(N-1 downto 0); -- 10
Q : out std_logic_vector(N-1 downto 0) ); -- 11
end component; -- 12
begin -- 13
inst_prioinhib : prioritni_inhibitor -- 14
generic map (N => 18) -- 15
port map ( A => SW, -- 16
Q => LEDR ); -- 17
end; -- 18 Program 13 - Prioritní inhibitor ve strukturálním popisu
Řádky 1 až 7 obsahují běžný začátek VHDL programu — odkazují na knihovny (na řádku 2) a po-
té deklarují entitu nazvanou test_inhibitoru, a proto soubor uložíme pod názvem test_inhibitoru.vhd.
V entitě definujeme v bloku port vstupy a výstupy, použijeme odkazy na desku DE2 coby přímé analogie
vstupů a výstupů ze schématu "Obrázek 13" na straně 22. Na řádce 7 je pak začátek architektury, opět
pojmenované dataflow.
Řádky 8 až 12 se vyskytují na místě definic lokálních proměnných sekce architektury, kam jsme
dříve vkládali i definice signálů. Nyní jsme tam uvedli blok entity přesně okopírovaný z řádek 3 až 7
"Program 12 - Prioritní inhibitor s generic" na straně 25. U okopírovaného bloku entity jsme jedině hradili
původní entity klíčovým slovem component, které jsme vložili i za end ukončující bloku, zde ho nelze
vynechat. Alternativně jsme mohli i uvést delší určení "end component prioritni_inhibitor".
Vložení deklarace bloku component lze považovat za něco podobného vkládání prototypů známé
funkcí z jazyka C nebo deklarací bloku interface PASCALu.
28
Pozn. Kód se nám přeloží, i kdybychom v bloku
component uvedli neúplné deklarace ("incomplete
type declarations") zavedené ve VHDL-93 pro
složitější konstrukce, tedy vynechali default hodno-
tu u N v generic nebo opominuli rozsahy vektorů
v port. Zmiňujeme se o tom, protože VHDL kódy
nalezené na webu to občas používají, ale doporu-
čuje se používat úplné deklarace — ty mají menší
problémy s kompatibilitou.
component prioritni_inhibitor is -- 8 generic( N: natural); -- 9 port ( A : in std_logic_vector; -- 10 Q : out std_logic_vector ); -- 11 end component; -- 12
Program 14 - Nedoporučené použití neúplných definic v bloku
"component"!
Elegantnější i pohodlnější možnost pro zkrácení kódu si ukážeme později na straně 30 v kapitole věnova-
né tvorbě knihoven pomocí deklarace knihovních balíčků ("packages").
2-1.a) Sekce port map a generic map
Řádky 14 až 17 vytvoří instanci inhibitoru, tu pojmenujeme inst_prioinhib , dlouhý název se zde
sice nepoužije, ale hodí se později v simulaci pomocí ModelSim. Mapování generic parametru N, které
mu přiřadí hodnotu, a dále zapojení vstupů A a výstupů Q z define port lze provést dvěma způsoby:
a) Jmenné asociace ("keyword notation") mají syntaxi:
POZOR - všimněte si, že
generic map( generic_name1 => value1, generic_name2 => value2,
Členy jsou od sebe oddělené čárkami, nikoliv
středníky — jde o seznam asociací.
...
generic_nameN => valueN ) Za závorkou není ; — mapa pokračuje.
port map ( port_name1 => signal_name1, port_name2 => signal_name2,
Zde jde opět o seznam s členy oddělenými od
sebe čárkami.
...
port_nameN => signal_nameN ); Až zde se za ) vyskytuje středník— konec mapy.
Jelikož jde o asociativní seznamy, můžeme jejich členy uvést v libovolném pořadí, ale pro přehled-
nost se doporučuje zachovávat ho.
Označení generic_nameX udává jméno parametru zcela shodné s jeho deklarací v sekci component
generic a hodnota valueX specifikuje jemu přiřazenou hodnotu ve vytvářené instanci. Použijeme-li
místo hodnoty výraz, pak jeho výsledek musí být známý v době překladu — jde o konstantu.
Stejně tak port_nameX udává název vstupu nebo výstupu, a to opět, shodný s deklarací v component
port a signal_name1 určuje, k jakému signálu bude připojen. Pokud se jedná o vstup, lze jeho hodno-
tu specifikovat výrazem, který nemusí mít konstantní hodnotu v době překladu, na rozdíl od generic-
kých parametrů. Výstup musí být samozřejmě vždy mapován na signál. Pozn. Použití výrazů v mapo-
vání port map dovolí Quartus, žel překladač v ModelSimu je zpravidla odmítne z implementačních
důvodů, kvůli tomu bývá lepší výrazy napřed přiřadit do signálů a teprve ty mapovat na vstupy.
b) Poziční asociace ("positional notation") mají syntax:
generic map( value1, value2, ...
valueN )
port map ( signal_name1, signal_name2, ...
signal_nameN );
Hodnoty se v pozičních asociacích musí pochopitelně objevit přesně v pořadí deklarací jednotlivých
parametrů, vstupů a výstupům v bloku component. Pro případné použití výrazů zde platí pravidla uve-
dená v odrážce "a) Jmenné asociace".
Rozhodnutí zda použit jmenných či poziční asociace závisí plně na programátorovi. Pro jednodušší
komponenty lze volit méně upovídané poziční asociace, zejména v případech, kdy deklarace použitých
29
component jsou vložené do entity, a tak si z nich lze snadno přečíst, čemu hodnoty vlastně přiřazujeme.
U složitějších komponent a u komponent definovaných v externích knihovnách se vyplatí napsat mnohem
přehlednější jmenné asociace, které navíc nabízejí vyšší imunitu vůči změnám v originálních entitách.
2-1.c) Sekce map a schéma
Program 13 na str. 27 odpovídá schématu "Obrázek 13" na straně 22. Můžeme ho přeložit jako
"top-level entity" a nahrát do desky DE2. Opačně lze i ze schématu vygenerovat VHDL soubor. Uložíme
schéma "Obrázek 13" jako "test_inhibitoru2.bdf", otevřeme ho a z hlavního menu Quartusu volíme "File->
Create/Update -> Create HDL Design File from Current File" a poté v dialogu zadáme VHDL.
Obrázek 15 - Příkaz "Create HDL Design File"
Ve složce projektu se nám vygeneroval soubor "test_inhibitoru2.vhd", který se však automaticky ne-
přidal do projektu. Jeho entita má název test_inhibitoru2, tedy stejný jako entita vytvořená ze schématu
"test_inhibitoru2.bdf". V projektu lze mít buď "test_inhibitoru2.vhd" nebo "test_inhibitoru2.bdf", nikoliv oba
soubory, aby nám překladač nehlásil chybu duplicity. Odstraníme tedy "test_inhibitoru2.bdf" (okno Project
Navigator záložka Files -> Remove File from Project") a můžeme vložit "test_inhibitoru2.vhd" (např. hlav-
ní menu Quartus:Project->Add Remove Files in Project…, najdeme soubor […], dáme [Add] a [OK]):
-- Copyright (C) 1991-2013 Altera Corporation… -- 1 LIBRARY ieee; USE ieee.std_logic_1164.all; LIBRARY work; -- 2 ENTITY test_inhibitoru2 IS -- 3 PORT( SW : IN STD_LOGIC_VECTOR(17 DOWNTO 0); -- 4 LEDR : OUT STD_LOGIC_VECTOR(17 DOWNTO 0) ); -- 5 END test_inhibitoru2; -- 6 ARCHITECTURE bdf_type OF test_inhibitoru2 IS -- 7 COMPONENT prioritni_inhibitor -- 8 GENERIC (N : INTEGER); -- 9 PORT( A : IN STD_LOGIC_VECTOR(17 DOWNTO 0); -- 10 Q : OUT STD_LOGIC_VECTOR(17 DOWNTO 0) ); -- 11 END COMPONENT; -- 12 BEGIN -- 13 b2v_inst : prioritni_inhibitor -- 14 GENERIC MAP(N => 18) -- 15 PORT MAP( A => SW, -- 16 Q => LEDR); -- 17 END bdf_type; -- 18 Program 15 - Automaticky generovaný VHDL kód
Vygeneroval se nám stejný kód; zde ho jen uvádíme zhuštěněji formátovaný a se zkrácenými úvod-
ními komentáři. Na řádce 2 má jen jeden nový prvek oproti našemu ručně udělanému programu — odkaz
na "LIBRARY work;", tj. případné knihovny v balíčcích ("packages") definované ve složce projek-
tu; ty zatím nemáme; povíme si o nich dále. Tvorba strukturálního VHDL popisu ze schémat může u slo-
žitějších obvodů ztrácet přehlednost díky pomocným proměnných, které si překladač někdy vytváří. Hodí
se však pro začátky — pomůže nám pro začátek jako šablona pro názornější manuální sestavení.
30
2-1.c) Mapování vstupů a výstupů pro desku DE2
Pro Program 13 na straně 27 můžeme vygenerovat symbol, stejně jako pro každou jinou entitu,
a vložit ho do schématu, tedy mít další vrstvu. Vůbec nevadí skutečnost, že jsme vstupy a výstupy
u entity "test_inhibitoru" pojmenovali shodně s přiřazením na vývojové desce DE2 ("Assignments"). Na-
opak, volbou "Generate Pins for Symbol Ports", již dříve popsanou, rychle přiřadíme správně popsané vstu-
py a výstupy, a to prakticky bez práce.
Obrázek 16 - Test inhibitoru 2 vložený do schématu
Pozn. Zde raději připomeneme, že mapování vstupů a výstupů na FPGA "assignments" (desky DE2) se
provádí jedině pro vstupy a výstupy uvedené na úrovni "top-level entity", tedy jen pro zvýrazněné prvky
v obr. nahoře
. U všech vložených entit se názvy berou jen jako jejich lokální proměnné.
2-2 Rozšíření prioritního inhibitoru o enable Zadání: Přidejte k prioritnímu inhibitoru vstup en (enable), který se při en=´0´ uvede maximálně rychle
do ´0´ na všechny výstupy, zatímco při stavu en=´1´, bude inhibitor normálně pracovat.
Řešení: Úloha by šla vyřešit použitím inhibitoru s N+1 vstupy a na jeho nejvyšší vstup s indexem A[N]
připojit negovaný en, takže při en=´0´ by A[N]=´1´ a výstupy Q[N-1] až Q[0] by přešly do ´0´.
Nicméně náš programátorský úmysl bude jasnější, pokud použijeme prioritní inhibitor s N vstupy a za něj
připojíme multiplexor. Jako předlohu ("template") použijeme "Program 13 - Prioritní inhibitor ve struktu-
rálním popisu", který upravíme přejmenováním a doplněním deklarace en.
--Prioritni inhibitor s enable vstupem -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 entity priroritni_inhibitor_enable is -- 3 generic( N: natural := 18); -- 4 port ( A : in std_logic_vector(N-1 downto 0); -- 5 en : in std_logic; -- 6 Q : out std_logic_vector(N-1 downto 0) ); -- 7 end; -- 8 architecture structural of priroritni_inhibitor_enable is -- 9
component prioritni_inhibitor is -- 10 generic( N: natural := 18); -- 11 port ( A : in std_logic_vector(N-1 downto 0); -- 12 Q : out std_logic_vector(N-1 downto 0) ); -- 13 end component; -- 14 signal Qtmp : std_logic_vector(Q´range); -- 15
begin -- 16
inst_prioinhib : prioritni_inhibitor -- 17
generic map (N => N) -- 18 port map ( A => A, Q => Qtmp ); -- 19
Q <= Qtmp when en='1' else (others=>'0'); -- 20 end; -- 21
Program 16 - Prioritní inhibitor s enable vstupem
Novou entitu jsme pojmenovali "priroritni_inhibitor_enable " a uložili do "priroritni_inhibitor_enable.vhd". Na
řádku 6 jsme přidali definici nového vstupu en. Pro vstup zvnějšku jsme použili A (řádek 5), pro výstu-
py opět Q (řádek 7) a pro generický parametr N (řádek 4). Záměrně jsme napsali názvy shodné s kom-
ponentou prioritni_inhibitor, abychom mohli demonstrovat skutečnost, zvýrazněnou i barvami pozadí, že
VCC SW[17..0] INPUT LEDR[17..0] OUTPUT SW[17..0] LEDR[17..0]
test_inhibitoru
inst2
31
proměnné z hlavičky vložené komponenty (řádek 10 až 14) jsou dostupné jedině u mapování její instan-
ce (řádek 17 až 19), a to na levé straně členů => asociativního listu, jinde se na ně nedá odkázat.
Pokud uvedeme v generic map nebo port map asociaci XX=>YY, pak její levý člen XX se au-
tomaticky hledá mezi jmény komponenty, kterou právě mapujeme, tj. mezi jejími generickými parametry,
či mezi vstupy a výstupy. Pravý člen asociace YY se bude hledat mezi proměnnými definovanými v na-
šem obvodu v entitě a v architektuře. Napíšeme-li asociaci N=>N, pak levé N bude vztaženo k N v sekci
generic map na řádku 18 a pravé N se vezme z generic na řádku 4.
Řádek 20 obsahuje příkaz pro vytvoření multiplexoru. Příkaz when…else obecně vede na kaskádu
multiplexorů při více podmínkách, tj. máme-li více řádků s when…else. Pro jednu jednoduchou podmínku
(en='1') je úplně jedno, zda použijeme příkaz Q <= Qtmp when en='1' else (others=>'0'); nebo delší kon-
strukci: with en select Q<= Qtmp when '1', (others=>'0') when others; — výsledkem bude
vždy jeden multiplexor. Upřednostnili jsme pochopitelně kratší zápis when…else.
2-2.a) Inicializace proměnných vektorů asociací others=>
Klíčové slovo others znamená veškeré nepoužité hodnoty. Kromě příkazů, viz nahoře, ho lze užít
i pro vytváření asociačních listů inicializujících proměnné, ovšem jedině za předpokladu, že bude přesně
známa délka výsledku už v době překladu, tj. z našeho kódu půjde jednoznačně odvodit velikost proměn-
né, do níž zapisujeme. Na levé straně asociace inicializací stojí určení indexů v poli s možnými směry
směry jak to tak downto a na pravé straně pak požadovaná hodnota jeho členu či členů.
Možnosti nejlépe přiblíží následující demo-příklad:
library ieee;use ieee.std_logic_1164.all; entity asociace is
generic(N:natural:=8); port ( X1, X2, X3, X4, X5, X6, X7 : out std_logic_vector(N-1 downto 0) ); end entity; architecture structural of asociace is
begin -- Pro N = 8 Pro N = 12
X1 <= (others=>'0'); -- X1="00000000" X1="000000000000"
X2 <= (others=>'1'); -- X2="11111111" X2="111111111111"
X3 <= (others=>'Z'); -- X3="ZZZZZZZZ" X3="ZZZZZZZZZZZZ"
X4 <= (0=>'0', others=>'1'); -- X4="11111110" X4="111111111110"
X5 <= (N-1=>'0', others=>'1'); -- X5="01111111" X5="011111111111"
X6 <= (N-1=>'0', 1=>'0', others=>'1'); -- X6="01111101" X6="011111111101"
X7 <= (N-1 downto N-3=>'1', 1 to 3=>'1', others=>'0'); -- X7="11101110" X7="111000001110"
end; Program 17 - Demonstrace použití others pro inicializaci
Všimněte si, že hodnota u inicializační asociace se specifikuje shodně s datovým typem prvků pole,
i když výsledek ovlivní více členů; jde o asociace na indexované prvky pole. Pole std_logic_vector má čle-
ny výčtového datového typu std_logic, a tak píšeme pravé strany asociací v ´ ´ apostrofech.
Při inicializace X7 jsme v rozsazích použili jak směr downto tak i to , což zde můžeme, protože defi-
nujeme asociaci pro vytvoření inicializační konstanty, až její výsledek se přiřadí vektoru.
Kdybychom chtěli X7 dát stejnou hodnotu bez asociativního listu, měli bychom více <= příkazů:
X7(N-1 downto N-3) <= "111"; X7(N-4 downto 4) <=(others=>'0'); X7(3 downto 1) <="111"; X7(0)<='0';
Aplikaci others jsme se v nich stejně nevyhnuli — prostřední část vektoru X7(N-4 downto 4) má pro-
měnlivou délku a bez others to tady nejde. V příkazech jsme již museli dodržovat směr downto určený
v definicí vektoru. Pokus o použití směru to (např. X7(1 to 3) <="111"; ) by samozřejmě vyvolal chybové
hlášení obsahující text: "…range direction of object slice must be same as range direction of object".
32
2-3 Vytvoření vlastní knihovny ("package") V kapitole "1-3.d) Dekodér pro lineární ukazatel s výstupy na jednotlivé " na straně 19 jsme slíbili,
že modifikaci dekodéru vytvoříme elegantněji, bez opakování kódu. Samozřejmě můžeme postupovat
stejným stylem jako v předchozí kapitole "2-2 Rozšíření prioritního inhibitoru o enable", tj. vložit hla-
vičku component a poté provést její mapování.
Nicméně neustálé vkládání deklarací komponent před každým jejich použitím je, jak se to lidově ří-
ká, "votravné". Vždyť pro podobné situace existuje v jazyce C direktiva preprocesoru "#include" a v C#
se zase používá příkaz using. V Java máme možnost odkázat na vytvořený "package". Stejný koncept se
používá i ve VHDL. Vytvoříme package obsahující potřebné deklarace, na který jen odkazujeme:
-- Knihovna kodu vytvorenych v uvodu do VHDL -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 -- 3
package uvod_knihovna is -- 4 -- 5
component majorita is -- 6 port ( a, b, c : in std_logic; -- 7 y, y1 : out std_logic ); -- 8 end component; -- 9 -- 10 component dekoder2linearni is -- 11 port ( A : in std_logic_vector(1 downto 0); -- 12 Q : out std_logic_vector(2 downto 0) ); -- 13 end component; -- 14 -- 15 component prioritni_dekoder is -- 16 port ( INT : in std_logic_vector(3 downto 1); -- 17 Q : out std_logic_vector(1 downto 0) ); -- 18 end component; -- 19 -- 20 component prioritni_inhibitor is -- 21 generic( N: natural := 18); -- 22 port ( A : in std_logic_vector(N-1 downto 0); -- 23 Q : out std_logic_vector(N-1 downto 0) ); -- 24 end component; -- 25 -- 26 component priroritni_inhibitor_enable is -- 27 generic(N: natural := 18); -- 28 port ( A : in std_logic_vector(N-1 downto 0); -- 29 en : std_logic; -- 30 Q : out std_logic_vector(N-1 downto 0) ); -- 31 end component; -- 32 -- 33
end; -- 34 Program 18 - Knihovna kódů vytvořených v úvodu do VHDL
Na řádku 2 odkazujeme na standardní knihovny, neboť v nich se nacházejí definice datových typů
std_logic a std_logic_vector, které používáme v deklaracích komponent.
Na řádku 4 definujeme náš název knihovny "uvod_knihovna" za klíčovým slovem package. Poté
následují deklarace komponent. Vložili jsme sem všechny dosud vytvořené. Jejich definice zůstávají ve
stejnojmenných souborech, takže je překladač snadno najde.
Na řádku 34 končí deklarace knihovna klíčovým slovem end, ve VHDL-93 a vyšším lze za něj
přidat i klíčové slovo package a případně i název knihovny uvod_knihovna, podobně jako u entity, viz
"Tabulka 1 - možnosti zakončení deklarace entity" na str. 8.
Náš balíček obsahuje pouze hlavičky komponent. Alternativně lze do něho přidat:
33
definice datových typů a subtypů pro použití v našich programech. Baliček představuje ostatně jedi-
nou možnost jak naše vlastní typy a subtypy používat už v bloku entity;
definice konstant;
pro VHDL psané stylem "behavioral", který úvod nezahrnuje, a pro testovací části kódu nazývané
"testbench" může knihovna obsahovat i deklarace funkcí, procedur a jiných prvků, ale pak v ní musí
existovat i další blok package body … end , kde jsou tyto prvky definované. Zájemce o podobné
speciality převyšující rámec kurzu možno odkázat na webové stránky, hledání "vhdl package".
2-3.a) Dekodér pro lineární ukazatel s výstupy na jednotlivé bity pomocí knihovny
Nyní můžeme lehce vytvořit modifikaci dekodéru pro lineární ukazatel s výstupy na jednotlivé bity
ze strany 19, neboť už nemusíme vkládat deklaraci použité komponenty — ta je obsažena v naší knihov-
ně.
--Dekoder pro linearni ukazatel s nevektorovymi I/O -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 library work; use work.uvod_knihovna.all; -- 3 -- 4
entity dekoder2linearniC is -- 5 port ( A1, A0 : in std_logic; -- 6 Q2, Q1, Q0 : out std_logic ); -- 7 end; -- 8 -- 9
architecture structural of dekoder2linearniC is -- 10 signal Q : std_logic_vector(2 downto 0); -- 11 begin -- 12 inst_dek2lin : dekoder2linearni -- 13 port map (A=> A1 & A0, Q=>Q); -- 14 Q2<=Q(2); Q1<=Q(1); Q0 <= Q(0); -- 15 end; -- 16
Program 19 - Dekodér pro lineární ukazatel s výstupy na jednotlivé bity pomocí knihovny
Na řádku 3 vkládáme odkaz na knihovnu; která se nachází v projektu; na ten odkazuje určení work.
Syntaxe zápisu je podobná jako u standardní knihovny na řádku 2.
Řádek 5 až 8 deklaruje vstupy a výstupy entity dekoder2linearniC.
Řádek 13 a 14 vytvoří instanci dekodéru dekoder2linearni nazvanou inst_dek2lin. Pro ma-
pování vstupů můžeme v Quartus použít výraz "A1 & A0" sjednocení bitových signálů do vektoru,
zatímco výstupy je nutné opět rozepsat po jednotlivých bitech přes pomocnou proměnnou.
Kód je nyní dokonce kratší než původní Program 5 na straně 19 a navíc i zachovává zásadu sdílení kódu.
2-4 Zapojení prioritního dekodéru a lineárním ukazatelem Zadání: Vytvořte ve VHDL obvod skládající se z prioritního dekodéru přerušení a indikátoru výstupního
čísla pomocí dekodéru pro lineární displej, dle schématu:
Obrázek 17 - Prioritní dekodér s lineárním ukazatelem
V zapojení si všimněte pojmenovaného vodiče, zvýrazněného červeně — pojmenování lze provést v edi-
toru přes kontextové menu vodiče volbou "Properties". Hodilo by se nám, kdybychom generovali VHDL
VCCKEY[3..1] INPUT
LEDG[2..0]OUTPUT
INT[3..1] Q[1..0]
prioritni_dekoder
inst
A[1..0] Q[2..0]
dekorer2linearni
inst1
NOT
inst2
Qtmp[1..0]
34
kód ze schématu, což si můžete zkusit za domácí úkol, pokud chcete — překladač pak vytvoří pomocný
signál stejného jména. Nicméně kód není složitý, lze ho lehce napsat přímo ve VHDL:
--Prioritni dekoder s linearnim ukazatelem -- 1
library ieee; use ieee.std_logic_1164.all; -- 2
library work; use work.uvod_knihovna.all; -- 3
entity prioritni2linearni is -- 4
port ( KEY : in std_logic_vector(3 downto 1); -- 5
LEDG : out std_logic_vector(2 downto 0) ); -- 6
end; -- 7
architecture stuctural of prioritni2linearni is -- 8
signal Qtmp : std_logic_vector(1 downto 0); -- 9
signal nkey : std_logic_vector(3 downto 1); -- 10
begin -- 11
nkey<= not KEY; -- 12
inst_priodek : prioritni_dekoder -- 13
port map (INT=> nkey, Q=>Qtmp); -- 14
inst_dek2lin : dekoder2linearni -- 15
port map (A=>Qtmp, Q=>LEDG); -- 16
end; -- 17 Program 20 - Prioritní dekodér s výstupem na lineární ukazatel
Řádky 2 a 3 deklarují knihovny a můžeme je beze změny okopírovat z předchozího kódu v Program
19 na straně 33.
Řádek 4 až 7 obsahuje entitu, v níž zadáme název našeho obvodu " prioritni2linearni " a poté běžným
způsobem definujeme vstupy a výstupy, které připojíme na desku DE2.
Řádek 8 a 9 zahajuje architekturu. V její hlavičce definujeme pomocný signál Qtmp pro propojení
obou obvodů a signál nkey.
Řádek 12 do signálu nkey připojíme negaci vstupů KEY; nestisknuté KEY dávají totiž ´1´. Signál použi-
jeme pro mapování port map bez použití výrazu — v příloze plánujeme totiž otestovat kód v prostředí
ModelSim, kde překladač nebere nekonstantní výrazy u mapování instancí.
Řádek 13 a 14 obsahuje vytvoření instance prioritního dekodéru. Na jeho vstup INT mapujeme negaci
signál nkey. Výstupy Q vyvedeme na pomocný vodič Qtmp — bez něho nelze propojení provést. Na
vstupy, výstupy a parametry instancí odvolávat jen uvnitř jejich mapování (příkazy port map a gener-
ic map). Pozn. Ve VHDL nelze odkazovat na prvky instancí zápisy v objektovém stylu, tedy např. psát
něco jako " inst.Q" apod.
Řádek 15 a 16 mapuje druhou instanci — dekodér na lineární displej. Na jeho vstup A připojíme vo-
dič Qtmp a na výstupy Q zelené led-diody LEDG.
Pozn. Pro obě instance jsme použili delší názvy, což nám umožní lepší orientaci při simulaci obvodu
Obvod je hotový, můžeme ho přeložit a vyzkoušet. Zde lze postupovat dvěma způsoby. Buď
1) přeložíme " prioritni2linearni.vhd", vytvoříme pro něj *.vwf soubor a provedeme simulaci v pro-
středí Quartus, viz stránky SPS kurzu.
2) nebo si napíšeme testbench a zkusíme obvod simulovat, což je preferovaný způsob používaný
v profesionální praxi; teprve při něm vyniknou výhody VHDL oproti schématu. Popis postupu 2)
je poněkud rozsáhlý a kvůli tomu byl zařazený do přílohy.
35
Závěr Příkladný úvod do VHDL není úplný. Nevznikl jako kompletní učebnice, ale jako rychlé seznámení
se základy VHDL v prostředí Quartus. Chybí zde například části o reprezentaci čísel a dále především
styl "behavioral", v němž se obvod popisuje svojí funkčností, která se od něho žádá. Po zkušenostech
s předchozí výukou můžeme konstatovat, že studentům obvykle nedělá větší potíže tvořit v "behavioral"
stylu, protože VHDL kód se v něm podobá klasickému programu. Problémy jim činí jedině provést
vhodný popis řešení, ze kterého lze obvod dobře syntetizovat. Někteří "experti" z minulých let vyčerpali
téměř celou kapacitu FPGA obvodu i jednoduchými stopkami se sedmisegmentovým displejem. Nepro-
brané části vyžadují delší vysvětlení, a tak si je necháme na přednášky.
Úvod může obsahovat nejasnosti ve výkladu, případně překlepy, nepřesnosti či jiné prohřešky, které
unikly korekturám. Autor uvítá, pokud mu o nich napíšete, nebo pošlete své připomínky a náměty na roz-
šíření stávajících částí pro příští školní rok.
~ o ~
36
Příloha A: Testbench pro ModelSim Ukážeme si, jak lze vytvořit simulaci pro ModelSim. Jde o profesionální techniku, kterou doporu-
čujeme ve VHDL používat. Není rozhodně podmínkou pro absolvování kurzu SPS. Jakmile však zvlád-
nete jednu simulaci, další vytvoříte již rychleji — postupy jsou podobné. Naučíte-li se aspoň krapánek
"simulovat", můžete získat špetku výhod, minimálně ždibec úspory svého času pro trošku jiné aktivity.
A-1 Vytvoření programu typu Testbench Pokud chceme nějaký program zkoušet, musíme pro něj napřed napsat testbench, jakýsi generátor
příslušných testovacích signálů. Při simulacích v Quartus se automaticky vytvářel ze souboru *.vwf, do
kterého se v jeho editoru "Simulation Vaweform Editor" nakreslily požadované průběhy vstupů. Z nich se
pak vygeneroval testbench.
Pro vyzkoušení našeho "Program 20 - Prioritní dekodér s výstupem na lineární ukazatel" ze strany
34, by vstupní soubor "prioritni2linearni.vwf" mohl vypadat třeba takto:
Kreslíme se změnami po 20 ns postupně všechny možné vstupní kombinace pro klávesy KEY (tlačítko
KEY dává ´0´ při stisknutí, a proto máme průběhy negované). Poté můžeme spustit Quartus II Simulator
.Napřed nastavíme "Simulation->Options - Quartus II Simulator" a poté spustíme funkční simulaci, která testu-
je jen rovnice, a to příkazem "Simulation->Run Functional Simulation":
Funkční simulaci jsme sice provedli relativně rychle, ale tady končí naše časová úspora. Nyní začí-
náme pomalu luštit průběhy, zdalipak se náš obvod chová mravně a netropí nějaké neplechy. Louskání
průběhů simulací si případně zopakujeme po každé změně v kódu. Po čase si položíme otázku, jestli by
testování nešlo zautomatizovat. Podobnou možnost nabízí právě VHDL testbench.
Pozn. U úloh zadávaných v kurzu SPS vystačíte s funkčními simulacemi doplněnými zprávami
z Quartus překladače v položce" TimeQuest Timing Analyzer". Nebudete řešit tak náročné obvody, abyste
museli zkoumat časové simulace uvažující i charakteristiky interních prvků FPGA. Ty sice ukáže
i Quartus simulátor "Simulation->Run Timing Simulation", ale jen v méně přesné formě.
37
VHDL testbench uvedeme v podobě vhodné pro kombinační obvody a poté ho postupně vysvětlí-
me. Kód není těžký a lze ho velmi lehce modifikovat pro další obvody:
--Testbench - Prioritni dekoder s linearnim ukazatelem -- 1 library ieee; use ieee.std_logic_1164.all; -- 2 entity tb_prioritni2linearni is -- 3 end; -- 4 architecture testbench of tb_prioritni2linearni is -- 5 component prioritni2linearni is -- 6 port ( KEY : in std_logic_vector(3 downto 1); -- 7 LEDG : out std_logic_vector(2 downto 0) ); -- 8 end component; -- 9 -- 10 type stimuls is array (0 to 7) of std_logic_vector(2 downto 0); -- 11 constant sKEY : stimuls := ("000","001","010","011","100","101","110","111"); -- 12 constant sLEDG : stimuls := ("000","001","011","011","111","111","111","111"); -- 13 signal tbKEY : std_logic_vector(3 downto 1); -- 14 signal tbLEDG : std_logic_vector(2 downto 0); -- 15 -- 16
begin -- 17 inst_prio2lin : prioritni2linearni -- instance testovaného programu -- 18 port map (KEY=> tbKEY,LEDG=>tbLEDG); -- 19 generator : process -- 20 begin -- 21 for i in stimuls'range loop -- 22 tbKEY <=not sKEY (i); -- KEY davaji negovanou hodnotu -- 23 wait for 20 ns; -- cekej 20 ns -- 24 assert tbLEDG = sLEDG (i) -- 25 report "vystupy odlisne pri indexu " & integer'image(i); -- 26 end loop; -- 27 wait; -- nekonecne cekani, simulace se zde zastavi -- 28 end process; -- 29 end architecture testbench; -- 30
Program 21 - VHDL testbech pro dekoder dekodér s výstupem na lineární ukazatel
Řádek 2 obsahuje standardní knihovny.
Řádek 3 a 4 definuje entitu - pro programy typu testbech má vždy tento tvar, tj. uvádí se pouze její
název tb_prioritni2linearni a chybí deklarace port nebo generic.
Řádek 5 zahajuje architekturu. Nazvali jsme ji testbench.
Řádek 6 až 9 vkládá deklaraci component zkoušené entity prioritni2linearni. Snadno ji vytvoříme
známým postupem, a to okopírováním bloku entity ze souboru prioritni2linearni.vhd.
Řádek 11 — vektory vstupů i výstupů prioritni2linearni mají stejné délky 3 členy, a tak si pro ně vy-
tvoříme datový typ stimuls popisující pole osmi prvků typu std_logic_vector(2 downto 0);
Řádek 12 obsahuje konstantní pole definující požadované simulační vstupy sKEY . Ty jsou přímou
analogií vstupních signálů zadaných ve "Vaweform" editoru na předchozí stránce, akorát jsme je pro
přehlednost zde nenapsali negované — tohle uděláme raději programově.
Řádek 13 obsahuje definice požadovaných simulačních výstupů sLEDG, které budeme v programu
automaticky kontrolovat.
Řádek 14 a 15 obsahuje definice proměnných shodných se vstupy a výstupy testované komponenty,
pro přehlednost jsme před jejich názvy napsali prefix tb od testbench.
Řádek 18 a 19 obsahuje příkaz vytvoření instance zkoušeného obvodu prioritni2linearni, instanci
pojmenujeme inst_prio2lin, a její vstupy a výstupy se připojíme na naše testovací signály.
Řádek 20 deklaruje blok procesu. Příkaz patří do zde neprobraného stylu "behavioral" programování.
Uvnitř procesů se používají příkazy zařazené do skupiny "Sequential Statements" — ty se neprovádějí
38
paralelně, ale vykonávají se sekvenčně jeden po druhém tak jako v běžných programech. Musíme
však zdůraznit, že způsob, jakým je zde napsaný proces, se povoluje jedině v simulacích, ale v těch
má zase většinou tento tvar. Pro syntézu obvodů se proces musí psát jiným způsobem, o němž si bu-
deme povídat na přednáškách - tenhle proces by v reálném obvodu nefungoval.
Strukturu procesu a jeho součinnost s testovaným obvodem ilustruje následující obrázek. Z něho vi-
díme, že z procesu vznikl obvod generátoru připojený k testované entitě.:
Řádek 22 zahajuje programový sekvenční cyklus for - loop , který do proměnné cyklu i typu integer
přiřazuje postupně hodnoty 0 až 7, tj. všechny indexy pole simulačních vstupů.
Řádek 23 načte testovací vstup z indexu i a jeho negovanou hodnotu pošle po signálu tbKEY příka-
zem "tbKEY <=not sKEY (i); " vedoucím na vstup KEY ( port map na řádku 19).
Řádek 24 obsahuje pokyn pro simulátor, aby se další příkaz bloku procesu vykonal až za 20 nano-
sekund. Zdržení jsme vložili kvůli tomu, aby nové hodnoty na vstupech KEY měly čas k ovlivnění vý-
stupů LEDR. Pauza se týká jen procesu, simulátor bude dál pilně počít reakce inst_prio2lin na vstupy.
Řádek 25 až 26 testuje, samozřejmě až po uplynutí předchozího čekání 20 ns, zda výstupy obvodu se
shodují s požadovanými. Při neshodě se vypíše chybové hlášení obsahující zadaný text a hodnotu in-
dexu převedenou na řetězec pomocí atributu integer'image(i); Pozn. Hodnoty vstupů a výstupů nevy-
pisujeme záměrně. Konverze typu std_logic_vector na řetězec je sice možná, ale potřebné funkce
nejsou zahrnuté ve standardních knihovnách a musely by se vkládat celé jejich definice. Pro zjedno-
dušení kódu jsme je raději vynechali. Zájemci naleznou vše potřebné na webu.
Řádek 27 končí cyklus a musí mít tvar "end loop;" — zde za end zde nelze vynechat loop.
Řádek 28 obsahuje příkaz wait, avšak bez zadaného času. Simulátor bude čekat nekonečně dlouho, tj.
zastaví se simulace.
Řádek 29 ukončuje blok procesu a opět zde nelze za end vynechat klíčové slovo process.
Řádek 30 končí architekturu. Máme již trochu delší kód, a tak jsme místo jednoduchého koncového
end zvolili i přidání klíčového slova architecture a názvu architektury testbench.
Budeme-li chtít náš testbench použít pro zkoušení jiného obvodu, vyměníme jen prioritni2linearni kom-
ponentu za novou a upravíme kód pro její instanci. Jakmile definujeme požadované testovací vektory,
můžeme zkoušet. Testbench stačí jen spustit a ihned víme, zda pracuje dle našich požadavků.
Pozn. V Quartusu II není tb_prioritni2linearni.vhd přeložitelný kvůli použití wait for <time>. Můžeme
program maximálně otestovat z lišty nástrojů VHDL editoru tlačítkem "Analyze Current File". Úplný
překlad by skončil chybou "Error (10533): VHDL Wait Statement error at tb_prioritni2linearni.vhd(25):
Wait Statement must contain condition clause with UNTIL keyword". Nicméně příkazy wait se v syntéze
už nepoužívají, a to ani wait until zmíněné v chybové hlášce — to zůstalo kvůli ve VHDL kvůli simulacím
a pro zpětnou kompatibilitu se staršími verzemi VHDL. Pro syntézu existují lepší konstrukce.
KEY[3..1] LEDG[2..0]
priority2linearni
inst_prio2lin
generator : process
tbKEY
tbLEDG
"000","001","010","011","100","101","110","111" sKEY(i)
sLEDG(i) shodné?
"000","001","011","011","111","111","111","111"
20 ns
"odlišné při indexu i"
různé
i 0 1 2 3 4 5 6 7
tb_prioritni2linearni
39
A-2 Spuštění Testbench v ModelSim Altera Program ModelSim-Altera Starter Edition se nainstaluje přímo s vývojovým nástrojem Quartus při jeho
instalaci z ISO-image. Lze ho používat bezplatně, ale neumožní některé postupy potřebné pro profesio-
nální vývoj. V bezplatné verzi provádí jen funkční simulace, tj. bez uvažování skutečných časů v přelože-
ném FPGA. Dovoluje však vkládat do VHDL kódu "breakpointy", takže snáze zjistíme nepovedené části.
Zde předpokládáme, že máte instalovaný "ModelSim-Altera 10.1d (Quartus II 13.0sp1)", tedy verzi
shodnou s výukovými laboratořemi.
Ve vašem Quartus projektu existuje podadresář "<project_root_directory>\simulation\modelsim\" pro Model-
Sim — vytvořte ho, pokud tam dosud není, a máte-li ho, raději smažte vše v něm, aby se vytvářel no-
vý ModelSim projekt bez interakce s možnými soubory automaticky generovanými Quartusem.
Předpokládáme dále, že zkoušená entita v souboru "prioritni2linearni.vhd" se Vám po označení jako
top-level entity přeložila bez chyb úplným překladem v Quartusu.
Spusťte ModelSim. Pozn. Návody k ModelSim najdete v menu Help->PDF… Není ale potřeba číst je
dopředu, další text bude srozumitelný i bez nich, ale hodí se později pro případné rozšíření vědomostí.
Doporučujeme především Tutorial, a to jeho kapitoly 1 až 4 a 6, které obsahují základní věci, a kapi-
tolu 2 příručky "User´s Manual", kde se vysvětluje uživatelské rozhraní ModelSim.
Vytvoření projektu ModelSim
Z hlavního menu ModelSim volíme
File->New->Project…
V dialogu "Create Project"
vyplníme libovolné jméno projektu, např. "test";
v Project Location nastavíme cestu k simulačnímu
adresáři v projektu Quartusu, abychom měli vše
pohromadě — pro lepší zobrazení cest v obrázcích
jsme přesunuli Quartus projekt do "E:/Uvod/", takže
zadáme "E:/Uvod/simulation/modelsim".
Ostatní položky necháme ve výchozím stavu
a dialog potvrdíme [Ok].
Po zavření "Create Project" tlačítkem [OK], se objeví
nabídka "Add Items…", volíme "Add Existing File" pro
přidání souborů, které chceme simulovat.
Objeví se nám dialog "Add file to Project", v němž stis-
kem [Browse] otevřeme "Select files to add to project"
dialog pro prohlížení disku.
Z projektu vybereme všechny soubory,
které bude simulace potřebovat:
tb_prioritni2linearni.vhd
prioritni2linearni.vhd
prioritni_dekoder.vhd
dekoder2linearni.vhd
uvod_knihovna.vhd
a poté ukončíme výběr tlačítkem [Open].
Vybraná jména se objeví v dialogu "Add file to Project". Ponecháme "Reference from current location" volbu
vybranou, aby se na soubory jen odkazovalo. Poté dialog zavřeme [OK].
40
Seznam souborů uvidíme v ModelSim okně na zá-
ložce "Project".
Pokud jsme zapomněli na nějaký soubor, můžeme
ho přidat, když pravou myší klikneme do okna
Project a volíme "Add to project"->"Existing File"
v jeho kontextovém menu.
Alternativní přidání souboru nabízí hlavní menu
"Project"->"Add to project"->"Existing File".
Máme-li nějaký soubor omylem navíc, odstraníme
z projektu pomocí klávesy [delete].
ModelSim vložil soubory a nastavil jejich pořadí, které nemusí být
správné. Volba "Compile"->"Compile Order…" z hlavního menu
otevře dialog, v němž lze pořadí upravit i ručně. Zde ale dáme
přednost automatické korekci volbou [Auto Generate], která vy-
volá i překlad. Po něm zavřeme dialog [OK].
Soubory se přeložily a jejich pořadí se změnilo na správné:
V dolním okně Transcript by se měly objevit spokojené
hlášky překladače potvrzující bezchybný průběh.
Pozn. ModelSim generuje výrazně méně hlášek než Quartus,
ale zato často píše dost podstatné věci, takže doporučuji
pečlivě sledovat okno Transcript. Je-li příliš plné, lze ho vy-
mazat přes jeho kontextové menu volbou Clear.
Opětovný překlad souborů lze kdykoliv později spustit z hlavního menu "Compile"->"Compile All".
Nyní přepneme na záložku [Library], kde v knihovně "work" máme přeložené soubory. U uvod_knihovna je
typ "package" a u ostatních "entity". Pozn. Souborům s testbench bývá někdy zvykem dávat příponu "*.vht", ten
náš by se mohl jmenovat "tb_prioritni2linearni.vht". Není to však povinné. Volili jsme "*.vhd" z ryze praktických
důvodů. Quartus překladač odmítá "*.vht" soubory jako nepřípustné typy a nedají se v něm ani otestovat.
Spustíme simulaci, avšak ne z hlavní-
ho menu "Simulate->Start Simulation";
které nabízí mnoho parametrů pro
speciální případy. Nám stačí výchozí
nastavení — pro něj lze simulaci spus-
tit jednodušeji.
Vybereme v [Library] "work" soubor
s naším testbench tb_prioritni2linearni.
Pravou myší vyvoláme jeho kontexto-
vé menu, v němž zvolíme simulaci
bez optimalizace, aby se nám neredu-
kovaly proměnné a příkazy a viděli
jsme vše.
41
ModelSim změnil svůj vzhled - přepnul svůj Layout (uspořádání oken) z původního "NoDesign" na nový
"Simulate".
Pozn. ModelSim dovoluje uchovat uspořádání oken.
Zpočátku máme 4 výchozí a k nim lze přidat svoje
vlastní, když momentální uspořádání oken uložíme
z hlavního menu "Layout->Save layout As…". Mezi
uspořádáními pak můžeme volně přepínat.
Entita tb_prioritni2linearni obsahuje ve svém stromu do
ní vložené entity. Kvůli tomu jsme instance pojmeno-
vávali raději delšími názvy, aby nám při simulacích
usnadnily orientaci.
V stromu výpisu vidíme také odkazy na čísla řádek
kódu, na ty lze vložit breakpointy.
Nastavíme dále graf průběhů.
Pravou myší vyvoláme kontexto-
vé menu u tb_prioritni2linearni
a volíme "Add to->Wave->All items
in region and below". Ukáže se
nám okno typu Wave , do kterého
se nám vložily všechny proměn-
né z našich VHDL souborů.
Pozn. Okno Wave se nám může objevit buď zahlou-
bené do hlavního okna ModelSim nebo jako samo-
statné okno, které lze umístit kdekoliv na plochu.
Stav jeho zahloubení se mění volbou Dock/Undock
z kontextového menu jeho podokna— pravou myší
na ikonu pod hlavním menu Wave.
Nyní již lze spustit simulaci volbou z hlavního
menu "ModelSim Simulate->Run->Run -All".
Simulace poběží až po první breakpoint nebo až ke
skončení testbench, což bude náš případ, protože
na konci procesu generate máme vložené nekoneč-
né čekání příkazem wait.
Pozn. Velké černošedé plochy okna Wave v obrázku
dole byly graficky upravené pro tisk inverzemi barev.
42
Vybereme okno Wave obsahující záznam průběhů všech proměnných. Stiskneme klávesu F (Zoom Full),
abychom viděli celé průběhy. Alternativně můžeme na liště nástrojů Zoom kliknout na ikonu , která
plní stejnou funkci.
V okně Transcript se nám zatím nevypsala žádná chybo-
vá hláška — náš program neobsahuje chybu.
Úmyslně ji vložíme, abychom viděli, co se stane. Na zá-
ložce [sim] vybereme levým dvojklikem myši entity
tb_prioritni2linearni obsahující náš testbench. Zobrazí se
kód VHDL Program 21 ze str. 37. Vypneme jeho ochra-
nu proti náhodnému přepsání zrušením volby Read Only
v kontextovém menu (pravou myší na text v editoru).
Nyní změníme v hodnotách inicializaci pole řádku 13
constant sLEDG : stimuls := ("000","001","011","011","111","111","111","111"); Na indexu 3 přepíšeme původní "011" na chybné "111": ("000","001","011","111","111","111","111","111");
Soubor uložíme a zapneme jeho Read Only ochranu.
Přeložíme vše (Compile->Compile All) a spustíme simulaci od začátku (Simulate->Restart…). V Restart dialo-
gu, který se objeví, necháme všechny položky vybrané, aby se zachovaly stávající prvky, a potvrdíme to
volbu [OK]. Poté opět spustíme simulaci (Simulate->Run->Run -All).
Proběhne znovu celá od začátku až do konce. V okně Transcript se však objevila hláška příkazu report:
# ** Error: vystup nesouhlasi pri indexu 3
# Time: 80 ns Iteration: 0 Instance: /tb_prioritni2linearni
Necháme si tedy simulaci programu zastavit na chybě, abychom věděli, co se děje. Vložíme breakpoint
do tb_prioritni2linearni.vhd na řádek 26, tj. na příkaz report (dvojklikem levou myší na číslo řádku). Zadáme
zas "Simulate->Restart…" a novou simulaci "Simulate->Run->Run -All".
Simulace se zastaví na na-
šem breakpointu. Najedeme-
li myším kurzorem na pro-
měnnou, která nás zajímá,
uvidíme její hodnotu. Na
obrázku vlevo jsme zvolili
tbLEDG na řádku 19.
Obrázek 18 - Breakpoint při simulaci
Klávesou F10 lze pokračovat v simulaci. Máme-li více breakpointů, pak se
její běh zastaví na dalším z nich; více viz již zmíněné návody v menu Help.
Program lze také krokovat, k tomu slouží tlačítka na liště nástrojů:
~ . ~
NNaauuččíímmee--llii ssee vvyyuužžíívvaatt MMooddeellSSiimm,, uuššeettřříímmee ssii ččaass,, aa ttoo zzeejjmméénnaa ppřřii ppssaanníí VVHHDDLL ssttyylleemm ""bbeehhaavviioorraall"" bbllíízz--
kkéémmuu kkllaassiicckkýýmm pprrooggrraammůůmm.. BBeezz ppoořřááddnnééhhoo ssiimmuulloovváánníí llzzee pprrooggrraamm llaaddiitt lleeddaa mmeettooddoouu ppookkuuss--oommyyll::
ZnovuALépe: PoEditujeme(); Přeložíme(); NahrajemeDoDE2(); BedlivěZkoumáme();
if("Neběží? Hmmm, zas ne, a tak zas") goto ZnovuALépe;
KKaažžddýý nnáávvrrhh ssee mmuussíí nnaakkoonneecc oovvěěřřiitt ppookkuusseemm nnaa ddeessccee DDEE22,, aabbyycchhoomm ssee uujjiissttiillii,, žžee nnáámm pprraaccuujjee ii vv rreeááll--
nnéémm oobbvvoodduu,, aavvššaakk ddoobbrráá ssiimmuullaaccee zzkkrrááttíí nnaaššii ddoobbuu kkrroouužžeenníí vvee ssmmyyččccee ZnovuALépe ..
~ o ~