Úplné základy a nezbytná teorie

Pokud k VHDL přistupujete se stejnými základy jako mám já, budete mít problém. Pojďme se podívat na nejčastější příčiny nepochopení, které u VHDL hrozí programátorům.

Teď nevím, jestli jsem neřekl už v perexu skoro všechno… Ale trocha teorie nikoho nezabije, pojďme na to.

VHDL patří do rodiny jazyků HDL, což znamená „Hardware Description Language“. Návrháři už v názvu naznačují, že jde o jazyk popisný (description), nikoli programovací (programming) – zdůrazňuju to proto, že programátoři mají tendenci každý „jazyk“ považovat za programovací, a pokud v něm nejde programovat (HTML například), tak prohlásí, že nestojí za nic. V jazycích HDL se moc neprogramuje. A to „V“ znamená VHSIC, což je další akronym z Very High Speed Integrated Circuit, tedy „velmi rychlé integrované obvody“.

VHDL tedy popisuje nějaké vnitřní zapojení integrovaných obvodů (navíc vysokorychlostních, ale to nechme stranou). Není svázané s konkrétním obvodem ani technologií a lze jej použít k popisu digitálních zapojení. Na základě tohoto popisu pak specializované nástroje připraví podklady pro naprogramování CPLD, FPGA nebo třeba vlastního IO.

Jak popsat IO?

Máme několik možností, jak popsat integrovaný obvod ve VHDL. Představme si takový klopný obvod R-S:

Klopný obvod R-S, ilustrace CC-BY, autor Napalm Llama.

V první řadě si ho popíšeme jako „black box“, tedy jako krabičku, která má dva vstupy (R, S) a dva výstupy (Q, /Q). Pro programátory: Tohle je něco jako deklarace. Je to něco, co se jmenuje „klopný obvod R-S“, a nabízí to pro komunikaci se světem tyhle možnosti. Po implementaci nepátráme, ta je někde jinde.

V terminologii VHDL jsme právě popsali entitu pomocí jeho portu. Entita „RS“ má tento port: jednobitový vstup R, jednobitový vstup S, jednobitový výstup Q a jednobitový výstup /Q.

Popis zvenčí bychom měli, ovšem ten nám nic neříká o tom, co se děje uvnitř. Ve VHDL máme rovnou tři možnosti, jak popsat vnitřní strukturu.

1. Strukturní popis

Strukturní popis se zaměřuje na funkční celky a jejich propojení. V tomto případě tedy řekneme, že uvnitř jsou dvě hradla NOR, nazvěme si je G1 a G2, každé hradlo NOR má zase deklarovaný port, řekněme A, B a Y, a zaměříme se na to, jak jsou propojena. Tedy G1 má svůj vstup A připojený na vstup R, vstup B na výstup /Q a svůj výstup Y na výstup Q… a tak dál. (Skutečnost bude o něco málo složitější…)

2. Data flow

Popis „data flow“, neboli toku dat, se nezaměřuje na jednotlivé komponenty, ale na to, jak se obvodem šíří data, respektive „kde se vezme výsledek?“ V tomto případě řekneme, že Q je výsledek funkce NOT (R or /Q), a /Q je NOT (S or Q). A máme to. (Ve skutečnosti to nemáme, protože takhle jednoduché to ve VHDL není, musíme použít jinou techniku, ale princip je tento.)

3. Behaviorální popis

Tento styl popisu se asi nejvíc blíží klasickému programování a lidi, kteří mají zkušenost s programováním, budou mít tendenci vše řešit takto. Což je postup, před kterým varuju rovnou, a ještě tak asi dvacetkrát varovat budu… Behaviorální popis má své nezastupitelné místo a výrazně zjednoduší návrh, na druhou stranu ale není všespásný a nelze si myslet, že „prostě naprogramuju chování obvodu“. V takovém případě jste špatně a hledáte „kurz programování v assembleru„. Ale zpět k behaviorálnímu popisu. V našem případě bychom definovali proces, který se spustí, pokud dojde ke změně na vstupech R nebo S, a vyhodnotí jednoduchou funkci: Pokud R je 1 a S 0, tak Q bude 0, /Q bude 1, jinak pokud R je 0 a S je 1, tak Q=1, /Q=0, jinak pokud jsou oba v nule, tak Q i /Q zůstávají stejné jako předtím, no a pokud jsou oba v jedničce, tak… něco, protože správně to je nedefinovaný stav. V procesu můžete použít právě podmínky, větvení, cykly a další programátorské vymoženosti, ovšem s výraznými omezeními.

V praxi se všechny tři přístupy kombinují podle toho, jak je to pro danou chvíli vhodné. Něco se líp zapíše pomocí propojení vývodů menších celků, něco zase pomocí toku dat, něco je nejlépe popsat procesem.

Chytáky

Asi největší chyták, do kterého se může programátor lapit, je fakt, že „příkazy zapsané pod sebou“ neznamenají, že se provedou „po sobě“. Když si představíte logický obvod, tak tam není nějaké „před“ a „po“, tam se uvažuje s ideálně nulovým zpožděním, takže změna vstupu se okamžitě projeví v celém systému naráz. (Ve skutečnosti ne, protože každý člen má nějaké svoje zpoždění, a při vysokých rychlostech je s ním potřeba počítat, ale pro tuto chvíli zpoždění zanedbejme.) Představa, že nejdřív něco změním, pak se něco provede, pak zase změním něco jinak je ve světě logických obvodů mylná. Ano, sekvenční operace lze udělat, ale musíte si pro ně nejdřív vytvořit stavový automat. Představte si popis architektury (data flow, behaviorální) nikoli jako posloupnost příkazů, které jdou po sobě, ale jako seznam operací, které se provádějí najednou a konkurenčně. Odpovídá to realitě: v obvodech pracují všechny části současně a naráz, není tam nic, co by je postupně přepínalo mezi stavy.

Druhý chyták je syntax. Lehce, ale fakt jen velmi lehce, připomíná Pascal s jeho klíčovými slovy begin a end. Středník někde být musí, někde nesmí, elseif není ani „elseif“, ani „elif“, ani „else if“, ale „elsif“, hodnota proměnné se přiřazuje pomocí := (ale velmi podobné přiřazení signálu se dělá pomocí <=) a celé to je silně typované. Jinak je VHDL tolerantní vůči velkým / malým písmenům, nijak neřeší mezery ani odsazení a je v tom velmi tolerantní.

A ještě takový terminologický detail: Překlad, tedy to co z programování známe jako kompilaci kódu, tu není kompilace, ale syntéza, a neprovádí ji kompilátor, ale syntetizér. On totiž nedělá to, že by kompiloval příkazy, on vytváří (syntetizuje) popsaný obvod…