1
Debreceni Egyetem Informatikai Kara
Végh János Bevezetés a Verilog hardver leíró nyelvbe INCK??? előadási segédlet
[email protected]
Tartalomjegyzék (folyt)
Tartalomjegyzék I. Alapfogalmak 1.. A digitális tervezés 1.1..A terv 1.2..Testbench 1.3..Fordítás és szintézis II. Verilog 1.. Mi az a Verilog? 2.. Absztrakciós szintek 3.. A tervezési folyam 3.1..Be és kimenetek 3.2..Szinkron tervezés 3.3..Időzítési kényszerek 4.. A szintézis veszélyforrásai 5.. Előnyök III. Alapelemek 1.. A ’Hello Világ’ program
Tartalomjegyzék
2
Tartalomjegyzék (folyt)
2.. 3.. 4.. IV. 1.. 2.. 3.. 4.. V. 1.. VI. 1.. VII. 1.. VIII. 1.. 2..
Nyelvi elemek Hierarchia megvalósítása Testbench modul Számok Számok, vezetékek és regiszterek Az idő kezelése További nyelvi elemek Regiszterek és vezetékek Always RTL always utasítás Procedurák Procedurális utasítások Órajel Always latch Operátorok Operátor Paraméterek
Tartalomjegyzék
3
Tartalomjegyzék (folyt)
3.. IX. 1.. X. 1.. A. 1.. 2.. 3.. 4.. 5.. 6.. 7.. 8.. 9.. 10.. 11..
Hierarchia Automaták RTL always utasítás Aritmetika és számlálók Aritmetikai műveletek Nem-blokkoló értékadás Bevezetés Versenyhelyzetek a Verilogban Blokkoló értékadás Nemblokkoló értékadás Verilog kódolási útmutató A Verilog réteges eseménysora Ön-triggerelő always blokkok Csővezeték modellezés Blokkoló értékadás és egyszerű példák Szekvenciális visszacsatolás modellezése Kombinációs logika - használjon blokkoló érték-
Tartalomjegyzék
4
Tartalomjegyzék (folyt)
adást 12.. Kevert logika – használjon nemblokkoló értékadást 13.. Többszörös értékadás ugyanannak a változónak 14.. Városi legendák a nemblokkoló értékadásról
Tartalomjegyzék
5
Tartalomjegyzék (folyt)
6
Alapfogalmak
7
fejezet I: Alapfogalmak A mindenütt jelenlevő digitális rendszereknek nem csak a száma, hanem (elektronikus értelemben) a mérete és összetettsége is folyamatosan nő. Ennek kezelésére viszonylag korán bevezették a számítógéppel segített tervezést (computer-aided design, CAD). A kezdeti primitív szimulációs és hardver generáló segédeszközök mára bonyolult, automatizált eszközkészletté váltak, amelyek verifikálást, magas-szintű szintézist, formális verifikálást, automatizált hardver előállítást és eszköz programozást tesznek lehetővé. Ennek a fejlődésnek nagy része az ún. hardver-leíró nyelveknek (hardware description languages, HDLs) köszönhető: az eszközök működését nagyrészt ezeken a nyelveken tervezik. Bár a mai hardver tervezés már szinte kizárólag HDL alapú, teljes lendülettel folyik jobb és absztraktabb nyelvek és leírási módszerek keresése. Jelenleg az egyik legelterjedtebben használt ilyen hardver leíró nyelv a Verilog. Tervezőmérnökök számára
8
nagyon hasznos, szinte kötelező ismerni. A nyelv ismeretéhez többékevésbé hozzátartozik a nyelven alapuló eszközök és módszerek használata is, így ezekről is szót kell ejteni. Ez a fejezet rövid bevezető a Verilog használatához és használati köréhez, valamint megismerkedünk az RTL szintézis fogalmával.
Szakasz 1: A digitális tervezés
9
1. A digitális tervezés folyamata Egy automatizált tervezési környezetben a tervezés folyamata azzal kezdődik, hogy különböző absztrakciós szinteken specifikáljuk elvárásainkat, és ott végződik, hogy előállítjuk a hálózat listát alkalmazás specifikus áramkörökből (application specific integrated circuits, ASIC), vagy elkészítjük egy felhasználó által definiált integrált áramkör (custom IC) tervét, vagy egy programozható logikai eszköz (programmable logic device, PLD) programját. Az I.1 ábra mutatja a folyamat lépéseit. A tervezés fázisában megadjuk a viselkedést leíró Verilog kódot, a modul példánykészítés módját Verilogban, a buszok és vezetékek leírását. A tervező mérnöknek kell elkészíteni a terv ellenőrzésére, valamint később a szintézis eredményének verifikálására szolgáló ún. testbench-et. A terv verifikálását szimulációval, assertion verification, formal verification útján, de általában a három keverékével, oldhatjuk meg. A terv validálása (ezt hívják szintézis előtti verifikálásnak is (presynthesis verification)) után a szintézis eljárásban a cél eszköz
Szakasz 1: A digitális tervezés
I.1. ábra. Programozható logikák tervezési folyama
c
2006, [1]
10
Szakasz 1: A digitális tervezés
11
tényleges hardverévé lefordítjuk. Itt a "cél eszköz" lehet egy bizonyos, a felhasználó által programozható logikai eszköz (field programmable logic device, FPLD), egy külső forrásból származó ASIC, vagy éppen a felhasználó által tervezett IC. A szintézis folyamata után, de még a tényleges hardver előállítása előtt, egy másik szimulációt (postsynthesis simulation) is végzünk. Ebben a szimulációban ugyanazt a testbench-et használjuk, mint amit a szintézis előtti szimulálás során. Ilyen módon a terv viselkedési modelljét és a hardver modelljét ugyanazokkal az adatokkal vizsgáljuk. A kétféle szimuláció között csak részletességben van különbség. Ejtsünk pár szót az I.1 ábra blokkjairól, amelyeket a legtöbb Verilog alapú EDA környezet támogat.
1.1. A terv elkészítése • A tervezés első lépése a terv elkészítése. Ebben a fázisban a tervet felülről lefelé haladva (top-down hierarchical fashion) írjuk le, Verilog nyelven. A teljes terv állhat kapu vagy
Szakasz 1: A digitális tervezés
12
tranzisztor-szintű komponensekből, a hardver modul magas szintű funkcionalitását leíró viselkedési részekből, vagy a sínrendszer szerkezetét leíró komponensekből. • Mivel a magas szintű Verilog terveket általában olyan szinten írjuk le, amelyek rendszer regisztereket és a busz használatával azok közötti átvitelt írnak le, ezt a rendszer leírási szintet regiszter átviteli szintnek (register transfer level, RTL) nevezik. Ennek a szintnek pontos hardver megfelelői vannak, pl. procedurális utasítások, folytonos értékadás és példányosító utasítások. • A Verilog procedurális utasításait használjuk magas szintű viselkedés leírására. Egy rendszert vagy komponenst procedurális módon ahhoz hasonlóan írunk le, ahogyan eljárásokat használunk egy szoftver nyelvben. Például, egy komponenst úgy írhatunk le, hogy megadjuk a bemenő feltételeit, a jelzőbit beállításokat, milyen eseményekre vár, hogyan monitorozhatjuk a kézfogó jeleket, és milyen kimeneteket produkál. Egy rendszer procedurális leírásához a Verilog if-else, case és hasonló, szoftver-nyelv szerű konstrukciókat használhatunk.
Szakasz 1: A digitális tervezés
13
• A Verilog folytonos értékadások olyan utasítások, amelyek logikai blokkokat, buszokat és ki/beviteli összekötési specifikációkat tartalmaznak. Ezeket a nyelvi konstrukciókat logikai műveletekkel és feltételekkel kombinálva, komponenseket és rendszereket regiszter és busz fogalmakkal írnak le. • A Verilog példánykészítő utasításait használhatjuk arra, hogy alacsonyabb szintű komponenseket használjunk magasabb szintű tervben. A Verilogban alacsonyabb szintű komponensekkel is leírhatjuk a rendszert, ahelyett, hogy viselkedési, funkcionális vagy összekötési specifikációt használnánk. Az ilyen komponensek skálája a kaputól vagy tranzisztortól a teljes processzorig terjedhet.
1.2. Testbench a Verilogban Egy Verilog nyelven megtervezett rendszer funkcionalitását még azelőtt szimulációval tesztelni kell, hogy a tényleges hardvert előállítanánk. A szimuláció ezen fázisában megkereshetők a tervezési hibák
Szakasz 1: A digitális tervezés
14
és a komponensek esetleges inkompatibilitása. A terv szimulációjához teszt adatok és a szimuláció eredményeinek megfigyelése szükséges. Ezt a folyamatot egy speciális Verilog modullal, a ’testbench’ modullal hajthatjuk végre. Egy Verilog testbench a nyelv magas szintű konstrukcióit használja adatok előállítására és a válasz monitorozására, sőt még a jelek kézfogásának tervezésére is. A testbenchben megtalálható a szimulált terv modul egy példánya. A Verilog szimulációs motorja együtt használja a testbench-et és a tervet. A különféle tervezési hibákat (hibás tervezési specifikáció, tervezői hiba, a részek helytelen használata, stb.) a terv validálásával találhatjuk meg. A terv szimulációval történő validálása a szintézis lépés előtt kerül sorra. Ezt a szimulációs fázist viselkedési, RT-szintű és szintézis előtti szimulációnak is nevezik. A terv RT szintjén már van órajel, de még nincsenek benne kapu és vezeték késleltetések. Ezen a szinten a szimuláció csak az órajelek szintjéig pontos, azaz nem kezeli a hazard szituációkat, versenyhelyzeteket, beállási és tartási problémákat, "szikrázásokat", és hasonló időzítési problémákat. Ennek a szimulációs típusnak fő előnye a sebessége, a tranzisztor vagy kapu szintű
Szakasz 1: A digitális tervezés
15
I.2. ábra. Szimuláció testbench és hullámforma szerkesztő használatával
c
2006, [1]
Szakasz 1: A digitális tervezés
16
szimulációkhoz képest. Egy terv szimulálásához teszt adatok szükségesek, és általában a Verilog szimulációs környezetek különböző módszereket biztosítanak arra, hogy ezeket az adatokat a vizsgálandó tervre alkalmazzuk. A teszt adatokat előállíthatjuk grafikusan hullámforma szerkesztőkkel, vagy testbench használatával. Az I.2 ábra mutatja, hogyan állíthatunk elő teszt adatokat a szimulációs motor számára. A szimuláció kimenetei vagy hullámformák (vizuális analízis céljára) vagy (főként nagyobb tervek esetén) szöveg, további gépi feldolgozásra. Amikor egy Verilog testbench használatával tesztelünk, a testbench a tervből készít egy példányt és a testbench kód részeként teszt adatokat alkalmaz a készített terv példányra. Az I.3 ábra egy egyszerű számláló áramkört mutat, a hozzá tartozó testbench mellett, valamint a hullám formában megjelenített szimulációs eredményeket. Mint látható, a szimuláció validálja a számlálókör funkcionalitását: a számláló értéke minden egyes órajel impulzus hatására eggyel nő. Vegyük észre, hogy a számláló kimeneti értéke az órajel felfutó élével változik, nincs kapu késleltetés és terjedési idő. A szimuláció szerint a
Szakasz 1: A digitális tervezés
I.3. ábra. Verilog Szimuláció testbench használatával
c
2006, [1]
17
Szakasz 1: A digitális tervezés
18
működés helyes, az órajel frekvenciától függetlenül. A tényleges hardver komponsek nyilvánvalóan kicsit eltérően viselkednek. A használt alkatrészek időzítésétől és késleltetésétől függően, nem-nulla késleltetés lép fel az órajel aktív éle és a számláló kimenete között. Ezen túlmenően, ha az órajel frekvencia túl gyors a terv kapui és tranzisztorai közötti adat továbbításhoz képest, az eredmény megjósolhatatlan. Az itt bemutatott szimuláció nem rendelkezik a szimulált hardver időzítésének részleteivel. Emiatt a kapu késleltetések miatti esetleges hardver időzítési problémákat sem tudjuk észlelni. Ez egy tipikus szintézis előtti, avagy magas szintű viselkedési szimuláció. Amit ellenőrizni tudunk, hogy számlálónk valóban binárisan számlál. Hogy a számláló milyen gyorsan tud működni és milyen órajel sebesség szükséges hozzá, azt csak azután vizsgálhatjuk, miután a tervet szintetizáltuk. Ahelyett, hogy manuálisan megvizsgáljuk az eredményeket és bonyolult testbench fejlesztést végzünk, megkövetelhetjük bizonyos feltételek fennállását, és azok teljesülésének ellenőrzését a szimulációs motorra bízzuk. Ennek érdekében magába tervbe ún. "assertion
Szakasz 1: A digitális tervezés
19
monitor" (követelmény teljesítés ellenőrzése) pontokat teszünk. A tervező elvárása, hogy ha a terv megfelelően működik, akkor bizonyos feltételeknek teljesülniük kell. Ezeket a feltételeket a terv lényegi tulajdonságainak tekintjük, és a tervező ilyen "assertion monitor"okat fejleszt ki annak ellenőrzésére, hogy ezek a tulajdonságok nem sérülnek. Ezek a monitorok akkor lépnek működésbe, amikor az elvárt működés sérül, ezáltal figyelmezteti a tervezőt, hogy a terv nem az elvárások szerint működik. Egy speciális könyvtár, az ’Open verification library’ (OVL) számos ilyen monitort tartalmaz, amellyel a szokásos tervezési elvárásokat monitorozhatjuk. Emellett a tervező saját ilyen monitorokat is fejleszthet és használhat a testbench működtetése során. A formális verifikálás az a folyamat, amikor azt vizsgáljuk, hogy a terv rendelkezik-e bizonyos tulajdonságokkal. Amikor egy tervet befejezünk, a terv rendelkezik egy sor olyan tulajdonsággal, amelyek a helyes viselkedést tükrözik. Egy olyan eszköz, amelyik a formális verifikálást végzi, azt ellenőrzi, hogy a megadott tulajdonság minden körülmények között teljesül. Amennyiben olyan helyzetet találunk,
Szakasz 1: A digitális tervezés
20
ahol a tulajdonság nem teljesül, akkor a tulajdonság sérül. Az ilyen helyzetet ellenpéldának tekintjük. A lefedettséggel azt adjuk meg, hogy a tulajdonság vizsgálatával a teljes terv milyen részét tudjuk vizsgálni.
1.3. Fordítás és szintézis A szintézis a terv leírásból történő automatikus hardver generálás azon folyamata, amelyiknek egyértelmű hardver megfelelője van. A szintézisre szánt Verilog leírás nem tartalmazhat jel és kapu szintű időzítési specifikációt, fájl kezelést, és semmi olyan nyelvi konstrukciót, ami nem fordítható le szekvenciális vagy kombinációs logikai egyenletekké. Továbbá, a szintézisre szánt Verilog leírásnak bizonyos kódolási stílust kell követnie. A tervezési folyamatban, miután a tervet a szintézis előtti szimulációval sikerrel ellenőriztük, azt le kell fordítani, hogy egy lépéssel közelebb kerüljünk a tényleges hardverhez. Ehhez a tervezési folyamathoz a tényleges cél-hardver specifikációja is szükséges.
Szakasz 1: A digitális tervezés
I.4. ábra. A fordítás és szintézis folyamata
c
2006, [1]
21
Szakasz 1: A digitális tervezés
22
Megadhatunk például egy bizonyos ASIC-et vagy egy programozható kaputömböt (field programmable gate array, FPGA) cél-hardverként. Amikor kiválasztjuk a cél-hardvert, annak technológiai fájljai (a részletes időzítési és funkcionális adatokkal) elérhetővé válnak a fordítási folyamat számára. A fordítási folyamat a terv különféle részeit egy közbülső formátumra fordítja (analízis fázis), összecsatolja valamennyi részt, előállítja a megfelelő logikát (szintézis fázis), elhelyezi a cél-hardver komponenseit és megtervezi az azokat összekötő útvonalakat, valamint előállítja az időzítés részleteit. Az I.4 ábra mutatja a fordítási folyamatot és az egyes fordítási fázisok eredményének grafikus ábrázolását. Mint látható, ennek a fázisnak a bemenete egy, különböző Verilog szinteket tartalmazó hardver leírás, a kimenete pedig egy olyan részletes hardver, amit FPLD programozásra vagy ASIC gyártásra használhatunk. A fordítás fázisai: • Analízis Egy Verilogban megírt teljes terv állhat viselkedési Verilog leírásból, busz- és összekötés specifikációból, és egyéb
Szakasz 1: A digitális tervezés
23
Verilog komponensekből. Mielőtt a teljes tervet hardverré fordítanánk, azt analizálni kell, és annak valamennyi részét egységes formátumúvá tenni. Ez a fázis a Verilog kód szintaxisát és szemantikáját is megvizsgálja. • Generikus hardver előállítás A terv egységes formátumúvá alakítása után a szintézis a tervet generikus hardver formátumúvá alakítja, ami logikai kifejezéseket és alapkapuk hálózatát jelenti. • Logikai optimalizálás Miután a tervet logikai kifejezésekké alakítottuk, a szintézis következő fázisa a logikai optimalizálás. Ez a fázis felelős a konstans bemenetű kifejezések redukálásáért, a felesleges logikai kifejezések eltávolításáért, a logikai megosztást tartalmazó kétszintű és többszintű minimalizálásért. Ez nagyon számítás intenzív folyamat, és néhány segédprogram megengedi, hogy a felhasználó döntsön az optimalizálás szintjéről. Ennek a fázisnak a kimenete logikai függvények, táblázatos logikai megjelenítés, vagy primitív kapu hálózatok. • Csatolás A logikai optimalizálás után a szintézis folyamat a
Szakasz 1: A digitális tervezés
24
cél-hardverről kapott információt használja arra, hogy eldöntse, melyik logikai elemek és cellák szükségesek a terv megvalósításához. Ez a folyamat a csatolás és ennek kimenete már FPLD, ASIC vagy felhasználói IC specifikus. • Útvonal tervezés és elhelyezés Az útvonal tervezés és elhelyezés fázisban dől el a cellák helye a cél hardverben. Ezen cellák kimenő és bemenő vezetékezését a cél hardver vezeték csatornái és kapcsoló területei határozzák meg. Ennek a fázisnak a kimenete a használt hardverre jellemző és FPLD programozásra és ASIC gyártásra is használható. Egy szintézis példát mutat az I.5 ábra, ahol a I.3 ábrán bemutatott szimulációban szereplő számláló áramkört szimuláljuk. A terv Verilog kódján túl a szintézis eszköznek szüksége van a cél hardver specifikációjára is. A szintézis segédprogram kimenete a cél hardverben elérhető kapuk és flipflopok listája, valamint azok összekötései. A szintézis eszköz által automatikusan előállított grafikus ábrázolás is látható a I.5 ábrán. (Altera’s Quartus II) • Szintézis utáni szimuláció Amikor a szintézis elkészült,
Szakasz 1: A digitális tervezés
25
a szintézis segédprogram előállítja a cél-hardver komponensei teljes hálózati listáját és időzítéseiket. Ebben a listában találjuk meg az implementációban használt kapuk részleteit. Ugyanez a lista tartalmazza a vezetékezési késleltetéseket is. A hálózat lista különféle formátumokban érhető el. Ez a leírás szimulálható, és azt nevezik szintézis utáni szimulációnak. Az időzítési problémák, a megfelelő órajelfrekvencia valamint a verseny és a hazard szituációk csak a szintézis utáni szimulációban vizsgálhatók. Amint az I.1 ábrán látható, a szintézis előtti és utáni szimuláláshoz ugyanaz a testbench használható. A vezetékek és kapuk késleltetése miatt előfordulhat, hogy a tervező által várt és a szintézis utáni viselkedés eltér. Ebben az esetben a tervezőnek módosítania kell a tervet, hogy elkerülje a szoros időzítéseket és a verseny helyzeteket. • Időzítés vizsgálat Amint az I.1 ábrán látható, a fordítási folyamat részeként, vagy bizonyos eszközökben a fordítási folyamat után, egy időzítés vizsgálat (Timing analyzis) is helyet kap. Ez állítja elő a legrosszabb esetre vonatkozó késleltetést,
Szakasz 1: A digitális tervezés
26
az órajel sebességet, a kapuk közötti késleltetést, valamint a beállási és tartási időket. Ennek a vizsgálatnak az eredménye táblázatokban és/vagy grafikonokban jelenik meg. A tervezők az áramkör sebességének becslésére használják az adatokat. • Hardver előállítás A Verilog alapú automatizált tervezés utolsó fázisa a hardver előállítás. Ez a fázis állítja elő a hálózatlistát ASIC gyártáshoz, az FPLD-hez a programot, vagy a felhasználói IC elrendezését.
Szakasz 1: A digitális tervezés
I.5. ábra. Példa a szintézisre
c
2006, [1]
27
A Verilog nyelvről
fejezet II: A Verilog nyelvről
28
Szakasz 1: Mi az a Verilog?
29
1. Mi az a Verilog? Megjegyzés: A Verilog nem C Vigyázzunk vele: szintaxisa látszólag nagyon hasonlít a C nyelvre, de szemantikája nagyon eltér. A Verilog egy hardver leíró nyelv (Hardware Description Language); elektronikus áramkörök és rendszerek szöveges leírására szolgál. Elektronikus áramkörök tervezésére használják; szimulációval való verifikálásra, időzítés vizsgálatra, teszt jellegű analízisre, és logikai szintézisre. • 1984 - Gateway Design Automation bevezeti Verilog-XL rendszert • 1989 - Cadence Design Systems megvásárolja a Gateway-t • 1990 - A Verilog HDL valamennyi jogát átruházzák az Open Verilog International-re (OVI, ma Accellera)
Szakasz 1: Mi az a Verilog?
II.1. ábra. A Verilog története.
c
2001, Doulos [2]
30
Szakasz 1: Mi az a Verilog?
Megjegyzés: A Verilog HDL IEEE sztenderd, mégpedig a 1364 számú. A sztenderd dokumentum maga a Language Reference Manual (LRM); ezt tekintjük a Verilog HDL ősforrásának és hiteles definíciójának. Viszont, nem biztos, hogy abból érdemes megtanulni. Az IEEE 1364 sztenderd egy programozási interfészt is definiál (Programming Language Interface, PLI). Ez lényegében szoftver rutinok gyűjteménye, amelyek egy kétirányú interfészt biztosítnak más nyelvekhez (általában a C-hez). Ez a PLI teszi lehetővé, hogy a Verilog nyelvet és a Verilog segédprogramokat (szimulátor) testre szabjuk.
• 1995 - Az IEEE Standard 1364 közzététele • 2001- "Verilog 2001" • A jövő - Verilog-AMS, SystemVerilog
31
Szakasz 1: Mi az a Verilog?
32
• Analog and Mixed-Signal Extension to Verilog A Verilog HDL története az 1980-as évekre nyúlik vissza, amikor egy Gateway Design Automation nevű cég kifejlesztett egy Verilog-XL nevű logikai szimulátort, és mellesleg azzal együtt egy hardver leíró nyelvet. Tehát eredetileg a Verilog szimuláció leírására és végzésére szolgált, a szintézis támogatása későbbi melléktermék. A Cadence Design Systems 1989-ben felvásárolta a Gateway-t, és azzal együtt a nyelvhez és a szimulátorhoz fűződő jogait. 1990-ben a Cadence a nyelvet (a szimulátort nem!) közkinccsé tette, azzal a szándékkal, hogy az egy sztenderd, nem céghez kötődő nyelv legyen. Az erre a célra létrehozott nem profit orientált szervezet, az Open Verilog International (OVI) végezte el a Verilog nyelv IEEE sztenderdizálási eljárását. 1995-ben (főként egy másik HDL, a VHDL nyelv növekvő sikerének hatására) lett a Verilog HDL IEEE 1364 sztenderd (Verilog-95). Az OVI 2000-ben egyesült a VHDL International-lel Accelera néven, és a továbbiakban a jövőbeli Verilog sztenderdeket koordinálja.
Szakasz 1: Mi az a Verilog?
33
A felhasználók által igényelt javítások után lett az új sztenderd 1364-2001 (Verilog 2001) Ez komoly változtatásokat és javításokat javasolt magához a Verilog nyelvhez és a PLI-hez. Ezzel egyidejűleg folyt a Verilog-AMS (analog and mixed signal extensions) fejlesztése is. A Verilog-2001 a Verilog-95 lényeges frissítését jelentette. Először is, explicit módon támogatja a (kettes komplemens) előjeles hálózatokat és változókat. Ezt megelőzően a kód íróknak figyelmes bit-manipulációkkal kellett előjeles műveleteket végezni (például, egy egyszerű 8-bites összeadásból származó átvitel bit helyes meghatározásához explicit Boole-algebrai leírás volt szükséges). Ugyanaz a függvény Verilog-2001 alatt sokkal tömörebben és egyértelműbben leírható a +, -, /, *, >>> operátorok valamelyikével. A generate/endgenerate konstrukció (A VHDL hasonló konstrukciójával megegyezően) lehetővé teszi, hogy a Verilog-2001 normál vezérlési operátorokkal (case/if/else) vezérelje példányok és utasítások létrehozását. Ezzel a konstrukcióval a Verilog-2001 úgy tud példányok tömbjének példányát létrehozni, hogy közben vezérelni
Szakasz 1: Mi az a Verilog?
34
tudja az egyes példányok közötti kapcsolatot. A fájl I/O-t számos új rendszer taszk segíti. Végül pedig pár szintaxis javítást is bevezettek, hogy a kód olvashatóságát javítsák (például always *, nevesített paraméter átértelmezés, C-stílusú függvény/task/modul fejzet deklaráció). A Verilog 2005 (IEEE Standard 1364-2005) ehhez képest csak apróbb korrekciókat tartalmaz, pontosításokat és pár új nyelvi tulajdonságot (mint az uwire kulcsszó) tartalmaz. Manapság a legtöbb fejlesztőrendszer a Verilog-2001-en alapszik. (A SystemVerilog is egy Verilog-2005 bővítmény, amely sok új tulajdonságot és a verifikálást/tervezést segítő tulajdonságot tartalmaz.) Ha csak külön nem jelezzük, a Verilog és a Verilog HDL a továbbiakban a nyelvre és nem a szimulátorra vonatkozik. A Verilog HDL digitális elektronikus hardver specifikálására, tervezésére és leírására szánt nyelv. • Rendszer szint A Verilog nem ideális a hardver-szoftver elkülönítés elvégzése előtti absztrakt rendszer-szintű szimulációra. A szimuláció ezen a szinten általában sztochasztikus,
Szakasz 1: Mi az a Verilog?
II.2. ábra. A Verilog használati köre
c
2001, Doulos [2]
35
Szakasz 1: Mi az a Verilog?
36
a modellezés hatékonyságát, átbocsátóképességét, sorbanállást és a statisztikus eloszlásokat veszik figyelembe. A Verilognak van pár olyan rendszertaszkja és függvénye, amely támogatja a sztochasztikus modellezést és némi sikereket is ért el ezen a téren. • Digitális a Verilog manapság a digitális hardver tervezési folyamatban sok mindenre használható, a specifikációtól a magas szintű funkcionális szimulációig, manuális tervezésre, logikai szintézistől a kapu-szintű szimulációig. A Verilog eszközök ezen a területen egy integrált tervezési környezetet biztosítanak. A Verilog használható részletes, implementáció szintű modellezésre, beleértve a kapcsolóelem-szintű szimulálást, időzítés analízist, és hiba szimulációt. • Analóg A Verilog-AMS nyelv lehetővé teszi kevert (analóg és digitális) valamint tisztán analóg modellezést és szimulációt. • Tervezési folyamat Az II.2 diagram az elektronikus tervező rendszer egy erősen egyszerűsített diagramját mutatja, a Verilog helyének megjelölésével. Az ábra középső része mutatja a
Szakasz 1: Mi az a Verilog?
37
tervezési folyamat fő részeit, ahol a Verilog jelentős befolyásoló szerepet tölt be.
Szakasz 2: Absztrakciós szintek
38
2. Absztrakciós szintek Az II.3 ábra összefoglalja egy ASIC (azaz kaputömb, sztenderd cella tömb vagy FPGA) magas szintű tervezési folyamatát. A gyakorlatban az egyes bemutatott lépések kisebb lépésekre bonthatók, így elfedik a tervezés kisebb hibajavításaiban rejlő iterációkat. Első lépésként a Verilog-ot használjuk az egy vagy több ASICból álló teljes rendszerek modellezésére és szimulálására. Ez lehet a rendszer teljes funkcionális leírása, amely lehetővé teszi, hogy az ASIC/FPGA specifikációját még azelőtt validáljuk, mielőtt a részletes tervezést elkezdenénk. A másik lehetőség, hogy csak egy részleges leírásunk van, ami a rendszer bizonyos tulajdonságait absztrahálja, például egy hatékonysági modell, amit a hatékonyság szűk keresztmetszeteinek felderítésére használunk. Amikor a rendszer architektúrája és felosztása stabilizálódott, kezdődhet az egyes ASIC/FPGA elemek megtervezése, Verilogban, RTL szinten, és tartalmazza Verilog teszt esetek megírását is. Ez a két feladat kiegészíti egymást, és gyakran két különböző csapat végzi, annak biztosítására, hogy a specifikációt
Szakasz 2: Absztrakciós szintek
II.3. ábra. Absztrakciós szintek
c
2001, Doulos [2]
39
Szakasz 2: Absztrakciós szintek
40
helyesen értelmezik-e. Az RTL Verilog-nak szintetizálhatónak kell lenni, ha automatikus logikai szintézist akarunk használni. A teszt esetek előállítása nagyon fontos feladat, rendszerességet és biztos mérnöki tudást követel: a végső ASIC minősége nagyon függ attól, hogy a teszt esetek mennyire fedik le a tervet. Ezután az RTL Verilog-ot szimuláljuk, hogy validáljuk a terv specifikációnak megfelelő funkcionalitását. Az RTL szimuláció általában egy-két nagyságrenddel gyorsabb, mint a kapu szintű szimuláció, és a tapasztalatok azt mutatják, hogy ez a gyorsaság úgy használható ki jobban, ha több szimulációt végzünk, és nem kevesebb időt töltünk vele. A gyakorlatban elég általános, hogy az ASIC/FPGA tervezési ciklusában az idő 70-80%-át az RTL szintű kódírással és szimulálással töltik, és csak 20-30%-át a kapuk szintetizálásával és verifikálásával. Bár valamennyi felderítő jellegű szintézist általában végeznek a tervezési folyamat elején, az architekturális döntésekhez és a Verilog szintézis mérnöki megértésének vizsgálatához szükséges pontos
Szakasz 2: Absztrakciós szintek
41
sebesség és terület adatok származtatása (a tényleges produkciós szintézis) általában csak akkor áll elő, amikor a funkcionális szimuláció befejeződik. Egyszerűen értelmetlen addig időt és energiát fektetni a szintézisbe, amíg a terv funkcionalitását nem ellenőriztük.
Szakasz 3: A tervezési folyam
42
3. A tervezési folyam 3.1. A szintézis eszköz be- és kimenetei A szintézisre fókuszálva, a II.5 ábra azt mutatja be, hogy egy tipikus RTL szintézis eszközhöz milyen be-és kimenetek tartoznak. Bemenetek A szintézis eszköz bemeneteihez nem csak a Verilog forráskód tartozik, hanem a tervezési kényszerek, a határfeltételek valamint a szintézis paraméterei és vezérlése is. A tervezési kényszerek tartalmazhatják a maximális órajel frekvenciát és a maximális kapuszámot, a határfeltételek tartalmazhatják a kimeneti tüskékre adható kapacitív terhelést, és a szintézis paraméterei tartalmazhatják az optimalizálásra fordítható CPU időt. Kimenetek A szintézis eszköz kimeneteihez nem csak szintetizált terv (általában egy hálózatlista formájában) tartozik, hanem a szintetizált áramkör is, meg egy összefoglaló jelentés, aminek alapján a terv minőségére következtethetünk. Egy
Szakasz 3: A tervezési folyam
II.4. ábra. A tervezési folyam
c
2001, Doulos [2]
43
Szakasz 3: A tervezési folyam
II.5. ábra. A tervezési folyam
c
2001, Doulos [2]
44
Szakasz 3: A tervezési folyam
45
teszt szintézis eszköz automatikusan elkészíti a gyártáshoz szükséges teszt vektorokat is.
3.2. Szinkron tervezés • Minden tárolás flip-flopokban történik, egyetlen külső órajellel. Az RTL szintézis, a tervező lánc többi tagjához hasonlóan, szinkron tervezést tételez fel. Számos, a szintézis alatt és után gyakran előforduló probléma, különösen az időzítési gondok, arra vezethetők vissza, hogy nem-szikron tervezési módszereket használtunk a Verilog terv készítésekor. Emiatt a szinkron tervezési módszerek megértése a sikeres RTL szintézis fontos alapköve. Egy tisztán szinkron terv tárolóelemeket (regisztereket) tartalmaz, amelyeket egy órajel éle vagy egy kombinációs logika triggerel. Mindent él-vezérelt flipflopokban tárolunk, amelyeket ugyanaz az órajel hajt meg. Valamennyi flip-flopban (a pipeline regisztereket kivéve) van reset jel.
Szakasz 3: A tervezési folyam
46
• Nincsenek kombinációs visszacsatolási hurkok Kombinációs áramkörnek azt a digitális áramkört nevezzük, amelynek a pillanatnyi kimeneti állapot jellemzői a bemeneteinek pillanatnyi állapotától, és csak attól, függenek. A logikában bármiféle hurok (visszacsatolás) regisztereken keresztül történhet.
3.3. Időzítési kényszerek A szintézis eszközök olyan módon alakítják a Verilog leírást kapukká, hogy az átalakítás megőrzi a terv funkcionalitását. Ennek azonban csak akkor van gyakorlati jelentősége, ha a terv időzítése helyes, a szintézis pedig csak a szinkron terv funkcionalitását őrzi meg. A szintézis úgy őrzi meg a terv órajel szintű viselkedését, hogy optimalizálja a regiszterek közötti kombinációs logikát, hogy elérje a célul kitűzött órajel periódust. Az adat érkezési idejét úgy számolja, hogy megvizsgálja az összes lehetséges útvonalat a regiszterek között; az órajel érkezését az óra útvonalának követésével, a periódusidőt
Szakasz 3: A tervezési folyam
II.6. ábra. Szinkron tervezés
c
2001, Doulos [2]
47
Szakasz 3: A tervezési folyam
II.7. ábra. Időzítési kényszerek
c
2001, Doulos [2]
48
Szakasz 3: A tervezési folyam
49
független tervezési kényszernek tekinti, az összeállítást pedig a technológiai könyvtár definiálja. A tervezőnek kell számba vennie az elsődleges bemenetekre érkező külső jelek időzítési viszonyait, hogy azok ne sértsék a beállási és tartási időket, számításba véve az I/O padok által okozott és a tok belsejébe hatoláshoz szükséges időt. Sajnálatos, de nem lehetünk biztosak abban, hogy megvalósított tervünk a tényleges környezetben is működni fog, pusztán azért, mert a szintézis eszköz szerint az időzítés rendben van.
Szakasz 4: A szintézis veszélyforrásai
50
4. A szintézis veszélyforrásai A szintézis nem csodaszer! Fontos, hogy mindenkinek, aki hozzákezd egy projekthez, reális elvárásai legyenek a szintézissel kapcsolatban. Pár megjegyzés alább. • A szintézis eredménye nagyon függ attól, hogy a Verilog kód milyen stílusban van megírva. – Meghatározott stílusra (style guide) van szükség. Nem elegendő, hogy a Verilog kód funkcionálisan helyes: olyan módon kell azt megírni, hogy a szintézis eszközt jó hardver készítésére irányítsa; ezen túlmenően a Verilog kódnak meg kell felelnie az illető szintézis eszköz sajátságainak. • A jó stílus azonban a tervező felelőssége. Az RTL szintézis során a tervezőnek biztos kézzel kell irányítani a tervezést! A tervezési döntések nagy részét az ember hozza, a szintézis eszköz automatizálja a kapu-szintű megvalósítást. – A szintézis eszközzel a rossz tervező még mindig rossz tervet készít, csak gyorsabban! Ha szemét a bemenet, szemét a
Szakasz 4: A szintézis veszélyforrásai
51
kimenet is • A szintézis nem helyettesíti az emberi tudást és tapasztalatot. A tervezési gyakorlatban a szintézis olyan eredményt ad, amelyik megegyezik az emberi tervezőével vagy annál jobb. – A szintézis jó eredményeket adhat, rövid idő alatt – "kézi munka" szükséges ahhoz, hogy gyors vagy sűrű tervet tudjunk készíteni. Azokban a tervekben, amelyek a technológia gyorsasági vagy sűrűségi határait feszegetik, a szintézis magában gyakran nem elég jó, kézi beavatkozás is szükséges.
Szakasz 5: Előnyök
52
5. Előnyök • Végrehajtható specifikáció. Gyakran találkozunk olyan ASIC tervekkel, amelyek kezdetben megfelelni látszanak specifikációjuknak, de a rendszerbe beépítve nem működnek. A Verilog kétféle módon is tudja kezelni ezt a problémát: egy Verilog specifikáció már a tervezés megvalósítása előtt végrehajtható, hogy nagyobb megbízhatósági szintet érhessünk el, és a terv egy-két nagyságrenddel gyorsabban szimulálható, mint a kapu szintű leírás. Egy rész Verilog specifikációja képezheti az alapját annak a szimulációs modellnek, ami az illető részt egy nagyobb rendszer részeként szimulál (például, nyomtatott áramköri lap szimuláció). Ez attól függ, milyen pontosan kezeli a specifikáció az időzítés és inicializálás vonatkozásait. – A specifikációt a rendszerrel összefüggésben is validálni kell • Funkcionálisan el van választva az implementációtól. A magas szintű tervezési módszer használatával a terv funkcionalitása még a terv részletekbe menő megvalósítása előtt validálható.
Szakasz 5: Előnyök
53
A korai szimulálás lehetővé teszi, hogy korábban megtaláljuk a tervezési folyamatban rejlő hibákat, amikor azokat még egyszerűbb és olcsóbb javítani. – Szimulálj gyakran és gyorsan. Figyelj a komplexitásra • Találd meg a tervezési alternatívákat. A viselkedési és RTL szintézis eszközök használatával lehetővé válik a tervezési tér nagyobb részének megismerése olyan módon, hogy több alternatív megvalósítást próbálunk ki és a legjobbat választjuk. – Legyen visszacsatolás. Készíts jobb terveket • Automatikus szintézis és teszt generálás. A logikai áramkörök és a gyártási teszt vektorok automatikus generálása rövidítheti a tervezési ciklust. Ettől függetlenül, a rövidebb piacra kerülési idő elérésének legfontosabb komponense, hogy a Verilog tervek minél nagyobb része legyen kezdettől fogva helyes, elkerülendő a költséges újraindulásokat. – Növeld a termelékenységet. Kerülj rövidebb idő alatt a piacra
Szakasz 5: Előnyök
54
• Figyelj a beruházásodra. Sok cég úgy igyekszik anyagi érdekeit védelmezni, hogy eszköz és technológia függetlenséget remél a Verilog használatától. Lásd alább. • Függetlenség a szimulációs eszköztől – Igen! (A nem-determinisztikus viselkedés ellenére) • Függetlenség a szintézis eszköztől – Nem! – Különböző alrendszerek, kódolási stílusok és optimalizáció – Javasolt IEEE Std 1364.1 • Eszközök A Verilog hardver tervek és teszt fixtúrák általában hordozhatók a tervező eszközök között, a tervező központok között és a projekt partnerek között. Bár a Verilog nyelv eredendően nem-determinisztikus (különböző szimulátorok elérő eredményre vezetnek bizonyos modellek esetén), nem szabad nem-determinisztikus kódot írni. Tehát biztosan számíthat arra, hogy a Verilog tanulás eredményeként nem fog valamely gyártó
Szakasz 5: Előnyök
55
egy eszközéhez kötődni, hanem válogathat az eszközök és platformok között. Emellett a különböző eszközök gyártói arra törekszenek, hogy minél jobb Verilog támogatást nyújtsanak eszközeiken. A fentiek igazak szimuláció esetén, azonban a szintézis eszközök másik történet: nincs egységes alrendszer vagy Verilog értelmezés szintézis esetén. Van egy javasolt IEEE sztenderd 1364.1, ami biztosít egy sztenderd Verilog alrendszert, de annak megvalósítása is időbe telik. • Függetlenség az ASIC technológiától – Igen! • Függetlenség az FPGA/CPLD technológiától – Nem! – A generikus kód rossz terület kihasználást és sebességet eredményez – Meg kell érteni az eszköz architektúráját • Technológia
Szakasz 5: Előnyök
56
A Verilog olyan módon teszi lehetővé a technológia fügetlen tervezést, hogy támogatja a felülről lefelé történő tervezést és logikai szintézist. Egy új technológiára való áttéréshez nem kell mindent elölről kezdeni vagy a specifikációt visszafejteni, ehelyett csak vissza kell térni a Verilog viselkedési leíráshoz, és azt az új technológiára implementálni: biztos lehet benne, hogy a helyes működés megmarad. Ez az eljárás követhető, pl. amikor egy FPGA tervet újra implementál ASIC gyanánt. A Verilog nagyon robosztusnak bizonyult abban, hogy terveket vigyünk át különböző FPGA technológiák között, de kevésbé sikeres FPGA és CPLD között. Az a generikus Verilog kódolási stílus, ami jól működik ASIC esetén, nem eredményez jó eszköz kihasználást és sebességet, amikor a cél programozható logikai eszköz. Hogy a legjobb eredményt érjük el programozható logikák esetén, alaposan meg kell érteni a cél eszköz architekturális tulajdonságait, és azokat be kell építeni a Verilog tervbe.
Szakasz 5: Előnyök
57
Alapelemek
58
fejezet III: Alapelemek Ebben a szakaszban megismerjük mindazokat a nélkülözhetetlen Verilog nyelvi alapelemeket, amelyek feltétlenül szükségesek ahhoz, hogy a legegyszerűbb feladatot meg tudjuk írni (és érteni).
Szakasz 1: A ’Hello Világ’ program
59
1. A ’Hello Világ’ program Programlista III.1: A ’Hello Világ’ program (Nem minden platformon működik ebben az egyszerű alakban) module main ; initial begin $display ( "Hello world ! " ) ; $finish ; end endmodule
Nos, végül is programnyelvet tanulunk, így hát lássuk az elején, milyen lesz a minden programnyelven megszokott ’Hello Világ’ program, amelyet ezúttal Verilog nyelven írunk meg. Ha eléggé távolról nézünk rá a III.1 programlistára, észleljük a távoli rokonságot a C nyelvvel: pontosvessző van az utasítások végén, idézőjelben van a kiírandó szöveg, az elején ott szerepel a main kulcsszó.
Szakasz 1: A ’Hello Világ’ program
60
Ugyanakkor komoly eltéréseket is látunk: itt a program egység nem hanem module. Érthető, hiszen, mint korábban említettük, ezt a nyelvet elektronikus áramkörök tervezésére használjuk, itt a modul egy többé-kevésbé "kerek" egységet jelöl, itt ez lesz a fordítási egység. Látjuk azt is, hogy itt a {} zárójelek helyett kulcsszavakat használunk. Ez egyike a kevés, Pascal-os hatásnak. function,
A kiírandó szöveg a $display argumentumaként szerepel. Természetesen kevés áramkörbe építenek mondjuk LCD kijelzőt (,), így programunk nem mindenütt lesz futtatható. Pontosabban, kétféle programírásra kell felkészülnünk: lesz olyan, amit ténylegesen az ASIC/FPGA számára szánunk, és olyan, amit számítógépünkön, egy szimulációs környezetben fogunk használni. Ez a kiíratás csak az utóbbi rendszerben lehetséges. Ezt a "program" írásakor nagyon érdemes szem előtt tartani, és a forráskódot eleve úgy írni, hogy külön fájlokban szerepeljenek a csak a szimulátorban végrehajtható kódok. A segédprogramok vagy hibát jeleznek a nem odaillő utasításra, vagy figyelmen kívül hagyják azt (természetesen a fordítóprogram nem tudhatja, hol akarjuk a készített kódot futtatni).
Szakasz 1: A ’Hello Világ’ program
61
Még egy szokatlan elemmel hangolódhatunk a nyelvre: mivel az elektronikus elemek egymással párhuzamosan működnek, azt is meg kell mondanunk, hogy mikor fusson az illető "program" blokk. Ebben az esetben kezdetben (az áramkör bekapcsolásakor). Természetesen ez csak egy kis ízelítő. Most megismerkedünk pár nyelvi elemmel. Néhol teljes alaki és funkcionális azonosságot találunk a C nyelvi elemekkel. Ne feledjük, ez inkább csak esetleges: áramkört programozunk, nem pedig programot írunk!
Szakasz 2: Nyelvi elemek
62
2. Nyelvi elemek Mivel hardver leíró nyelvet tárgyalunk, az alapegységnek és annak paramétereinek külön neve és szerepe van. • Modul A modul egy hardver blokkot ábrázol, jól definiált kimenetekkel és bemenetekkel, valamint jól definiált feladattal, lásd. ?? ábra és III.2 lista. A modul két részből áll. A port deklarációk a modul külső interfészét reprezentálják. A modul többi része egy belső leírás: a viselkedése, a szerkezete, vagy e kettő keveréke. • Port A module kulcsszó után a modul neve áll (a példában AOI), majd a port nevek listája következik. Egy port egy IC lábnak, a kártya egy élcsatlakozójának, vagy egy logikai kommunikációs csatornának felel meg egy hardverblokk esetén. Az egyes port deklarációk egy vagy több port nevet tartalmaznak (például: A, B, ...), továbbá megadják, hogy az információ melyik irányból folyhat át a portokon (input, output vagy inout).
Szakasz 2: Nyelvi elemek
63
Programlista III.2: Egy modul ami AND-OR-INVERT kaput ír le // Egy AND−OR−INVERT kapu module AOI (A, B, C, D, F) ; input A, B, C, D; output F ; assign F = ~ ( (A&B) | (C&D) ) ; endmodule
Ez a sor adja meg a funkcionalitást
Az eddig látott Verilog szabályok: • A C-vel megegyezően, egysoros és többsoros megjegyzések vannak. A // karakterek jelzik az egysoros megjegyzés (komment) kezdetét; a sor további része elhanyagolódik. Hagyományosan egysoros kommenteket használunk dokumentálásra; a többsorost pedig kódrészek átmeneti kiiktatására. • Az utasításokat ";" zárja le. Érdemes soronként csak egyetlen utasítást írni, és az utasításokat bekezdésekkel úgy formázni, hogy az mutassa a kód szerkezetét.
Szakasz 2: Nyelvi elemek
64
• Az azonosítókban megkülönböztetjük a kis és nagybetűt. A felhasználó által definiált nevekben egyaránt lehetnek kis- és nagybetűk, de ezek különbözőnek számítanak. • A kulcsszavak kisbetűsek • Minden definíció és utasítás a modulba kerül Szójegyzék: assign A modul egy folyamatos értékadó utasítást tartalmaz, ami egyúttal a modul funkcióját is megadja. Azért nevezik folyamatosnak, mert a bal oldal folyamatosan frissül, hogy tükrözze a jobb oldali kifejezés értékét. A szimuláció során a folyamatos értékadó utasítás akkor hajtódik végre, amikor a jobb oldalon szereplő négy port, A, B, C és D valamelyikének értéke megváltozik.
Általában véve, egysoros kommenteket (//) érdemes használnunk, még akkor is, ha több egymás utáni sort akarunk megjegyzésként
Szakasz 2: Nyelvi elemek
III.1. ábra. Az eddigi nyelvi szabályok
c
2001, Doulos [2]
65
Szakasz 2: Nyelvi elemek
66
Szójegyzék: Operátor Ebben a példában a folyamatos értékadás három operátort használ, amelyek: &, | és ~ sorjában bitenkénti és, vagy és nem műveletet jelentenek. Az operátorokat és kifejezéseket később tárgyaljuk.
kezelni. Csak akkor használjunk blokk kommentet, ha nagyon sok sort akarunk megjegyzésként kezelni (pl. egy egész modult). Ez azért van, mert a blokk kommenteket nem lehet egymásba ágyazni: a belső blokk komment vége (*/) a külső komment végeként értelmeződik! A blokk kommenten belül azonban az egysoros kommentek a blokk komment részét képezik, lásd III.3 lista. A modulok, portok, stb. nevét azonosítónak nevezik. Az azonosítók bármilyen hosszúak lehetnek, és betűkből, számokból, aláhúzásjelből (_), valamint dollárjelből ($) állhatnak. Az első karakter csak betű vagy aláhúzás lehet.
Szakasz 2: Nyelvi elemek
67
Programlista III.3: Megjegyzések használata a Verilog programokban /∗ Beágyazhatunk // i l y e n egysoros kommenteket // többsoros ( blokk ) kommentbe ∗/ /∗ Nem tehetünk azonban /∗ blokk kommentet ∗/ másik blokk kommentbe ∗/
Programlista III.4: Azonosítók használata a Verilog programokban AB Ab
aB
ab
Y Y_ _Y
// Ez mind más azonosító //Ezek pedig e l t é r ő e k
G4X$6_45_123$ // Ez i s l e g á l i s 4 plus4 $1 \4 plus4 \$1
//Ez viszont hibás
\a+b$\%^&∗( // Kivételes azonosítók
Szakasz 2: Nyelvi elemek
68
A kis- és nagybetűk különböznek, azaz ha két azonosítóban nem egyformán használjuk a kis- és nagybetűket, a két azonosító különböző lesz. Egy kivételes azonosító fordított törtvonallal (\) kezdődik és nemnyomtatódó karakterrel (betűköz, tabulátor vagy új sor) zárul. Nem nyomtatódó karakter kivételével bármit tartalmazhat. A kivételes azonosítókra a Verilog kódot előállító segédeszközök miatt van szükség, mivel ezek elnevezési konvenciói nagyon eltérőek lehetnek. Ilyenek például a séma szerkesztők és a szintézis eszközök. Kulcsszavak and default event function wire
Nagyszámú olyan fenntartott azonosító van, amelyet nem használhatunk névként. Ezek teljes listája az III.1 táblázatban található. Az eddig tárgyalt modulok elég egyszerűek voltak ahhoz, hogy a bemenetek függvényében a kimenetet egyetlen folyamatos értékadással tudjuk megadni. A valódi modulok ennél sokkal bonyolultabbak,
Szakasz 2: Nyelvi elemek
69
III.1. táblázat. A Verilog fenntartott azonosítói and always assign begin buf bufif0 bufif1 case casex casez cmos deassign default defparam disable edge else edge end endcase
endfunction endprimitive endmodule endspecify endtable endtask event for force forever fork function highz0 highz1 if ifnone initial inout input integer
join large macromodule medium module nand negedge nor not notif0 notif1 nmos or output parameter pmos posedge primitive pulldown pullup
pull0 pull1 rcmos real realtime reg release repeat mmos rpmos rtran rtranif0 rtranif1 scalared small specify specparam strength strong0 strong1
supply0 supply1 table task tran tranif0 tranif1 time tri trian trior trireg tri0 tri1 vectored wait wand weak0 weak1 while
wire wor xnor xor
Szakasz 2: Nyelvi elemek
70
ott belső összekötésekre is szükség van. Hogy egy belső jelnek (l. III.5 lista betét ábrája) folyamatos értékadással értéket adjunk, a jelet először vezetékként kell deklarálnunk. Egy vezeték deklarációja ugyanolyan, mint egy porté (lásd III.5 lista), de típussal (wire), esetlegesen vektormérettel és egy névvel, vagy nevek listájával. (A vektorokkal kicsit később foglalkozunk) A portok alapértelmezetten vezeték típusúak, ezért az F wire deklarációja opcionális. A többi deklaráció viszont szükséges. Rövídítésként, egyetlen utasítással is tudunk egy vezetéket deklarálni és/vagy annak értéket adni. A két módszer között csak annyi a különbség, hogy az utóbbi csökkenti a programszöveg méretét, lásd III.6 lista. Egy vezeték értékadásakor a késleltetés a megfelelő folyamatos értékadás késleltetésével egyenértékű, nem pedig a vezeték késleltetésével. Emiatt szükség lehet arra, hogy a vezeték értékadást elválasszuk a folyamatos értékadástól, úgy, hogy a késleltetést nem az értékadáshoz, hanem a vezetékhez rendeljük. Ez azonban olyan
Szakasz 2: Nyelvi elemek
71
Programlista III.5: Egy modul ami AND-OR-INVERT kaput ír le; belső jelekkel module AOI (A, B, C, D, F) ; input A, B, C, D; output F ; wire F ; // Alapértelmezett wire AB, CD, O; // Ez viszont k e l l assign AB = A & B; assign CD = C & D; assign O = AB | CD; assign F = ~O; // Az assign csak wire−nek adhat é r t é k e t ! endmodule
Szakasz 2: Nyelvi elemek
72
Programlista III.6: Az AND-OR-INVERT modul többszörös értékadással module AOI (A, B, C, D, F) ; input A, B, C, D; output F ; /∗ wire F; wire AB, CD, O; assign AB = A & B; assign CD = C & D; assign O = AB | CD; assign F = ~O; ∗/ // Ezzel ekvivalens assign AB = A & B, CD = C & D, O = AB | CD, F = ~O; endmodule
Szakasz 2: Nyelvi elemek
73
Programlista III.7: Az AND-OR-INVERT modul többszörös deklarációval module AOI (A, B, C, D, F) ; input A, B, C, D; output F ; /∗ wire AB = A & B; wire CD = C & D; wire O = AB | CD; wire F = ~O; ∗/ // Ezzel ekvivalens wire AB = A & B, CD = C & D, O = AB | CD, F = ~O; endmodule
Szakasz 2: Nyelvi elemek
74
finomkodás, amivel aligha találkozunk a gyakorlatban! Az III.6 lista a Verilog szintaxisának azt a tulajdonságát is illusztrálja, hogy egynél több deklaráció, példány vagy folyamatos értékadás esetén a műveletnek és a deklarált, példányosított vagy értéket kapó objektumnak ugyanolyan típusúnak kell lennie. Ebben az esetben vagy több (pontosvesszővel elválasztott) egyedi utasítást használunk, vagy pedig egyetlen utasításban több deklarációt vagy értékadást (vesszővel elválasztva), lásd III.7 lista. A net (összekötés) típus a Verilog három fő adattípusának egyike. A másik kettő a regiszter és a paraméter adattípus; ezeket később tárgyaljuk. Az összekötések az elektromos áramkör pontjai közötti elektromos összekötéseket ábrázolják. Az összekötéseknek 8 típusa van, ezek egyike a wire, lásd III.2 táblázat. A net alapértelmezett típusa a wire. A Verilog nyelvben van még hét másik féle típus is, amelyek másféle elektromos jelenségnek felelnek meg. A többi típus bizonyos egyedi esetek modellezésekor fontos. Használatukra példa:
Szakasz 2: Nyelvi elemek
III.2. táblázat. Az összekötések típusai wire, tri Ordinary net, tristate bus wand, triand Wired and wor, trior Wired or tri0 Pulldown resistor tri1 Pullup resistor trireg Capacitive charge storage supply0 Ground supply1 Power
75
Szakasz 2: Nyelvi elemek
76
wire a, b, c; // Alapertelmezett wand p,q,r; supply0 Gnd;
Az összekötési lista definiálatlan nevei alapértelmezetten egybites vezetékként értelmeződnek, és nagy valószínűséggel, a összekötéseink 99 %-a wire típusú lesz.
Szakasz 2: Nyelvi elemek
Megjegyzés: Egyéb típusok wire, tri A wire (szinonimája a tri) vagy egy pont-pont közötti elektromos összeköttetést modellez, vagy egy több meghajtóval rendelkező háromállapotú buszt. Busz konfliktus esetén a vezeték értéke ‘bx lesz. wand, wor A wand (szinonimája a triand) és a wor (szinonimája a wor) AND illetve OR kapuként viselkednek, amikor a hálózatban egynél több meghajtó van. tri0, tri1 A tri0 és tri1 összekötések úgy viselkednek, mintha a vezetékhez egy felhúzó vagy lehúzó ellenállást kapcsoltunk volna, azaz amikor a hálózatban nincs meghajtó, az összekötés 0-hoz vagy 1-hez lebeg. Hasonló esetben egy wire értéke z lenne. trireg A trireg összekötés kapacitív töltés tárolást és kisütést modellez. Amikor egy trireg összekötésen nincs meghajtó, az megőrzi előző értékét. trireg összekötéseket kapcsolószintű
77
Szakasz 3: Hierarchia megvalósítása
78
3. Hierarchia megvalósítása A modulok hivatkozhatnak más modulokra, így egy hierarchiát hoznak létre. Az III.8 listán, a MUX2 modul hivatkozik az alacsonyabb szintű modulokra, és leírja a közöttük levő kapcsolatot. A betét ábrán egy 2:1 multiplexert látunk, ami egy AOI kapuból és két inverterből áll. A Verilog zsargonjában az alacsonyabb szintű modulra való hivatkozást egy modul példányosításának (példány készítésnek) hívják. Mindegyik példány független, egyidejűleg aktív kópiája az eredeti modulnak. (Másik hardver egység és nem szubrutin hívás!) Az egyes modul példányok tartalmazzák a modul nevét (pl. AOI vagy INV), a példány nevét (ami abban a modulban egyedi név) és egy port összekötés listát. A példánynak szükséges nevet adni, hogy ugyanazon modul több példányát meg tudjuk különböztetni, lásd a III.8 listán az INV modult. Az előző példában egy INV modult példányosítottunk. Valójában azonban nem kell saját inverter modult írnunk: a Verilog nyelvbe be
Szakasz 3: Hierarchia megvalósítása
79
Programlista III.8: Példa modul hierarchiára sorrendi leképezéssel module AOI (A,B,C,D, F) ; ... endmodule module INV (A, F) ; ... endmodule module MUX2 (SEL,A,B, F) ; input SEL, A, B; output F ; INV G1 (SEL, SELB) ; // Modul példány AOI G2 (SELB,A, SEL,B,FB) ; // Sorrendi leképezés // SELB es FB i m p l i c i t wire típusúak ! INV G3 (FB, F) ; // Másik modul példány endmodule
Szakasz 3: Hierarchia megvalósítása
80
Programlista III.9: Példa modul hierarchiára név szerinti leképezéssel module AOI (A,B,C,D, F) ; ... endmodule module INV (A, F) ; ... endmodule module MUX2 (SEL,A,B, F) ; input SEL, A, B; output F ; INV G1 ( .A(SEL) , . F(SELB) ) ; // Név s z e r i n t i leképezés AOI G2 ( .A(SELB) , .B(A) , .C(SEL) , .D(B) , . F(FB) ) ; INV G3 ( . F(F) , .A(FB) ) ; // A sorrend nem számit endmodule
Szakasz 3: Hierarchia megvalósítása
81
Szójegyzék: Sorrendi leképezés A modul port összekötéseket sorrendben adjuk meg, és a sorrendet a példányosítandó modul első sorából származtatjuk. A lista első elemét a modul első portjához kapcsoljuk, és így tovább. (Ordered mapping) Programlista III.10: Példa modul hierarchiára primitívek használatával module MUX2 (SEL,A,B, F) ; input SEL, A, B; output F ; not G1 (SELB, SEL) ; // Primitív példánya AOI G2 (SELB,A, SEL,B,FB) ; not ( F,FB) ; // A példány neve opcionális endmodule
Szakasz 3: Hierarchia megvalósítása
82
Szójegyzék: Implicit vezetékek Minden olyan nevet, amelyet port összekötés listában használunk, de nem deklarálunk sehol, a fordító implicit módon egybites vezetékként deklarál. Ez nagyon fontos – nagyon sok hiba forrása lehet!
van építve néhány beépített primitív, avagy kapu; lásd ?? ábra és III.10 lista. Ezek a primitívek egyszerű logikai kapukat, háromállapotú puffereket, felhúzó és lehúzó ellenállásokat, valamint számos egyirányú és kétirányú kapcsolót modelleznek. A primitíveket kiterjedten használják kapuszintű és kapcsolószintű modellekben; például könyvtárak és felhasználó-definiálta ASIC áramkörök modellezésekor. Néha más alkalmakkor is hasznosan alkalmazhatunk primitíveket, bár általában előnyösebb folytonos értékadást használni.
Szakasz 3: Hierarchia megvalósítása
83
Szójegyzék: Név szerinti leképezés A portok helyes sorrendben való felsorolása mellett egy másik lehetőség a portok név szerinti leképezése. Itt a kapcsolat úgy jön létre, hogy (egy pontot elébe téve) megadjuk a modul portjának a nevét, és zárójelben odaírjuk annak a vezetéknek vagy regiszternek a nevét, amelyiket ahhoz a porthoz kell kapcsolni, lásd III.9 lista. Mivel itt portok nevét adjuk meg, nem lényeges, hogy azok milyen sorrendben jelennek meg. A név szerinti leképezést általában előnyben részesítik a sorrendi leképezéssel szemben, mert egyszerűbb megérteni és nehezebb hibázni.
A primitíveket ugyanúgy használhatjuk, mint a modulokat, attól eltekintve, hogy nem szükséges nevet adni nekik. (Ha akarunk, azért adhatunk, lásd III.10 lista.) Emellett, primitívek esetén nem használhatunk név szerinti leképezést sem: ismernünk kell a portok
Szakasz 3: Hierarchia megvalósítása
84
sorrendjét. Segítségünkre van az a tény, hogy az output portok mindig megelőzik az input portokat. Ha sorrendi leképezést használunk, egy modul példány bizonyos portjait bekötetlenül hagyhatjuk olyan módon, hogy kihagyjuk azokat a bekötési listából. A helyet két egymás után tett vesszővel hagyhatjuk üresen. Név szerinti leképezés esetén kétféle módon is bekötetlenül hagyhatunk egy portot: • teljesen elhagyjuk a portot • a portot ugyan szerepeltetjük, de üresen hagyjuk a zárójelet A portok (és vezetékek) egyaránt ábrázolhatnak nemcsak egyes biteket, hanem buszokat (avagy vektorokat) is. Az III.12 listán és a betétz ábrában mutatott példában a SEL port egy kétbites busz, a nagyobb helyértékű bit (MSB) az 1. számú bit és a kisebb helyértékű bit (LSB) a 0.
Szakasz 3: Hierarchia megvalósítása
Programlista III.11: Példa be nem kötött portok használatára module AOI (A,B,C,D, F) ; input A, B, C, D; output F ; ... endmodule module . . . AOI AOI1 (W1,W2, ,W3,W4) ; // Nincs bekötve a C port /∗ Ezzel egyenertékű l e í r á s o k : AOI AOI1 ( .F(W4) , .D(W3) , .B(W2) , .A(W1) ) ; AOI AOI1 ( .F(W4) , .D(W3) , .B(W2) , .A(W1) , .C() ) ; ∗/ endmodule
85
Szakasz 3: Hierarchia megvalósítása
86
Programlista III.12: Példa vektor port használatára module MUX4 (SEL,A, B, C, D, F) ; input [ 1 : 0 ] SEL; // különálló input A, B, C, D; // input deklaráció output F ; // . . . endmodule
Ezzel egyenértékűen, a SEL így is deklarálható: input [0:1] SEL; vagy input [1:2] SEL; stb. Minden esetben a megadott tartomány bal oldali bitje az MSB, a tartományban szereplő aktuális értékek nem játszanak szerepet. A későbbiekben látni fogjuk, hogy a vektor elemei előjel nélküli számként értelmeződnek; ezért fontos figyelembe vennünk, hogy a baloldali bit
Szakasz 3: Hierarchia megvalósítása
87
a legnagyobb helyértékű. A Verilogban négyféle beépített logikai érték van. Az egybites értéket egy egybites egész literális ábrázolja. Ez az 1 értékből (az egybites hosszúság jelölésére), a ‘b kombinációból (a bináris típus jelölésére), valamint a (0, 1, X vagy Z) betűk valamelyikéből áll. Szójegyzék: Verilog logikai érték 1‘b0 - logikai 0, false, föld 1‘b1 - logikai 1, true, tápfesz 1‘bX - ismeretlen vagy inicializálatlan 1‘bZ - nagy impedanciájú, lebegő Egy vektor egyes bitjeit külön is kiolvashatjuk vagy azoknak értéket adhatunk, ha szögletes zárójelben megadjuk a bit számát. Például, V[7] a V vektor hetedik bitjét jelenti (ami ez esetben az MSB), lásd III.2 ábra. Ez a műveletet bitválasztásnak (bit select) is
Szakasz 3: Hierarchia megvalósítása
88
Szójegyzék: Logikai érték vektor A Verilog értékvektor egyszerűen logikai értékek sorozata (0, 1, X és Z). Az értéket megelőzi a vektor bitjeinek száma és a számrendszer alapjának jele. Például, 8‘bXXXX0101 egy nyolcbites bináris érték. A vektor neve az egész vektort jelenti.
nevezik. A reg [7:0] V deklaráció magyarázatát lásd alább. Egy rész kiválasztás egy vektor folytonos index tartományú részének kiválasztását jelenti, pl. V[2:5]. A részkiválasztásokat arra használjuk, hogy vektor egy részét kiolvassuk vagy annak értéket adjunk. A vektoros értékadás balról jobbra, bitenként hajtódik végre, így a W = V[2:5] értékadás eredménye W[3]=V[2], stb., lásd III.3 ábra. A W[1:0] = 2’b11; értékadás W[3] és W[2] értékét változatlanul
Szakasz 3: Hierarchia megvalósítása
III.2. ábra. Példa logikai érték használatára
c
2001, Doulos [2]
89
Szakasz 3: Hierarchia megvalósítása
III.3. ábra. Példa vektor részének kiválasztására
c
2001, Doulos [2]
90
Szakasz 3: Hierarchia megvalósítása
91
hagyja. Egy vektor értéktartománya egyaránt lehet növekvő (pl. [0:7]) és csökkenő (pl. [3:0]). A kiválasztott résztartomány csak ugyanolyan irányú lehet, mint amilyen az eredeti deklarációban szerepel. Így a W[0:1] kifejezés illegális, mivel W csökkenő irányú tartománnyal van deklarálva. Megengedett egyetlen bitet jelölő tartomány (pl. W[2:2]) kiválasztása is.
Szakasz 4: Testbench modul
92
4. Testbench modul Egy teszt fixtúra olyan Verilog modul, amelyik egy terv szimulációs környezetét írja le. Általában teszt vektorok előállítására és az eredmények analizálására szolgáló kódot tartalmaz (vagy összehasonlítja az eredményeket az elvárt kimenettel). Tartalmazza a tesztelendő modul egy példányát is. Bár a ’teszt fixtúra’ a Verilog világban általánosan elfogadott kifejezés, a ’test bench’ is használatos ugyanilyen értelemben. A III.13 lista a MUX4 modul tesztelésére szolgáló teszt fixtúra szerkezetét mutatja. A teszt fixtúra tartalmaz két ’initial’ utasítást (az egyik a teszt stimulus tényleges használatát mutatja, a másikat az eredménytáblázat nyomtatására használjuk), valamint a vizsgálandó modul egy kópiáját. A teszt fixtúrák közös jellemzője, hogy nem tartalmaznak portokat. Az ’initial’ blokk a Verilog procedurális blokkjainak egyike (a másik az ’always’ blokk). A procedurális blokkokat viselkedés leírására használják, különböző szituációkban. A ?? ábrán és a III.14
Szakasz 4: Testbench modul
93
Programlista III.13: A MUX4 modul tesztelésére szolgáló teszt fixtúra module MUX4TEST; // nincsenek portok . . . // deklarációk initial . . . // A bemenő j e l e k l e i r á s a MUX4 M ( // A v i z s g á l t modul .SEL(TSEL) , .A(TA) , .B(TB) , .C(TC) , .D(TD) , . F(TF) ) ; initial . . . // Az eredmények k i í r a t á s a endmodule
Szakasz 4: Testbench modul
94
listán az initial blokk a terv stimulusát avagy test patternjeit írja le. Az initial blokk végrehajtása a szimuláció kezdetekor indul (nulla időpontban) és a begin és end közötti utasítások felülről lefelé, sorban egymás után hajtódnak végre. #10 egy 10 egység hosszú eljárás késleltetés. Egy eljárás késleltetés a megadott periódusra felfüggeszti az ’initial’ blokk végrehajtását. Az egyes események végrehajtását nulla, egy vagy több késleltetés előzheti meg. Ezek az utasítás részei, nem kell utánuk pontosvesszőt írni (bár megengedett): #10 A = 0; ugyanaz, mint #10; A = 0; (kivéve, hogy az utóbbi két utasításnak felel meg) A SEL = 2’b00 és A = 0 utasítások procedurális értékadások. A Verilog lehetővé teszi, hogy az 1‘b0 és 1‘b1 formátumú számok helyett
Szakasz 4: Testbench modul
95
rövidítésként a 0 és 1 egész értékeket használjuk. Programlista III.14: Példa a tesztjelek előállítására // Egyszer hajtódik végre , f e l ü l r ő l l e f e l é i n i t i a l // Stimulus begin TSEL = 2 ’ b00 ; #10 TA = 0 ; // 1 ’b0 röviden #10 TA = 1 ; #10 TSEL = 2 ’ b01 ; #10 TB = 0 ; // Procedurális értékadás // ^ Procedurális k é s l e l t e t é s end
Eddig csak vezeték (wire) típusú érték tárolással és továbbítással foglalkoztunk, ez a legelterjedtebb összekötési mód. Van azonban a Verilogban egy másik adattárolási mód is: a regiszter. A regiszter itt nem feltétlenül egy hardver regisztert (pl. flip-flop) jelent. Regisztereket használnak a viselkedés leírására a procedurális
Szakasz 4: Testbench modul
96
blokkokban (initial és always). A wire és register közötti választás szabálya egyszerű, mégis sok félreértésre ad okot: regisztert csak procedurális blokkokban használunk, és procedurális blokkokban csak regisztereknek adhatunk értéket, lásd III.15 lista. Az összekötések vagy folyamatos értékadással, vagy porton keresztül kapnak értéket. Regisztereket a reg kulcsszóval deklarálunk. Később majd más típusú regisztereket is látni fogunk. A második ’initial’ blokk végzi az eredmények analízisét. Ez az initial blokk tartalmaz egy $monitor rendszertaszk hívást. Bár a $monitor hívás csak egyszer szerepel (a 0 időpontban), a hívás létrehoz egy monitort, ami mindaddig aktív marad, amíg ki nem kapcsoljuk vagy a szimuláció be nem fejeződik. A $monitor hívás első argumentuma a $time rendszerfüggvény. A $monitor további argumentumai kifejezések. Valamennyi kifejezés értéke kiíródik, amikor azok valamelyike megváltozik. Egy ’szünet’ a listában (két egymás utáni vessző) hatására egy betűköz karakter íródik ki. A $monitor hívást tartalmazó initial blokk nem tartalmaz begin
Szakasz 4: Testbench modul
97
Programlista III.15: Példa regiszterek használatára module MUX4TEST; reg TA, TB, TC, TD; // e g y f aj t a r e g i s z t e r reg [ 1 : 0 ] TSEL; wire TF; i n i t i a l // Stimulus begin TSEL = 2 ’ b00 ; #10 TA = 0 ; #10 TA = 1 ; #10 TSEL = 2 ’ b01 ; #10 TB = 0 ; ... end MUX4 M ( . SEL(TSEL) , .A(TA) , .B(TB) , .C(TC) , .D(TD) , . F(TF )); ... endmodule
Szakasz 4: Testbench modul
98
és end sorokat. Ezek itt nem szükségesek, mivel az initial blokkban csak egyetlen utasításunk van (a $monitor hívás). Ettől függetlenül, kiírhatjuk azokat. Az első initial blokkban a begin és end szükségesek, mivel ott egynél több utasításunk van. Teszteléskor a vizsgált egységre megfelelő stimulusokat adunk és az vizsgáljuk, hogy az elvárt választ kapjuk-e. Ennek egyik módja eredménytáblázat kiíratása. A III.17 listán bemutatott program által kiírt eredménytáblázat (az előállított vizsgáló jeleket lásd a ?? ábrán): Szójegyzék: Rendszertaszk A rendszertaszkok be vannak építve a Verilog nyelvbe (és a szimulátorba) és különféle hasznos funkciókat végeznek. A nevük mindig $ jellel kezdődik. A text fixtúra modulban deklarációk és utasítások vannak, de
Szakasz 4: Testbench modul
99
Programlista III.16: Példa eredmények kiíratására module MUX4TEST; reg TA, TB, TC, TD; reg [ 1 : 0 ] TSEL; 0 0 XXXX X wire TF; i n i t i a l // Stimulus 10 0 0XXX 0 begin TSEL = 2 ’ b00 ; 20 0 1XXX 1 #10 TA = 0 ; #10 TA = 1 ; 30 1 1XXX X #10 TSEL = 2 ’ b01 ; #10 TB = 0 ; end MUX4 M ( . SEL(TSEL) , .A(TA) , .B(TB) , .C(TC) , .D(TD) , . F(TF )); i n i t i a l // Kiíratás $monitor ( $time , ,TSEL, ,TA,TB,TC,TD, ,TF) ; // A $monitor rendszer taszk , a $time rendszer függvény endmodule
Szakasz 4: Testbench modul
100
Szójegyzék: $monitor A $monitor rendszertaszk egy sornyi szöveget ír a szimulátor naplójába (ami egy ablak és/vagy egy fájl) minden alkalommal, amikor valamilyen esemény történik, azaz amikor az argumentumaként feltüntetett reg vagy wire típusú értékek valamelyike megváltozik.
nincsenek portok. A modul három fő utasítása konkurrens (azaz egymással párhuzamosan hajtódnak végre); nem számít, milyen sorrendben írjuk le azokat. Másrészt viszont az adattípusokat (wire, reg, stb.) használat előtt deklarálnunk kell! Egy initial blokk egyetlen utasításnak tekintendő, még ha procedurális utasítások sorozatát tartalmazza is. Az első initial blokk utasításait a begin ... end zárójelezi; a közöttük levő utasítások felülről lefelé, sorban hajtódnak végre.
Szakasz 4: Testbench modul
101
Szójegyzék: A $time rendszerfüggvény A rendszertaszkokhoz hasonlóan, a rendszerfüggvények is be vannak építve a Verilog nyelvbe (és szimulátorba). A rendszertaszkoktól eltérően, a rendszerfüggvények egy értéket adnak vissza, ebben az esetben azt a szimulált időt, amikor egy esemény kiváltotta, hogy a $monitor egy sort írjon ki (természetesen a szimulációs idő változása nem triggereli a $monitor rendszertaszkot).
Mivel a második initial blokk csak egyetlen utasítást tartalmaz, a begin ... end utasítások nem szükségesek.
Szakasz 4: Testbench modul
102
Programlista III.17: A teljes testbench modul module MUX4TEST; reg TA, TB, TC, TD; reg [ 1 : 0 ] TSEL; wire TF; initial begin TSEL = 2 ’ b00 ; #10 TA = 0 ; #10 TA = 1 ; #10 TSEL = 2 ’ b01 ; #10 TB = 0 ; #10 TB = 1 ; ... end MUX4 M ( . SEL(TSEL) , .A(TA) , .B(TB) , .C(TC) , .D(TD) , . F(TF )); initial $monitor ( $time , ,TSEL, ,TA,TB,TC,TD, ,TF) ; endmodule
Szakasz 4: Testbench modul
103
Számok, vezetékek, regiszterek
104
fejezet IV: Számok, vezetékek, regiszterek Ebben a fejezetben (az előző "gyorstalpaló" után) elkezdjük rendszerezetten megismerni a Verilog nyelv alapelemeit.
Szakasz 1: Számok, vezetékek és regiszterek
105
1. Számok, vezetékek és regiszterek Erre a szakaszra azért érdemes odafigyelni, mert eddigi ’programozási’ tanulmányaink alatt fix bithosszúságú számokhoz szoktunk hozzá, és hogy egy bitnek csak kétféle értéke lehet. A Verilogban egyik sem teljesül. Programlista IV.1: Számok megadása a Verilogban // A számokban k i s és nagybetűk f e l c s e r é l h e t ő k reg [ 7 : 0 ] V; // Bináris 111xx00 V = 8 ’b111XX000 ; // _ elhanyagolódik számokban V = 8 ’B111X_X000; // Binary 111xx00 V = 8 ’hFF; // Hex = 8b’1111_1111 // Bevezető 0k adódnak a számhoz V = 8 ’ o77 ; // Octal = 8b ’00_111_111 V = 8 ’ d10 ; // Decimal = 8b’0000_1010
Szakasz 1: Számok, vezetékek és regiszterek
106
A számokat nem bájt, word, stb méretűként, hanem tetszőleges hosszúságú bit vektorként ábrázoljuk. A vektor értékeket olyan egész literálisként írhatjuk fel, amelyik a 0, 1, X és Z karaktereket tartalmazza, előtte pedig a szélességet megadó számot (a bitek számát), valamint – bináris számok esetén – a ’b kombinációt (pl. 8’b0101XXXX), lásd IV.1 lista. Számok más számrendszerben való megadásához a ’o (oktális), ’d (decimális) vagy ’h (hexadecimális) kombinációt használhatjuk. A számok – a hosszú számok tagolására, a jobb olvashatóság érdekében – tartalmazhatnak aláhúzás karaktereket, ezek egyszerűen elhanyagolódnak. A számokban (a számrendszer alap és a számjegyek megadásakor is) egyaránt használhatunk kis- és nagybetűket. Ha nem adjuk meg egy szám valamennyi jegyét, a legnagyobb helyértékű bitek nullával töltődnek fel. Például, 8’o77 megfelelője 8’b00_111_111. Láttuk, hogy ha nem adjuk meg egy szám valamennyi jegyét, a legnagyobb helyértékű bitek nullával töltődnek fel. Azonban, ha a legnagyobb helyértékű bit X vagy Z, a hiányzó bitek ennek megfelelően X vagy Z értékekkel töltődnek fel. Így tehát 8’bx valójában a
Szakasz 1: Számok, vezetékek és regiszterek
Programlista IV.2: Számok csonkítása és kiterjesztése reg [ 7 : 0 ] V; // Bináris 111xx00 V = 8 ’bX; //8 ’bXXXXXXXX V = 8 ’bZX; //8 ’bZZZZZZZX // Nincs e l ő j e l k i t e r j e s z t é s ! V = 8 ’ b1 ; // 8b’00000001 V = 1 ’hFF; // 1 ’hFF = 1 ’b1 ( csonkítás ) = 8 ’h01 ( kiterjesztés )
107
Szakasz 1: Számok, vezetékek és regiszterek
108
8’bxxxx_xxxx számot ábrázolja, 8’bzx pedig a 8’bzzzz_zzzx számot, lásd IV.2 lista. A reg és wire értékeket előjel nélküli egésznek kell tekinteni, még akkor is, ha kifejezésekben negatív számokat is használhatunk. Vigyáznunk kell, mert itt nincs előjelkiterjesztés. Általában véve, a Verilog csendben megcsonkítja vagy nullával feltölti a vektorokat, amikor rossz méretet adunk meg. Emiatt, az IV.2 programlistán mutatott példában 1’hFF csonkított értéke 1’b1 (mivel a szám egy bitnyi széles), és az 1’b1 érték terjesztődik ki nyolc bitre, balról nullákkal kiegészítve (mivelhogy egy egybites értékkel (1’h...) adunk értéket egy 8-bites vektornak (V)). A Verilogban mindig nagyon vigyáznunk kell a vektorok csonkítására! Az eddigi példák jól láthatóan bináris számoknak tűntek (azaz csak a 0, 1, X és Z jegyeket tartalmazták). Az IV.3 programlistában bemutatott számok is binárisak, még ha ez kevésbé nyilvánvaló is. A 10 egy 32 bites decimális szám és nem egy kétbites bináris szám! A -3 is egy 32-bites decimális szám, és kettes komplemens formában
Szakasz 1: Számok, vezetékek és regiszterek
109
Programlista IV.3: Megtévesztő bináris számok // Megtévesztő számok reg [ 7 : 0 ] V; // Nem bináris V = 1 0 ; // 10 = ’d10 = 32 ’d10 = 32 ’b1010
=> 8 ’b00001010
V = −3; // −3 = 32 ’hFF_FF_FF_FD => 8 ’b1111_1101 V = "A" ;
// ASCII = 8 ’d65 => 8 ’b01000001
értelmeződik. A Verilog azonban a V értékét előjel nélküli 8-bites értéknek (azaz 253-nak) tekinti. A stringek vektorokká alakításakor a karakterek 8-bites ASCII kódja szolgál alapul. Ha nem adunk meg semmit, a számok alapértelmezett szélessége 32 bit. (Szigorúan véve, az alapértelmezett értéket a szimulátor vagy szintézis eszköz implementációja határozza meg, ez azonban a gyakorlatban szinte mindig 32 bit.) Ez váratlan eredményekhez vezethet, ha 32 bitnél szélesebb wire vagy reg adatokkal dolgozunk.
Szakasz 1: Számok, vezetékek és regiszterek
Programlista IV.4: Számok, 32 biten túl // 32 b i t e t t ú l l é p ő értékek k e z e l é s e reg [ 7 : 0 ] V; reg [ 6 3 : 0 ] R; // R[ 6 3 : 8 ] = 56 ’b0 , R[ 7 : 0 ] = V ( extended ) R = V; // V = R[ 7 : 0 ] V = R;
( truncation )
// 64 ’h00000000ZZZZZZZZ R = ’ bz ; // 32 b i t az alapértelmezett
110
Szakasz 1: Számok, vezetékek és regiszterek
111
Az alsó példában az IV.4 programlistán az R, ami egy 64-bites érték, olyan szám értékét kapja meg, ami maga csak 32 bit szélességű. A számokat 64-bitre nullákkal terjesztjük ki. Megjegyezzük, hogy szám kiterjesztésekor X és Z értékeket csak a megadott vagy alapértelmezett méretig használunk; amikor egy értéket reg vagy wire változóhoz rendelünk, akkor nem. Más szavakkal: a Verilog fordító nem "nézi meg" az értékadás jobboldalát, amíg az X vagy Z értékekkel való kiterjesztés be nem fejeződött. Szójegyzék: $display A $monitor mellett a $display a másik rendszertaszk, amelyik a szimulátor naplófájljába írni tud. A $display csak egyszer ír a naplófájlba, közvetlenül a hívás után, és nem hoz létre egy folytonos monitort. A $display és a $monitor kimenete speciális karakterekkel formázható; a karaktereket string argumentumként adjuk át. Az egyes
Szakasz 1: Számok, vezetékek és regiszterek
112
Szójegyzék: $time $time olyan rendszer függvény, amelyik a szimulációs időt adja vissza.
%b, %d formátumleírók a listában szereplő következő argumentum (megfelelően formátumozott) értékével helyettesítődnek. A formátumleíró stringek: %b bináris %o oktális %d decimális %h hexadecimális %s ASCII string %v érték és meghajtási erősség %t idő (lásd később)
Szakasz 1: Számok, vezetékek és regiszterek
113
%m modul példány \n újsor \t tab \" " \nnn nnn a karakter ASCII kódjának értéke, oktálisan \\ \ A fentiek a $display és $monitor által értelmezett formátumleírók. Escape karakterként a fordított törtvonalat használják. Ha stringben ilyen fordított törtvonalat kell kiíratnunk, a formátumot előíró stringben két egymás utáni fordított törtvonalat kell írnunk. Hasonlóképpen, ha literálisként egy kettős idézőjelet kell kiíratnunk, azt is egy fordított törtvonal után kell írnunk. (Egyébként a " a stringet lezáró karakterként értelmeződik.) Például, $display("The file is \"C:\\Files\\File.txt\"");
Szakasz 1: Számok, vezetékek és regiszterek
114
eredménye a szövegben The file is "C:\Files\File.txt" Programlista IV.5: Formattált kimenet // Formátumozott k i v i t e l reg [ 3 : 0 ] TA, TB, TC, TD; reg [ 1 : 0 ] TSEL; wire [ 3 : 0 ] TF; // A f e j z e t és az eredmény t á b l á z a t nyomtatása initial begin $display ( " ", "Time TSEL TA TB TC TD TF" ) ; $monitor ( "%d %b %b %b %b %b %b" , $time , TSEL, TA, TB, TC, TD, TF) ; end
A $display és a $monitor kimenete speciális karakterekkel formázható; a karaktereket string argumentumként adjuk át. Az IV.5
Szakasz 1: Számok, vezetékek és regiszterek
115
programlistán bemutatott formattálás eredménye: Time 0 10 20 30
TSEL TA 00 0001 00 1110 01 1110 01 1110
TB 0010 0010 0010 1101
TC 0100 0100 0100 0100
TD 1000 1000 1000 1000
TF 0001 1110 0010 1101
A formázott argumentumokat kétféleképpen rendezhetjük el. Az egyik, hogy a sok formázó sztringet egyetlen hosszú sztringgé rendezzük össze, és azt követi a formázandó argumentumok listája. A másik, hogy az argumentumokat párokba rendezzük, ahol a párok egy formázó stringből és egy formázandó értékből állnak. Általában véve, a formázó sztringek számának meg kell egyeznie a formázandó kifejezések számával. Ha túl sok kifejezés van, az alapértelmezett formátum a decimális. Ha túl kevés, az hiba. Ez alól kivétel a %m, amihez nem szükséges kifejezés. A %m a modul azon példányának nevét írja ki, amelyikben a $display vagy $monitor utasítás megjelenik.
Szakasz 1: Számok, vezetékek és regiszterek
Programlista IV.6: Formattált kimenet // Egyetlen formátum sztring , megfelelő számú é r t é k k e l $display ( "%b %b" , Expr1 , Expr2 ) ; // vagy ekvivalens módon: // Két formátum sztring , megfelelő számú é r t é k k e l $display ( "%b" , Expr1 , " %b" , Expr2 ) ; // Túl sok é r t é k k e l : Expr2 decimálisan í r ó d i k k i $display ( "%b" , Expr1 , Expr2 ) ; // Túl kevés é r t é k k e l : hiba $display ( "%b %b %b" , Expr1 , Exp2) ;
116
Szakasz 2: Az idő kezelése
2. Az idő kezelése Programlista IV.7: Időskála ‘ t i m e s c a l e 1ns /100 ps // időegység/pontosság module MUX4TEST; ... initial begin ... #10 A = 4 ’ b1110 ; // #10 = 10 ns = 100(x100 ps ) ... end initial begin ... $timeformat ( −9 , 1 , "ns" , 7 ) ; $monitor ( "%t %b %b %b %b %b , %b" , $realtime , SEL, A, B, C, D, F) ; end endmodule
117
Szakasz 2: Az idő kezelése
118
‘timescale Az eddig látott Verilog szimulációk során az idő dimenzió nélküli mennyiség volt. A szimulációt fizikai időegységekben szokás végezni, például picosec vagy nanosec egységeket használva. A Verilogban az időegységet a ‘timescale fordítóprogram direktívával állíthatjuk be. A ‘timescale direktíva esetén az első mennyiség az időegység, lásd IV.7 programlista. A Verilog forráskódban szereplő valamennyi késleltetés ennek az időegységnek többszöröse. A példában #10 egy 10 ns értékű procedurális késleltetést jelent. A második mennyiség a pontosság. A példában a pontosság 100ps, ami azt jelenti, hogy a késleltetések a legközelebbi 100 ps értékre kerekítődnek. Így #10 jelentése 10ns avagy 100 100ps egység. Ha a terv valamely moduljában van időskála, akkor a terv valamennyi moduljában meg kell azt adni; akkor is, ha a a modul nem is használ késleltetést. A követendő gyakorlat, hogy minden egyes modulban adjunk meg időskálát.
Szakasz 2: Az idő kezelése
119
Szójegyzék: direktíva Egy fordítóprogram direktíva fordított idézőjellel (‘) kezdődik, és a fordítónak ad utasítást a még hátralevő Verilog programkódra vonatkozóan.
A ‘timescale használatos a késleltetés időegységének megadására ("#10 jelentése 10ns"). Természetesen előfordulhat, hogy a terv valamely moduljának eltérő időskálája van. Ilyen esetben az első értéket – az időegységet – a fent leírt módon használjuk. A második érték – a pontosság – esetében a szimulátor kiszámítja a tervben szereplő legkisebb pontosságértéket, és mindenütt azt használja, elhanyagolva az egyedi pontosság értékeket. Néhány Verilog segédprogram lehetővé teszi, hogy egyidejűleg több fájlt fordítsunk. Ez ugyanaz, mintha a forrásfájlokat egymás mögé írnánk és az így kapott fájlt fordítanánk le. Egy olyan direktíva, mint a ‘timescale, valamennyi ezután következő modult befolyásolja,
Szakasz 2: Az idő kezelése
120
valamennyi, egyidejűleg fordított fájl esetén, amíg újabb ilyen direktíva nem jelenik meg. Ez azt jelenti, hogy ha egyes modulokban van időskála, másokban meg nincs, akkor az utóbbiak saját időskálájukat az előzőleg fordított modulokból származtathatják. Azt láttuk, hogy ha a terv bármelyik moduljában van időskála, akkor minden modulban kell lennie. Egy modulnak adhatunk egy alapértelmezett időskálát úgy, hogy azt fordítjuk utolsóként. Programlista IV.8: Többszörös időskála; A modul ‘ t i m e s c a l e 1ns /1 ns // időegység/pontosság module A ( . . . ) ; ...
Programlista IV.9: Többszörös időskála; B modul ‘ t i m e s c a l e 10 ns /10 ns // időegység/pontosság module B ( . . . ) ; ...
Szakasz 2: Az idő kezelése
Programlista IV.10: Többszörös időskála; C modul // nincs időegység module C ( . . . ) ; ...
A három modul fordítási sorrendjétől függ az időegység: verilog A.v B.v C.v sorrendben az időskála 10ns/10ns verilog B.v A.v C.v sorrendben az időskála 1ns/1ns verilog C.v A.v B.v sorrendben pedig hibát kapunk: nincs időskála A $timeformat négy paraméterének jelentése
121
Szakasz 2: Az idő kezelése
122
Programlista IV.11: Az időkiírás formátumának megadása ‘ t i m e s c a l e 1ns /100 ps // Ez esetben #10 j e l e n t é s e 10.0 ns k é s l e l t e t é s $timeformat ( −9 , 1 , "ns" , −7) ; $monitor ( "%t " , . . . $realtime , . . .
• az az egység, amiben az időt ki kell írni, ahol -12 jelentése picoseconds, -9 nanoseconds, -6 microseconds, etc.; • a kijelzendő decimális helyek száma; • az idő után írandó sztring; • a minimális kiírandó karakterszám. Ha szükséges, a mező balról betűközzel feltöltődik. Megjegyezzük, hogy a $timeformat lehetővé teszi, hogy az időket olyan egységben írjuk ki, ami eltér a ‘timescale egységétől.
Szakasz 2: Az idő kezelése
123
Szójegyzék: $timeformat Amikor időket íratunk ki a $display és $monitor felhasználásával, a $timeformat használatával adhatjuk meg, hogyan kell az értékeket kiírni. A $timeformat a %t formátumleíróval használatos. A $timeformat tervenként csak egyszer hívható, így annak a teszt fixtúrában van a legjobb helye.
Inkább a $realtime függvényt érdemes használni a $time helyett, mivel az utóbbi a késleltetés értékét a legközelebbi (egész) időértékre kerekíti. A példában #10 úgy íródik ki, mint 10ns, mivel a ‘timescale direktíva ennyinek írja elő, és a $timeformat szerint az értéket nanoszekundumban kell kiírni, egy decimális jegyre, a "ns" utótag használatával, valamint annyi bevezető betűközzel, hogy összesen 7 karakter legyen, amibe a tizedespontot és az utótagot is bele kell érteni.
Szakasz 2: Az idő kezelése
Szójegyzék: $realtime A $realtime olyan rendszerfunkció, amelyik a pillanatnyi szimulációs időt valós számként adja vissza, annak a modulnak a ‘timescale egységében, amelyikből hívtuk.
124
Szakasz 3: További nyelvi elemek
125
3. További nyelvi elemek Programlista IV.12: Példa az always procedurális blokk használatára module ClockGen( Clock ) ; output Clock ; // a kimenet l e h e t r e g i s z t e r reg Clock ; initial Clock = 0 ; always #5 Clock = ~Clock ; endmodule ... // Bináris számláló initial Count = 0 ; always #10 Count = Count + 1 ;
Szakasz 3: További nyelvi elemek
126
Már találkoztunk a procedurális blokk egyik formájával, az initial utasítással. A procedurális blokk másik formája az always blokk. Egy always blokk nagyon hasonló egy initial blokkhoz, de nem csak egyszer hajtódik végre, hanem végrehajtása folyamatosan ismétlődik a szimuláció során. Amint a végére érünk, rögtön kezdi előlről, így egy végtelen ciklust definiál. A IV.12 programlista első részében a ClockGen modul az órajel hullámforma előállítására használ egy always blokkot. Aminek egy always blokkban értéket adunk, azt regiszterként kell definiálni. Megjegyezzük, hogy a Clock regiszternek két különböző procedurális blokkban is értéket adunk: az initial és az always blokkban is. Az initial blokk Clock értékét nullába állítja a szimuláció kezdetén. Ezután az always blokk újból értékeket ad a Clocknak. Ez az explicit inicializálás azért szükséges, mert a reg változók kezdeti értéke alapértelmezetten ’bx. A IV.12 programlista második példája azt mutatja, hogyan használhatunk egy always blokkot bináris számsorozat előállítására. Itt ismét csak egy initial blokkot használunk a Count kezdőértékének
Szakasz 3: További nyelvi elemek
127
előállítására. Amikor Count eléri a maximálisan lehetséges értéket (pl. 4’b1111 egy négy bites reg esetén), még egyet hozzáadva "átfordul" 0-ba és a számlálás újraindul. Programlista IV.13: A $stop és $finish direktívák hatása // Ez állandóan f u t n i fog module ClockGen_test ; wire Clock ; ClockGen CG1_ ( Clock ) ; endmodule module ClockGen_Test ; wire Clock ; ClockGen CG1_ ( Clock ) ; initial #100 $stop ; // töréspont initial #200 $ f i n i s h ; // k i l é p é s endmodule
Szakasz 3: További nyelvi elemek
128
Megjegyzés: Az IV.13 programlistában Clock nem lehet regiszter, hanem csak vezeték. Ez azért van, mert ebben a modulban Clockot egy port hajtja meg, ami nem kap értéket a procedurális blokkon belül. A Clock ezért vezeték típusú, annak ellenére, hogy olyan porthoz van kötve, ami maga a ClockGen modulon belül található regiszter.
A IV.13 programlistában mutatott példák a IV.12 programlistában bemutatott órajelgenerátor egy példányát használják. Ha egy terv az előző ábrán bemutatotthoz hasonló always utasításokat tartalmaz, fennáll annak a lehetősége, hogy a szimuláció a végtelenségig tart. Ezt megelőzendő, megmondhatjuk a szimulátornak, hogy csak egy véges ideig hajtódjék végre; megszakíthatjuk a szimulációt; vagy pedig töréspontot iktathatunk be a Verilog kódba. Amikor a $stop végrehajtódik, a szimuláció felfüggesztődik, azzal
Szakasz 3: További nyelvi elemek
129
Szójegyzék: $stop A $stop rendszertaszk töréspontok beiktatására használható.
a céllal, hogy a továbbhaladás előtt egy interaktív hibakeresővel meg tudjuk vizsgálni a szimuláció állapotát. Szójegyzék: $finish A $finish rendszertaszk használható arra, hogy a szimulációból kilépjünk. A $finish például olyankor használható, amikor egy órajelgenerátort vagy egyéb végtelen ciklust tartalmazó szimulációt akarunk befejezni. Kilépési alternatívaként használhatjuk a szimulátor utasításait vagy grafikus interfészét.
Szakasz 4: Regiszterek és vezetékek
130
4. Regiszterek és vezetékek Programlista IV.14: Regiszterek használata module UseRegs ( outreg ) ; output [ 7 : 0 ] OutReg ; // A deklarációknak reg [ 7 : 0 ] OutReg ; // azonosnak k e l l lennie reg R; initial R = ...
// r e g i s z t e r legyen
always OutReg = 8 ’b . . . // r e g i s z t e r legyen endmodule
A szakasz végefelé, nézzük meg részletesebben az összekötések és a regiszterek használatát, lásd IV.14 programlista. Az alapszabály két részből áll: regisztert kell használnunk, amikor initial és always
Szakasz 4: Regiszterek és vezetékek
131
blokkban értéket adunk; és valójában regisztereket csak ebben az összefüggésben kell használnunk. Ezt a szabályt kell követnünk még akkor is, ha az azt jelenti, hogy egy output portot reg-ként kell deklarálnunk. Ilyen esetben az output és reg deklarációknak egymásnak megfelelőnek kell lenniük, pl. ugyanaz az LSB és MSB legyen a két deklarációban. Vezetékeket akkor használunk, amikor nem kell regisztereket használnunk. Mint már láttuk, vezetékeket kell használnunk input portok és folyamatos értékadás esetén. Szintén egyszerű vezetékeket kell használnunk, amikor modulok vagy primitívek kimenetéhez kapcsolódunk. Skalár vezetékeket használhatunk implicit deklarációval is, de a vektorokat explicit módon deklarálni kell. A vezetékekre és regiszterekre vonatkozó szabályok azt jelentik, hogy ugyanazt a kapcsolatot vezetékként és regiszterként is modellezhetjük. Az IV.1 ábra és az IV.16 programlista szerinti példában a Top modulnak van egy R regisztere, ami a Silly modul S példányának InWire portjához van kapcsolva. Tudjuk azonban, hogy a
Szakasz 4: Regiszterek és vezetékek
132
Programlista IV.15: Vezetékek használata module UseWires ( InWire , OutWire) ; input InWire ; // A bemeneteknek vezetéknek k e l l lenni output OutWire ; // A kimenet l e h e t vezeték wire [ 1 5 : 0 ] InternalBus ; //Belső vektor AnotherModule U1 ( InternalBus , YetAnother U2 ( InternalBus ,
...) ; ...) ;
not ( ImplicitWire , InWire ) ; // A példány kimenete csak vezeték l e h e t and ( AnotherWire , ImplicitWire , . . . ) ; assign OutWire = . . . // Az assign miatt vezeték k e l l endmodule
Szakasz 4: Regiszterek és vezetékek
Programlista IV.16: Modul határok és a reg/wire átmenet module Top ; reg R; wire W1, W2; S i l l y S ( . InWire (R) , OutWire(W1) , OutReg(W2) ) ; ... endmodule module S i l l y ( InWire , OutWire , OutReg) ; input InWire ; output OutWire , OutReg ; reg OutReg ; i n i t i a l OutReg = InWire ; assign OutWire = InWire ; endmodule
133
Szakasz 4: Regiszterek és vezetékek
IV.1. ábra. Modul határok és a reg/wire átmenet
c
2004, Doulos Verilog
134
Szakasz 4: Regiszterek és vezetékek
135
modulok bemenetének vezeték típusúnak kell lennie. Hasonló módon, a Silly OutReg nevű kimenetének, ami regiszter, egy vezetékhez kell kapcsolódnia, amikor Silly példányosítódik. A színfalak mögött implicit folytonos értékadások történnek a modulok határán. Programlista IV.17: Inout port használata module UsesInOut ( Data ) ; inout [ 7 : 0 ] Data ; // inout nem l e h e t reg típusú reg [ 7 : 0 ] Data_reg ; always . . . = Data ; // Most éppen input always Data_reg = 8 ’b . . . ; // Ennek reg−nek k e l l lenni assign Data = Data_reg ; // Az assign miatt wire k e l l endmodule
Ez a korlátozás nem probléma bemenetek esetén, mivel bemenetet nem kell regiszterként deklarálnunk. Lehet azonban ilyen probléma az inout portokkal, mivel előfordulhat, hogy az always blokkban értéket akarunk adni egy inoutnak - a szabályok alapján viszont nem
Szakasz 4: Regiszterek és vezetékek
136
Szójegyzék: inout Egy inout port kétirányú, és az inputhoz hasonlóan, vezetéknek kell lennie, lásd IV.2 ábra.
tudunk! Helyette, lásd IV.17 programlista, egy reg-et kell használnunk az always utasításban, majd egy folyamatos értékadást, hogy a reg értékét az inout-nak átadjuk. • Regisztereket kell használnunk – amikor initial utasításban értéket adunk – amikor always utasításban értéket adunk • Vezetéket kell használnunk – amikor folyamatos értékadást használunk – input és inout esetén – amikor primitív példányt outputhoz vagy inout-hoz kapcsolunk
Szakasz 4: Regiszterek és vezetékek
137
IV.2. ábra. Inout port értelmezése
c
2004, Doulos Verilog
• Az összekötések strukturális jellegűek, a regiszterek viselkedés jellegűek. A fentiek megjegyzésére az egyik módszer, hogy a vezetékek strukturális modellekben használatosak (netlist), a regiszterek pedig viselkedési modellekben. Egy folyamatos értékadást lényegileg strukturális utasításnak tekintünk.
Szakasz 4: Regiszterek és vezetékek
IV.3. ábra. Vezeték és regiszter kapcsolata
c
2004, Doulos Verilog
138
Szakasz 4: Regiszterek és vezetékek
139
Always blokkok
fejezet V: Always blokkok
140
Szakasz 1: RTL always utasítás
141
1. RTL always utasítás Programlista V.1: Az always utasítás két tipikus használati módja // Kombinációs always reg OP; Esemény vezérelt ... always @(SEL or A or B or C) i f (SEL) OP = A and B; else OP = C; // Órajel v e z é r e l t always reg Q; Esemény vezérelt ... always @( posedge Clock ) Q <= D; // Nem blokkoló értékadás
Always
Szakasz 1: RTL always utasítás
142
A V.1 programlista és annak betét ábrája az always utasításra mutat két olyan példát, amelyek szintetizálhatók. Az első példa egy kombinációs logika leírására használt always utasítást mutat, a második pedig egy szekvenciális logikát (D flip-flop). Az always a Verilog egyik leghatékonyabb és legflexibilisebb utasításainak egyike, amelynek használatával egy rendszer bármely részét, bármilyen absztrakciós szinten leírhatjuk, a Boole-egyenletektől a regiszter szintű átviteleken át a komplex algoritmusokig. Eseményvezérlés A @ és az always szó után a zárójelek között felsorolt nevek listáját eseményvezérlésnek (vagy érzékenységi listának, sensitivity list) is nevezik. Az always utasítás mindig végrehajtódik, amikor az eseménylistában felsorolt vezetékek vagy regiszterek akármelyike megváltozik. Csak reg változóknak adhatunk értéket egy always utasításban, emiatt mind az OP, mind a Q változót regiszterként kell definiálnunk. Azonban, csak Q szintetizálódik hardver regiszterként (flip-flop). A
Szakasz 1: RTL always utasítás
143
Megjegyzés: or Az ’or’ az eseménylistában nem logikai operátor; hanem az események elválasztására szolgáló szeparátor. Az első példában az azt jelenti, hogy "amikor SEL értéket vált vagy A értéket vált vagy B értéket vált vagy C értéket vált, hajtsd végre az always utasítást”. A második példában az eseményvezérlés hatására az always utasítás akkor hajtódik végre, amikor a Clock jelnek felfutó éle van. Az órajelvezérelt always utasításokat később részletesen tárgyaljuk.
Verilogban a reg kulcsszó nem feltétlenül olyasmit jelöl, aminek eredménye egy regiszter lesz. Órajelvezérelt always utasításokban a reg változók gyakran a nem-blokkoló értékadás (<=) operátor használatával kapnak értéket, amint majd később tárgyaljuk. A V.2 programlista és betét ábrája egy olyan always utasítást mutat, amelyik egy kombinációs logikai függvényt ír le. Az always
Szakasz 1: RTL always utasítás
144
Programlista V.2: Az always utasítás két tipikus használati módja always @(C1 or C2 or C3 or A or B) begin i f (~C1) F = A; else F = B; i f (C2 & C3) F = ~F ; end
Szakasz 1: RTL always utasítás
145
utasítás két if utasítást tartalmaz. if Az első if utasítás ˜C1 értékét teszteli, majd az értéktől függően a két lehetséges értékadás valamelyikét hajtja végre. Ha ˜C1 értéke logikai egy, (azaz C1 értéke 1’b0), akkor ezt ’true’ értéknek tekinti és az első értékadást (F = A) hajtja végre. Ha ˜C1 logikai nulla vagy ismeretlen, akkor ezt ’false’ értéknek tekinti és a második értékadást hajtja végre. Az else részt egy if utasítás után elhagyhatjuk. A második utasítás invertálja F értékét, ha C2 és C3 egyaránt magas logikai értékű, különben (ha akár C2, akár C3 értéke logikai alacsony szint) nem invertálja. Szintézis Az if utasításoknak a szintézis után egy multiplexer felel meg, minden, az if utasításban adott érték esetén. A multiplexer kiválasztó jelét a logikai kifejezés határozza meg, az adatokat pedig az értékadás jobb oldalán álló kifejezés. Ha egy regiszter bizonyos feltételek esetén nem kap értéket, akkor a régi érték adódik át a multiplexeren, mint a
Szakasz 1: RTL always utasítás
146
fenti második if utasítás mutatja. A Verilog modell és a szintetizált hardver közötti megfeleltetés nem egy-egy értelmű. Nevezetesen, reg F három vezetékként szintetizálódik. Ezen túlmenően, ha optimalizáljuk a logikát, az optimalizált tervben esetleg semmi olyat nem fogunk látni, ami az eredeti reg F deklarációnak megfelelne. Az if utasítás mindkét ágában egy utasításnak van helye. Ha egynél több utasítás szükséges, az utasításokat a begin és end utasítás zárójelek közé kell írni. A példában mindkét ágon két-két utasítás van, így mindkét ágon szükségesek a begin-end-ek. Az if utasítás – beleértve az else ágat, és mindkét ágon az utasításokat – egyetlen utasításnak számít. Emiatt a V.3 programlistán és annak betét ábráján bemutatott always utasításban nem szükségesek a begin-end utasítászárójelek (bár használhatók). Utasítás sorozatok vizsgálatát if utasítások egymásba ágyazásával érhetjük el. Ezt legjobb egymást követő else if utasítások sorozataként írni, amint az az ábrán is szerepel. Az egyes feltételeket sorban egymás
Szakasz 1: RTL always utasítás
Programlista V.3: A begin-end utasításpár használata always @(SEL or A or B or C or D) begin // opcionális i f (SEL) begin // szükséges F = A; G = C; end // szükséges else begin // szükséges F = B; G = D; end // szükséges end // opcionális
147
Szakasz 1: RTL always utasítás
148
Programlista V.4: Egymásba ágyazott else-if utasítások hatása // Teljes "érzékenységi l i s t a " always @(C0 or C1 or C2 or A or B or C or D) i f (C0) F = A; e l s e i f (C1) F = B; e l s e i f (C2) F = C; else F = D;
Szakasz 1: RTL always utasítás
149
után megvizsgáljuk, amíg olyan értéket nem találunk, amelyik ’true’ értéket ad, ilyenkor az egymásba ágyazott if utasítások megfelelő ága hajtódik végre; azzal a mellékhatással, hogy a korábban említett feltételeknek elsőbbsége van a később említettekkel szemben. Programlista V.5: Egymásba ágyazott else-if utasítások hatása beginend nélkül // Akár így í r j u k : i f (C1) i f (C2) F = A; else F = B; // Akár így : i f (C1) i f (C2) F = A; else F = B;
Szakasz 1: RTL always utasítás
150
Programlista V.6: Egymásba ágyazott else-if utasítások hatása beginend használatával // Ez viszont mást j e l e n t : i f (C1) begin i f (C2) F = A; end else F = B;
Szakasz 1: RTL always utasítás
151
Néha begin-end utasításokkal kell megmondanunk a Verilog fordítónak, hogy melyik if melyik else utasításhoz tartozik. A V.5 programlistán mutatott példában a két legfelső if utasítás, az eltérő bekezdési stílus dacára, azonos, lásd a betét ábrát. A második példában (V.6 lista és a betét ábra) a begin-end "elrejti" a belső if utasítást az else elől. A fordítóprogramok nem figyelik a bekezdési stílusokat! és nem is gondolatolvasással működnek! Ha egy reg változó csak bizonyos feltételek esetén kap értéket, akkor az más feltételek esetén megőrzi előző értékét. A hiányzó feltételt akár úgy is felfoghatjuk, mint azt a V.7 lista második felében látjuk. A Q kimenetnek mindenképpen értékkel kell rendelkezni, itt nincs tárolt előző érték, mint egy program változó esetén, ahol a megfelelő memória rekesznek van előző értéke. A szintézis eszközök ezt a helyzetet úgy kezelik, hogy beszúrnak egy transzparens latch tárolót a régi érték megőrzésére. Ez néha nem teljesen szándékunk szerint történik, amikor megpróbálunk egy kombinációs logikát procedurális blokkal leírni, és a szintézis eredményeként ilyen nemkívánt latch tárolókat is kapunk.
Szakasz 1: RTL always utasítás
Programlista V.7: Nem-teljes értékadás használata // Egy i l y e n kód : always @( Enable or Data ) i f ( Enable ) Q = Data ; // valójában e z t j e l e n t i : always @( Enable or Data ) i f ( Enable ) Q = Data ; else Q = Q;
152
Szakasz 1: RTL always utasítás
V.1. ábra. Nem-teljes értékadás latch tároló nélkül
c
2004, Doulos Verilog
153
Szakasz 1: RTL always utasítás
154
Bizonyos FPGA architektúrák nem tartalmaznak transzparens latchet. Ezért transzparens latch implementálásához aszinkron visszacsatolást kell használnunk, ennek használatát viszont nem minden FPGA támogatja; emiatt a Verilog kód megírása előtt érdemes a kiválasztott eszköz adatlapját megtekinteni. Szimuláció és szintézis esetén is az X értékkel való összehasonlítás eredménye mindig false. Mint a fenti ábrán látható, írhatunk olyan if utasításokat, amelyek figyelembe veszik a lehetséges X értékeket úgy, hogy explicit módon tesztelik a 0 és 1 értékeket. A harmadik feltételt azonban a szintézis eszközök elhanyagolják, mivel a hardver addigra már teljes mértékben le van írva. A gyakorlatban az RTL kódok nem tartalmaznak ilyenféle X kezelést, mivel X állapotokat általában nem állítunk elő, kivéve a "lényegtelen" értékek esetét. Az explicit X értékkel való értékadást a szintézis eszközök "lényegtelen" állapotként kezelik. Ez a szintézis eszközök számára lehetővé teszi, hogy hatékonyabban optimalizáljanak, de azzal a hátránnyal jár, hogy eltérhet az RTL modell illetve a szintetizált kapuk használatával végzett szimuláció eredménye. Ez egyszerűen
Szakasz 1: RTL always utasítás
155
Programlista V.8: Ismeretlen és lényegtelen értékek használata // Az X k e z e l é s é t e l k ü l ö n í t j ü k a s z i n t é z i s számára i f (A == 1 ’ b0 ) F = 1; e l s e i f (A == 1 ’ b1 ) F = 0; else F = 1 ’bx ; // Csak a szimulációban használatos // Az X érték valójában "don ’ t care" // Használatával e l k e r ü l h e t j ü k a l a t c h e k e t F = 1 ’bX; i f (A) F = 0; i f (B) F = 1; // viszont , a szimuláció e l t é r h e t a s z i n t é z i s eredményétől
Szakasz 1: RTL always utasítás
156
amiatt van, hogy egy RTL szimulációban F értéke lehet 1‘bX, a szintetizált összekötéslista esetén azonban csak 1’b0 vagy 1’b1 lehet. A V.8 programlista bemutatja a == összehasonlító operátort is, amit kifejezésekben használunk két érték összehasonlítására. Az A == 1’b0 kifejezés értéke 1’b1 (TRUE), ha A értéke 1’b0; egyébként 1’b0 vagy 1’bX (FALSE). Az if utasítás alternatívájaként a Verilogban használhatjuk a feltételes operátort is. Az operátort használhatjuk initial és always utasításokban, valamint folyamatos értékadásokban is, lásd V.9 programlista. A feltételes operátor két karakterből (? és :) áll, és három operandusa van. Az ezt az operátort használó kifejezés értéke az első (a ? előtti) operátortól függ. Ha ez az operandus TRUE (nem nulla), a kifejezés értéke a második (a ? utáni) operandus. Ha az első operandus FALSE, a kifejezés értéke a harmadik (a : utáni) operandus értéke. Az operátor nagyon hasznos multiplexerek és háromállapotú pufferek modellezésére.
Szakasz 1: RTL always utasítás
Programlista V.9: A feltételes operátor használata // A háromoperandusú f e l t é t e l e s u t a s í t á s value = A ? B: C; // egyenértékű a i f (A) value = B; else value = C; // kombinációval . Folytonos értékadásra (MUX) i s használható : assign F = SEL ? A : B;
157
Szakasz 1: RTL always utasítás
158
Van a feltételes értékadás operátora és az if utasítás között egy nagyon fontos különbség, ami akkor válik láthatóvá, amikor a feltétel ismeretlen. Egy if utasításban szereplő ismeretlen feltételt ’false’ értékűnek tekintünk, és ilyenkor az if utasítás else ága hajtódik végre. A feltételes értékadás operátora esetén, ha az első operandus ismeretlen, a feltételes kifejezés értékét úgy kapjuk, hogy bitenként összehasonlítjuk a második és a harmadik operandust. Ahol a megfelelő helyeken levő bitek értéke azonos, az eredményben a megfelelő bit ez a közös érték lesz. Ahol az értékek különböznek, vagy azok egyike ismeretlen, az eredmény megfelelő bitje X lesz. A V.10 programlistán bemutatott példában az eredmény 4’b01XX, mivel a 4’b010X és 4’b0111 értékek két baloldali bitje megegyezik, a harmadik bit eltér, a jobboldali bitek egyike pedig X. A háromállapotú meghajtók Z értékadást tudnak megvalósítani, akár folyamatos értékadással, akár always utasítással, lásd V.11 programlista és betét ábrája. Amikor egy háromállapotú puffert egy inout porttal kapcsolatban használunk, sokkal könnyebb – a bemutatotthoz hasonló módon – folyamatos értékadást használni,
Szakasz 1: RTL always utasítás
159
Programlista V.10: A feltételes értékadás operátor használata // Ismeretlen f e l t é t e l esetén value = 1 ’bX ? B: C; // j e l e n t é s e NEM i f ( 1 ’bX) value = B; else value = C;
// Ez az u t a s í t á s hajtódik végre
// hanem a két operandus b i t e n k é n t i összehasonlítása , azaz value = 1 ’bX ? 4 ’b010X : 4 ’ b0111 ; // eredménye value = 4 ’b01XX
Szakasz 1: RTL always utasítás
160
mint always utasítást. Ez azért van így, mert az inout portok és összekötéseik összekötés (általában wire) típusúak, és a folyamatos értékadás cél objektuma vezeték. Programlista V.11: Tristate állapot megvalósítása // Nagy impedancia b e á l l í t á s a assign Op = Enable ? Data : 1 ’bZ ; // vagy always @( Enable or Data ) i f ( Enable ) Op = Data ; else Op = 1 ’ bz ;
Szakasz 1: RTL always utasítás
161
Procedurális utasítások
fejezet VI: Procedurális utasítások
162
Szakasz 1: Procedurális utasítások
163
1. Procedurális utasítások case A case utasítás nagyon hatékony mód többutas elágaztatásra. A case utasítás elején egy kifejezéssel adjuk meg a lehetséges értékeket. A case utasítás végrehajtásakor annak pontosan egy, a kifejezés értékétől függő ága hajtódik végre. Ez az az ág lesz, amelyiknek a címkéje megegyezik a kifejezés értékével. A címkéket az utasításoktól kettőspont választja el. A default ág, ha jelen van, minden olyan esetre működésbe lép, amelyet valamelyik ág nem választ ki explicit módon. Nem feltétlenül szükséges azonban minden lehetséges esetnek egy ágat megfelelteni, azaz a default utasítás opcionális. A VI.1 programlistán bemutatott példában a default utasítás értelmetlen a szintézis számára, mivel SEL valamennyi lehetséges értéke megjelenik a case utasításban címkeként. A case utasítás útválasztója vektor vagy bármely érvényes Verilog kifejezés is lehet. Szintézis
Szakasz 1: Procedurális utasítások
164
A szintézis ugyanúgy kezeli a case utasítást, mint az if utasítást a case utasításban szereplő minden egyes reg számára multiplexert generál. A case utasítás általános és kényelmes mód kombinációs logikák viselkedésének leírására, amíg a bemenetek száma nem túl nagy. FPGAk Érdemes figyelni arra, hogy bizonyos FPGA architektúrák esetén a bemenetek száma korlátozott (fanin limited), azaz az architektúra olyan logikai blokkokból épül fel, amelyeknek csak (kis) rögzített számú bemenete van. Ha a case utasításban ezt a határt túllépjük, a szintézis több logikai blokkot és logikai szintet fog használni. Lényeges javulást érhetünk el területkihasználás és időzítés szempontjából is, ha a logikával alkalmazkodunk az architektúrához. A VI.2 programlistán bemutatott példa a case utasítás újabb tulajdonságait mutatja meg. Két vagy több címke, vesszővel elválasztva (3’b001, 3’b010, 3’b100 : ...), is tartozhat ugyanahhoz az ághoz. A kifejezés egy adott értéke esetén több utasítást is végrehajthatunk,
Szakasz 1: Procedurális utasítások
165
Programlista VI.1: A case utasítás használati módja always @(SEL or case (SEL) 2 ’ b00 : F = 2 ’ b01 : F = 2 ’ b10 : F = 2 ’ b11 : F = default : F = endcase
A or B or C or D) A; B; C; D; 1 ’bX; // Csak szimulálásra
ha az utasításokat egy begin ... end blokkba zárjuk. Akárcsak az if utasítások esetén, itt is latch-ek generálódnak a szintézis során, ha egy regiszter nem teljes körűen kap értéket egy always blokkban. A casez utasítás a case egy variációja. Szintaxisuk és viselkedésük megegyezik, kivéve, hogy egy számban a Z érték egy „nem érdekes” értéket jelent. Emiatt a Z helyén a ? karaktert használják, mivel az világosabban mutatja az utasítás jelentését.
Szakasz 1: Procedurális utasítások
Programlista VI.2: A case utasítás további tulajdonságai case (Code) 3 ’ b000 : // I t t k e l l begin−end begin P = 1; Q = 1; end 3 ’ b001 , 3 ’ b010 , 3 ’ b100 : // Ezek a l t e r n a t í v á k begin Q = 1; R = 1; end 3 ’ b110 , 3 ’ b101 , 3 ’ b011 : R = 1; // Nincs minden lehetőség j e l e n − OK endcase // Latch tárolókat hozhatunk l é t r e
166
Szakasz 1: Procedurális utasítások
167
Programlista VI.3: A casez utasítás tulajdonságai wire A, B, C, D, wire [ 3 : 0 ] E; r e f [ 1 : 0 ] Op; always @(A or B or C or D or E) casez ( {A, B, C, D, E} ) // Összefűzés i s l e h e t // Az egyes e s e t e k e t sorrendben v i z s g á l j u k // Átfedés megengedett casez 0 1 8 ’ b1 ? ? ? ? ? ? ? : Op = 2 ’ b00 ; 8 ’ b001 ? ? ? 0 0 : Op = 2 ’ b01 ; 0 1 0 8 ’ b0 ? 1 ? ? ? ? 0 : Op = 2 ’ b10 ; 1 0 1 8 ’ b0111 ? ? 1 1 : Op = 2 ’ b11 ; default : Op = 2 ’bXX; X 0 0 // X : nem fontos Z 1 1 // így minden e s e t e t kezelünk endcase // NEM endcasez
X
Z
0 0 1 1
1 1 1 1
Szakasz 1: Procedurális utasítások
168
A VI.3 programlistán bemutatott példában az első címke a case kifejezés bármely olyan értékének megfelel, amelyikben az MSB értéke 1; ez akkor áll elő, amikor A értéke 1. A második és a harmadik címke átfed. Például, 8’b00100000 megfelel mind a 8’b001???00 mind a 8’b0?1????0 értéknek. Az elsőnek van prioritása, így ebben az esetben Op a 2’b01 értéket kapja. A példában szereplő casez a következő módon írható fel if utasításokkal: if (A) Op = 2‘b00; else if (˜A & ˜B & C & ˜E[1] & ˜E[0]) Op = 2‘b01; else if (˜A & C & ˜E[0]) Op = 2‘b10;
Szakasz 1: Procedurális utasítások
169
else if (˜A & B & C & D & E[1] & E[0]) Op = 2‘b11; else Op = 2‘bxx; Összeillesztés (concatenation) Vegyük észre az összeillesztés hatását a case kifejezésben. A case kifejezés egy olyan vektor, amelynek elemeit kapcsos zárójelben ({}) soroljuk fel. Egy másik variáció a VI.4 programlistán látható, a casex, aminek hasonló koncepciója van, mint a casez utasításnak. A VI.3 és VI.4 ábrákon a betét táblázatokban látható a casez és casex utasítások igazságtáblázata. Mint mondottuk, Z bármilyen értéket elfogad egy casez utasításban; Z és X bármilyen értéknek megfelel egy casex utasításban.
Szakasz 1: Procedurális utasítások
170
Programlista VI.4: A casex utasítás tulajdonságai wire A, B, C, D, wire [ 3 : 0 ] E; r e f [ 1 : 0 ] Op; always @(A or B or C or D or E) casex ( {A, B, C, D, E} ) 8 ’b1XXXXXXX: Op = 2 ’ b00 ; 8 ’b010XXXXX: Op = 2 ’ b01 ; 8 ’b001XXX00: Op = 2 ’ b10 ; 8 ’b0111XX11 : Op = 2 ’ b11 ; default : Op = 2 ’bXX; // X es Z : nem fontos endcase // NEM endcasex
casex
0
1
X
Z
0 1 X Z
1 0 1 1
0 1 1 1
1 1 1 1
1 1 1 1
Szakasz 1: Procedurális utasítások
171
casez versus casex A casez utasítástól eltérően, a casex nem használható arra, hogy explicit módon felderítsünk X értékeket egy kifejezésben, mivel a case kifejezésben az X-ek és Z-k "wildcard" karakterként viselkednek (azaz, bármely értéknek megfelelnek). Emiatt casex használata nem javasolt, mivel ha egy kifejezés értéke a szimuláció során nem ismert (ami előfordulhat), az első címkét megfelelőnek találja és azt az értéket hajtja végre. Ugyanez a probléma merülhet fel a casez utasítással kapcsolatban, ha a case kifejezés Z-ket tartalmaz; ez viszont ritkábban fordul elő. A Verilog nyelv case utasításának szintaxisában szokatlan elem, hogy (az utasítás elején álló) case kifejezés lehet konstans is, és (belül) a "címkék’" pedig változók is lehetnek, lásd VI.5 programlista! Egy ilyen stílusú case utasításban előfordulhat, hogy a case kifejezés egynél több címkének felel meg. Ilyen esetben –felülről számítva– az első megfelelőt használjuk. Más szavakkal, a case utasítást prioritás leírására használjuk, és ez egymásba ágyazott if-
Szakasz 1: Procedurális utasítások
172
Programlista VI.5: Prioritás enkóder case utasítás használatával // Prioritás enkóder : // A case választó k i f e j e z é s állandó // a címkék a változó case ( 1 b1 ) A[ 0 ] : F = 2 b00 ; A[ 1 ] : F = 2 b01 ; A[ 2 ] : F = 2 b10 ; A[ 3 ] : F = 2 b11 ; endcase
else utasításokkal egyenértékű, lásd VI.6 programlista. Ha egy case utasítás egymást kölcsönösen kizáró címkéket tartalmaz, akkor azt párhuzamosnak nevezzük; ha valamennyi lehetséges esetet lefedi, akkor pedig teljesnek, lásd VI.7 programlista és betét ábrája. Egy párhuzamos és teljes case utasítás egy vagy több multiplexerként realizálható. A szintézis eszközök általában automatikusan képesek felismerni a párhuzamos és teljes case utasításokat. Ahol
Szakasz 1: Procedurális utasítások
173
Programlista VI.6: Prioritás enkóder ekvivalens if utasítások használatával // Prioritás enkóder : // ekvivalens if kifejezésekkel i f (A[ 0 ] ) F = 2 b00 ; e l s e i f (A[ 1 ] ) F = 2 b01 ; e l s e i f (A[ 2 ] ) F = 2 b10 ; else F = 2 b11 ;
Szakasz 1: Procedurális utasítások
Programlista VI.7: A parallel case programlista // ‘ ‘ a u t o p a r a l l e l , auto f u l l ’ ’ case (A) // a ’ case ’ k i f e j e z é s e k kölcsönösen kizáróak 4 ’ b0001 : F = 2 ’ b00 ; 4 ’ b0010 : F = 2 ’ b01 ; 4 ’ b0100 : F = 2 ’ b10 ; 4 ’ b1000 : F = 2 ’ b11 ; // a ’ default ’ valamennyi l e h e t ő s é g e t figyelembe v e s z i // −> nem hozunk l é t r e latch−e t default : F = 2 ’bxx ; endcase
174
Szakasz 1: Procedurális utasítások
175
a szintézist végző segédprogram nem képes felismerni, hogy a case címkék kölcsönösen kizárják egymást, ott egy prioritás enkódert szintetizál. Ha viszont tudjuk, hogy a címkék valójában kölcsönösen kizárják egymást, ezt az információt megadhatjuk a segédprogram számára, így az ott is multiplexert szintetizál. Programlista VI.8: Prioritás enkóder casez használatával // ‘‘NOT p a r a l l e l , auto f u l l ’ ’ case (A) // a ’ case ’ k i f e j e z é s e k NEM kölcsönösen kizáróak 4 ’b ? ? ? 1 : F = 2 ’ b00 ; 4 ’b ? ? 1 ? : F = 2 ’ b01 ; 4 ’b ? 1 ? ? : F = 2 ’ b10 ; 4 ’ b1 ? ? ? : F = 2 ’ b11 ; // a ’ default ’ valamennyi l e h e t ő s é g e t figyelembe v e s z i // −> nem hozunk l é t r e latch−e t default : F = 2 ’bxx ; endcase
A casez utasítás teljes (van default címkéje), de nem párhuzamos.
Szakasz 1: Procedurális utasítások
176
Például, ha A valamennyi bitje 1 lenne, ez valójában valamennyi címkére illene; így (csak) az első utasítás hajtódna végre, azaz így egy prioritás enkódert írna le. Programlista VI.9: "One-Hot" Dekóder megvalósítása casez használatával // onehotcase : case ( 0 ’ b1 ) // synopsys parallel_case A[ 0 ] : F = 2 ’ b00 ; A[ 1 ] : F = 2 ’ b01 ; A[ 2 ] : F = 2 ’ b10 ; A[ 3 ] : F = 2 ’ b11 ; default : F = 2 ’bXX; endcase // ami e z z e l egyenértékű : F [ 0 ] = A[ 1 ] F [ 1 ] = A[ 2 ]
| A[ 3 ] ; | A[ 3 ] ;
Tegyük fel, hogy az A rendelkezik azzal a tulajdonsággal, hogy
Szakasz 1: Procedurális utasítások
177
bármely időpillanatban valamennyi eleme (pontosan egy kivételével) nulla. Ezt a szintézis segédprogrammal közölnünk kell, különben szükségtelen logikát állít elõ. Szintézis direktíva Ennek egyik módja, hogy szintézis direktíva kerül a Verilog kódba. Ez egy olyan Verilog komment, amit a szimulátor elhanyagol (hiszen csak komment...), a szintézis segédprogram viszont elolvassa, sõt reagál rá (tehát mégiscsak több, mint komment). parallel_case A parallel_case direktíva a case utasításra vonatkozik, és azt mondja meg a szintézis segédprogramnak, hogy a case utasítás címkéi egymást kölcsönösen kizárják. full_case A full_case direktíva a case utasításra vonatkozik. Azt mondja meg a szintézis segédprogramnak, hogy a case utasítás a kiválasztó kifejezés összes lehetséges értékét lefedi.
Szakasz 1: Procedurális utasítások
178
Ezeket a direktívákat nagyon óvatosan kell használnunk, mivel ezek hatására a szintézis segédprogram és a szimulátor ugyanazt a Verilog kódot eltérően értelmezi! A következő direktívákat a legtöbb szintézis eszköz támogatja, bár kicsit eltérő szintaxissal // synopsys parallel_case // synopsys full_case // synopsys full_case parallel_case // synthesis parallel_case // synthesis full_case // synthesis full_case parallel_case // pragma parallel_case // pragma full_case // pragma full_case parallel_case
Szakasz 1: Procedurális utasítások
179
IEEE 1364.1 Az IEEE 1364.1 szabvány ezekre ilyen formát ajánl: // rtl_synthesis parallel_case // rtl_synthesis full_case // rtl_synthesis full_case, parallel_case A for ciklusok olyan szekvenciális utasítások, amelyeket gyakran használunk vektorokkal kapcsolatban. A ciklus belsejében található utasítások több alkalommal ismétlődnek, ahogyan azt a ciklus elején található iterációs séma előírja. A példában a ciklus négyszer ismétlődik, és a ciklusváltozó a 0, 1, 2 és 3 értékeket veszi fel. A ciklusparaméter a cikluson belül olvasható, de nem célszerű azt megváltoztatni. Az IEEE Verilog standard ugyan nem ragaszkodik ehhez a szabályhoz, de erősen ajánlatos betartani azt. A for ciklusok ismétlődő hardver strukturákként szintetizálódnak, feltéve, hogy a ciklushatárok rögzítettek. A VI.10 programlistán a
Szakasz 1: Procedurális utasítások
Programlista VI.10: for ciklus megvalósítása HW ismétléssel // for == HW ismétlése always @(A or B) begin G = 0; for ( I = 0 ; I < 4 ; I = I + 1) begin F [ I ] = A[ I ] & B[3 − I ] ; G = G ^ A[ I ] ; end end
180
Szakasz 1: Procedurális utasítások
181
ciklusbeli első értékadás 4 AND kapuként szintetizálódna. A második értékadás kaszkád szerkezetű XOR kapukként szintetizálódik, mivel a cikluson belül az előző menetben adott érték használódik a következő menetben. Ebben az esetben az optimalizáló képes lenne az XOR-okat ágszerkezetűvé újra strukturálni, a késleltetési pályák kiegyensúlyozására. Bonyolultabb esetben azonban az optimalizálónak nem feltétlenül sikerül egy ilyen kaszkád szerkezetű logikát újrastrukturálni, különösen, ha aritmetikai operáció is szerepel benne, tehát ilyen ciklusokat nagyon figyelmesen kell megírnunk. A szintézis nem képes változó ciklushatárú ciklusokat kezelni, mivel a szintézer nem képes meghatározni, hogy a ciklus tartalmából hány példányt kell generálnia. Egy for ciklusváltozót (itt I) regiszterként kell deklarálnunk. Megpróbálhatnánk I-t reg [1:0]-ként deklarálni, mivel I-t arra használjuk, hogy 0-tól 3-ig számláljunk. Ez nem működne, mivel a regisztereket előjel nélkülinek tekintjük, és a két bittel ábrázolható legnagyobb szám 3. Az I = I + 1 értékadás I "átfordulását’" (2’b11 + 1 = 2’b00) okozza, amikor I értéke maximális. Így aztán I értéke
Szakasz 1: Procedurális utasítások
182
Programlista VI.11: for ciklusváltozó deklarálása // for c i k l u s v á l t o z ó deklarálása // elvben 2 b i t elegendő , de 3+1 = 0 reg [ 1 : 0 ] I ; // nem elegendő reg [ 2 : 0 ] I ;
// elég , DE
f o r ( I = 3 ; I >= 0 ; I = I − 1 ) // esetén már nem használhatunk reg−e t
mindig négynél kisebb lenne, és a szimulált ciklus soha nem fejeződne be. A problémát itt úgy tudjuk megoldani, hogy I szélességét elegendően nagynak választjuk. Még ez sem működne azonban egy olyan ciklusban, ahol a ciklusváltozó értékét nem növeljük, hanem csökkentjük: mivel egy reg előjel nélküli, annak tartalma soha nem kisebb, mint nulla.
Szakasz 1: Procedurális utasítások
Programlista VI.12: for ciklusváltozó deklarálása // d e k l a r á l á s i e l t é r é s : reg [ 3 : 0 ] F ; integer I ;
// 4 b i t e s , e l ő j e l n é l k ü l i // 32 b i t e s , e l ő j e l e s
// ennek eredménye végtelen c i k l u s always @( a ) f o r (F = 3 ; F >= 0 ; F = F − 1 ) F [ I ] = A[ I ] & B[3 − I ] ; // ez rendben van always @(A) f o r ( I = 3 ; I >= 0 ; I = I − 1 ) F [ I ] = A[ I ] & B[3 − I ] ;
183
Szakasz 1: Procedurális utasítások
184
A Verilog rendelkezik egy olyan speciális regiszter típussal, amellyel matematikai egész értéket lehet ábrázolni. A Verilog integer adattípusában tárolt érték pontosan ugyanolyan, mint a reg [31:0] adattípusban tárolt érték, csak éppen az értékek másképpen kezelődnek minden olyan összefüggésben, ahol számokra van szükség; az integer értékek kettes komplemensű előjeles számokként értelmeződnek, a reg értékek pedig előjel nélküli számokként. A reg adattípust kell használnunk hardver ábrázolására, az integer adattípust pedig ciklusváltozóként és ott, ahol tisztán matematikai számra van szükség (pl. text fixtúrában számláló) . A for ciklusutasításon túl, a Verilog három másik ciklusutasítással is rendelkezik: repeat, while és forever ciklusutasítással. A for ciklustól eltérően, ezeket a ciklusutasításokat nem-szintetizálhatónak tekintjük. Nagyon hasznosak azonban test fixtúrákban és magasszintű viselkedési modellekben. while Egy while ciklus mindaddig végrehajtódik, amíg a feltétel teljesül
Szakasz 1: Procedurális utasítások
Programlista VI.13: Egyéb ciklusutasítások // S z i n t é z i s r e csak a ’ for ’ használható : f o r ( i n i t i a l i z e ; condition ; assignment ) ... // A masik három t e x t fixturákba es algoritmus l e í r á s r a való while ( condition ) ... // Amig a f e l t é t e l t e l j e s ü l repeat ( expression ) ... // a megadott számúszor végrehajtódik forever ... // végtelen c i k l u s
185
Szakasz 1: Procedurális utasítások
186
(azaz amíg annak értéke nem nulla). repeat Egy repeat ciklus (az ismétlési kifejezés értékétől függő) rögzített számúszor hajtódik végre. A repeat ciklusok ugyanúgy szintetizálhatók is lehetnek, mint a for ciklusok, de ezt nem mindegyik segédprogram támogatja. forever Egy forever ciklus feltétel nélkül hajtódik végre. Csak egy disable utasítással lehet megszakítani. A disable utasítás egy begin-end blokk végrehajtását állítja meg. Ennek hatása, hogy a disable végrehajtása után következő utasítás az éppen letiltott blokkot követő utasítás. Elnevezett blokk Hogy egy begin-end blokkot tiltani tudjunk, annak névvel kell rendelkeznie. Egy begin-end blokknak úgy adhatunk nevet, hogy a
Szakasz 1: Procedurális utasítások
Programlista VI.14: Blokk végrehajtás megszakítása // Blokk végrehajtás megszakítása begin : Outer // Named block forever begin : Inner // Named block ... disable Inner ; ... disable Outer ; ... end end
187
Szakasz 1: Procedurális utasítások
188
begin kulcsszó után kettőspontot teszünk és odaírjuk a blokk nevét. A disable lehetőséget ad a ciklusokból való kilépésre, akár úgy, hogy a végrehajtást a soron következő iterációjával folytatjuk, akár teljesen befejezzük a ciklus végrehajtását (lásd a C nyelv continue és break utasítását, valamint a VHDL nyelv next és exit utasításait.) Ezt illusztrálja a VI.14 programlista, ahol a disable Inner hatására a ciklus következő iterációjával folytatjuk, a disable Outer hatására pedig teljesen befejezzük a ciklus végrehajtását. A disable arra is használható, hogy egy taszkból visszatérjünk, lásd később. Ha egy begin-end blokknak van neve, akkor azon belül is deklarálhatunk reg vagy integer típusú regisztert. Az így deklarált regisztert csak abban a blokkban lehet használni. Hatókör (Scope) A szoftver mérnökök a hatókör (scope) fogalmat használják erre a koncepcióra. Egy azonosító (pl. regiszter) hatóköre a programszöveg azon tartománya, amelyen belül az azonosító jellemzői hozzáférhetők. A példában reg Tmp hatóköre annak deklarációjától a begin-end
Szakasz 1: Procedurális utasítások
Programlista VI.15: Elnevezett blokk és a deklaráció // Named blockban deklarálhatunk reg−e t begin : blk reg Tmp; i f (~C1) Tmp = A; else Tmp = B; i f (C2 & C3) Tmp = ~Tmp; F = Tmp; end // . . . vagy egészeket i s always @(A) begin : FourAnds integer I ; f o r ( I = 3 ; I >= 0 ; I = I −1) F [ I ] = F [ I ] & A[3 − i ] ; end
189
Szakasz 1: Procedurális utasítások
190
blokk végéig terjed. Két vagy több azonosítót is deklarálhatunk azonos névvel, feltéve, hogy a deklarációk hatóköre eltérő. A Verilog négyféle hatókört definiál: modulok, elnevezett blokkok, taszkok és függvények. A VI.16 lista olyan always utasítást mutat, amit kombinációs logika szintéziséhez használnak. A példa a Verilog utasításainak széles körét használja: vektorokat, összeillesztést, for ciklust és disable utasítást. A betét ábra mutatja a szintetizált kapukat is, optimalizálás után. Egy always utasítás akkor szintetizálható kombinációs logikává, ha teljesülnek a következő feltételek: • Az érzékenységi lista teljes. Más szavakkal, valamennyi kombinációs inputot fel kell sorolni az always utasítás elején az @ utáni listában. • Valamennyi kimenet értéket kap. Valahányszor egy always blokk végrehajtódik (azaz a bemenetek valamelyike megváltozik), valamennyi kimenetnek is értéket kell kapnia. Ha ez nem teljesül, akkor az nem kombinációs, hanem szekvenciális logika és a
Szakasz 1: Procedurális utasítások
Programlista VI.16: Kombinációs always blokk reg [ 1 : 0 ] F ; always @(A or B or C or D) begin : Blk reg [ 3 : 0 ] R; R = {A, B, C, D} ; F = 0; begin : Loop integer I ; for ( I = 0 ; I < 4 ; I = I + 1) i f (R[ I ] ) begin F = I; // Kilép a c i k l u s b ó l disable Loop ; end end // Loop end
191
Szakasz 1: Procedurális utasítások
192
szintézis eredménye valószínűleg latch-eket fog tartalmazni. • Nincs kombinációs visszacsatolás. Ez ismét csak aszinkron szekvenciális viselkedést eredményezne.
Szakasz 1: Procedurális utasítások
193
Órajelek és flip-flopok
fejezet VII: Órajelek és flip-flopok
194
Szakasz 1: Always latch
195
1. ’always’ – latch tárolóval Programlista VII.1: always – latch tárolóval // l a t c h kikényszerítésének "helyes" módja // t e l j e s érzékenységi l i s t a always @(ENB or D or A or B or SEL) i f (ENB) // nem−t e l j e s értékadás begin Q = D; i f (SEL) F = A; else F = B; end // v i s s z a c s a t o l á s n é l k ü l
Ez az always utasítás azt mutatja, hogyan kényszeríthetjük ki latch áramkörök használatát. Az egyetlen különbség a kombinációs logika előállításához képest, hogy szándékosan nem határozzuk meg a kimenet értékét a bemenet minden lehetséges változásának esetére.
Szakasz 1: Always latch
196
Hogy ez előre látható módon történjen, használjunk egy if utasítást, azaz a latch engedélyezési jelét feltételként, mint a példa mutatja. Programlista VII.2: Él-vezérelt Flip-Flop // Így csak kapuzunk always @( Clock ) i f ( Clock ) Q = D; // Így viszont az é l r e reagálunk // Ugyanez van ’ negedge ’ esetén i s always @( posedge Clock ) Q = D;
Bár szimuláció alkalmával a VII.2 programlistán leírt áramkör korrekt módon D-flipflopként viselkedik, lásd a betét ábrát, a szintézis során (ha egyáltalán szintetizálható) transzparens latchet kapunk. Ez azért van, mert a szintézis eszközök nem-teljes eseményvezérlést feltételeznek, mivel D hiányzik a listából. Hogy flip-
Szakasz 1: Always latch
197
flopot állítsunk elő, a szintézis programnak a helyes funkcionalitásnál több útmutatásra van szüksége: a posedge vagy negedge kulcsszavak valamelyikének használatára. Flip-flopot akkor (és csak akkor) állítunk elő, ha az always utasítás tartalmazza a posedge or negedge kulcsszavak valamelyikét az eseményvezérlési listájában. A második példa a legegyszerűbb órajelvezérelt always utasítást mutatja: egy élvezérelt D flipflopot ír le, amelyet a Clock felfutó éle vezérel. A Verilog nem-determinisztikus A Verilog nyelv egyik jellemzője, hogy nem-determinisztikus. Más szavakkal, írhatunk olyan Verilog kódot, amelyik különbözõ szimulátorokon másként viselkedik. Ez nem valamelyik szimulátor hibája, hanem a Verilog nyelv tulajdonsága. A tulajdonság egy jellemző példája, amikor (az ábra szerint) különálló always utasításokkal írunk le flipflop-okat. Amikor egy
Szakasz 1: Always latch
Programlista VII.3: Szimulációs versenyhelyzetek elkerülése // Egyszerre olvassuk es í r j u k b−t ! always @( posedge clock ) b = a; always @( posedge clock ) c = b; // A versenyhelyzet f e l o l d á s a always @( posedge clock ) begin tmpa = a ; #1 b = tmpa ; end always begin tmpb #1 c end // Nem
@( posedge clock ) = b; = tmpb ; ajánlott :((
198
Szakasz 1: Always latch
199
pozitív órajel fordul elő a szimuláció során, mindkét always utasítás végrehajtódik. Nem tudjuk megmondani, hogy a szimulátor melyik utasítást hajtja végre először. Ha az utasítások az ábrán bemutatott sorrendben hajtódnak végre, akkor először b értéke frissül, és b új értékével frissül c értéke. Így tehát b és c értéke azonos lesz. Másrészt viszont, ha az always utasítások fordított sorrendben hajtódnak végre (és a Verilog definíciójában semmi sem tiltja, hogy ez előfordulhasson), akkor c kapja meg b értékét, még mielőtt b értéke felfrissülne a értékével. A szintézis során kétségkívül két flipflop generálódik. Emiatt az RTL-szintű leírás szimulációs eredménye és a szintetizált kapuk eltérhetnek. Mindennek eredményeként tartási idő sértést tartalmazhat az RTL modell. A problémát úgy kerülhetjük el, hogy a mutatott módon átmeneti reg-eket és késleltetéseket helyezünk el az always utasításokban. Ez a módszer működhet (a szintézis ugyan elhanyagolja a késleltetéseket, de azok helyessé teszik a szimulációt); de nem elegáns.
Szakasz 1: Always latch
Programlista VII.4: Nem-blokkoló értékadás // Javasolt megoldás : // nem−blokkoló értékadás always @( posedge clock ) b <= a ; always @( posedge clock ) c <= b ; // Ajánlott : ) )
200
Szakasz 1: Always latch
201
A szimulációs versenyhelyzeteket az RTL (vagy "nem-blokkoló") értékadás operátorának (<=) használatával kerülhetjük el. (Ennek megfelelően a közönséges értékadás operátorát (=) "blokkoló" értékadásnak nevezik; ezeket a fogalmakat majd később tárgyaljuk.) A közönséges (blokkoló) értékadástól eltérően, egy nem-blokkoló (avagy RTL) értékadás nem frissíti azonnal a baloldalt álló reg értékét. Ehelyett, beütemez egy olyan eseményt, amelyik majd a szimulációs időlépés végén fordul elő. Így tehát a példában b és c mindaddig nem kapják meg új értéküket, amíg mindkét értékadás végre nem hajtódott. A VII.5 programlistán és betét ábráján bemutatott két példa azt mutatja, hogyan írhatunk le aszinkron bemenetű flipflopokat. Megjegyezzük, hogy az órajelnek és az aszinkron set/reset jelnek szerepelnie kell az érzékenységi listában, és mindkettőt el kell látni a posedge vagy negedge jelzők valamelyikével. Ez még akkor is igaz, ha a set vagy reset jel él-érzékeny. Ha ilyen jelzőt nem adunk hozzá, a modell szimulációja helytelen lesz.
Szakasz 1: Always latch
Programlista VII.5: Aszinkron értékadás és törlés always @( posedge Clock or posedge Reset ) i f ( Reset ) Q1 <= 0 ; else Q1 <= D; always @( posedge Clock or posedge Set ) i f ( Set ) Q2 <= 1 ; else Q2 <= D;
202
Szakasz 1: Always latch
203
Programlista VII.6: Szinkron és aszinkron akciók always @( posedge Clock or posedge Reset ) i f ( Reset ) // Aszinkron reset Q <= 0 ; else i f (Load) // Szinkron b e t ö l t é s Q <= Data ; else Q <= Q + 1 ;
A VII.6 programlista és betét ábrája egy bonyolultabb, aszinkron resetelésű szinkron logikát mutat. Mint eddig is, a reset aktív éle és a clock aktív éle egyaránt szerepelnek az érzékenységi listában. Az aszinkron bemeneteket a szinkron viselkedés leírása előtt kell megvizsgálnunk. A minta:
Szakasz 1: Always latch
204
always @(active_clock_edge or active_async_edge) if ( async_is_active ) perform_async_operation else perform_synchronous_operation Ha FPGA-t tervezünk, az optimális eredmény eléréséhez szükségünk lesz arra, hogy a Verilog forráskódba architektúra-specifikus tulajdonságokat is beírjunk. Például, a Xilinx FPGA eszközeiben van egy STARTUP blokk, ami a dedikált globális set/reset hálózatot (dedicated global set/reset net, GSR) vezérli. Ha ezt működésbe hozzuk, a GSR az eszköz valamennyi flip-flopját beállítja vagy törli. A STARTUP blokk konfigurálható úgy, hogy a GSR az eszköz normál működése során is aktivizálható legyen. Az FPGA szintézis segédprogramok a GSR használatát általában automatikusan végzik. Vannak azonban olyan esetek, ahol az automatikus működtetés nem célszerű, és a globális resetelést explicit módon példányosítani kell, lásd VII.7 ábra.
Szakasz 1: Always latch
Programlista VII.7: Technológia-specifikus tulajdonságok // A s z i n t é z i s számára " f e k e t e doboz" module Block1 ( Clock , Reset , D, Q) ; input Clock , Reset , D; output Q; ... // Technológia . s p e c i f i k u s komponens példány STARTUP Dummy( .GSR( Reset ) ) ; always @( posedge Clock or posedge Reset ) i f ( Reset ) Q <= 0 ; else Q <= D; ... endmodule
205
Szakasz 1: Always latch
206
A fenti Verilog kód a Xilinx STARTUP modul egy olyan példányát mutatja, ahol az első (GSR) input a Reset-hez kapcsolódik. Ez szimuláció esetén nem játszik szerepet, de megmondja a Xilinx helyfoglaló és útvonaltervező eszközeinek, hogy a reset útvonaltervezésekor használja a globális GSR-t. Sajnos, ez azt is jelenti, hogy a Verilog már nem technológiafüggetlen. Ezek a minták azt mutatják, hogyan hozhatunk létre kombinációs logikát, transparens latch tárolókat és flipflopokat a viselkedést leíró Verilog kóddal. Amikor szintetizálás céljából írunk Verilog kódot, az utasításoknak a fenti minták valamelyikét kell követniük. Az IEEE 1364.1 RTL szintézis standard definiálja, milyen mintákat kell a szintézis eszközöknek támogatni. Megjegyezzük, hogy kombinációs logikák esetén folyamatos értékadások is használhatók.
Szakasz 1: Always latch
VII.1. ábra. Szintézis minták
c
2004, Doulos Verilog
207
Szakasz 1: Always latch
VII.2. ábra. Szintézis minták
c
2004, Doulos Verilog
208
Szakasz 1: Always latch
209
Az RTL szintézis eszközök tipikusan az ábra szerinti két fázisban működnek. Az első fázisban (tipikusan HDL szintézis vagy fordítás) beolvassák a HDL forráskódot és átalakítják technológia-független strukturális ábrázolássá. Ez az ábrázolás általában olyan technológiafüggetlen primitívek hierarchikus hálózatából áll, mint NAND kapuk, D flipflopok, összeadók, multiplexerek, stb. A második fázis ezt a közbülső reprezentációt primitívek optimalizált hálózatává alakítja, amelyet egy speciális, cella alapú ASIC technológián (manapság leginkább CMOS kapu tömbökön és FPGAkon) alapuló könyvtárból vesz. A második fázis optimalizációt és leképzést is végez. Az optimalizálás egy többszintű Boole-minimalizáláson alapuló technika, ahol a Boole-egyenletek többszintű hálózatának szerkezetét úgy változtatják meg, hogy a használt terület (a közös Boole-tagok megosztásával) minimális legyen, és (a kritikus útvonalon levõ logikai szintek számának csökkentésével és gyorsabb cellák használatával) megfeleljen az időzítési követelményeknek. Az „állapot kódolás” (state encoding) technikáját használják arra, hogy véges állapot automatákat az
Szakasz 1: Always latch
210
állapotregiszter kódolásának változtatásával optimalizáljanak. VII.3. ábra. RTL szintézis
c
2004, Doulos Verilog
Egy RTL szintű leírásban valamennyi műveletet explicit módon
Szakasz 1: Always latch
211
órajelekkel szinkronizálni kell - ez az RTL munkamegfogalmazása. Az RTL lényege, hogy definiáljuk a regisztereket és az egyes órajelek alkalmával történő átviteleket a regiszterek között (azaz a kombinációs logikát). Az RTL szintézis eszközök a regiszterek létezését közvetlenül a Verilog forráskódból vonják le, néhány egyszerű szabály használatával. RTL optimalizálás Az RTL szintézis eszközök megtartják a Verilog nyelven megadott szerkezeteket. Más szavakkal, az RTL szintézis folyamata nem hoz létre és nem töröl regisztereket, azokat nem vonja össze, mozgatja és optimalizálja. Az RTL szintézis folyamata a regiszterek közötti kombinációs logikát optimalizálja. Néhány segédprogram az itt leírtaknál több funkciót is végez; például, mint később látni fogjuk sok (de nem mindegyik) segédprogram összevonja az egyenértékű flipflopokat. Amikor egy órajellel szinkronizált procedurális blokkban regeknek értéket adunk, azzal valószínűleg flipflopokat állítunk elő. Hogy a
Szakasz 1: Always latch
Programlista VII.8: Flip-flopok kikényszerítése reg Up; reg [ 1 1 : 0 ] Acc , nextA ; always @( posedge SampleClock ) begin i f (Up) begin nextA = Acc + Delta ; i f (nextA >= Max) Up <= 0 ; end else begin nextA = Acc − Delta ; i f (nextA [ 1 1 ] == 1 ’ b1 ) // nextA < 0 Up <= 1 ; end Acc <= nextA ; end
212
Szakasz 1: Always latch
213
szintézis során egy regből flipflop vagy vezeték lesz-e, az attól függ, hogyan használjuk. Azokból a regekből lesznek vezetékek, amelyeknek új értéket adunk, mielőtt azokat az egyes órajel ciklusokban olvasnánk (a fenti példában NextA). Azokból a regekből lesznek flipflopok, amelyeket azelőtt olvasunk, hogy bármelyik órajel ciklusban értéket kapnának (a fenti példában Up). Végezetül, azok a regek, amelyek nem-blokkoló értékadással kapnak értéket, feltétlenül flipflopokat generálnak. Hogy egy blokkoló értékadással flipflopot állítunk-e elő, az attól függ, hogy a reg értékét meg kell-e jegyezni az órajelek között. • Ha egy aktív órajel hatására egy regnek azelőtt adunk értéket, mielőtt az értéket használnánk, akkor nem állítódik elő flipflop. • Ha egy reg értékét azelőtt használjuk, mielőtt új értéket rendelnénk hozzá, akkor a felhasznált érték az lesz, amit az előző órajel hatására rendeltünk hozzá. Emiatt flipflopra van szükség. Nem-blokkoló értékadások esetén a flipflop előállítás szabálya egyszerű: mindig flipflop állítódik elõ.
Szakasz 1: Always latch
214
Programlista VII.9: Flip-flopok kikényszerítése - blokkoló értékadás // Write before read => no f l i p −f l o p always @( posedge Clock ) begin Tmp = ~(IP1 & IP2 ) ; OP1 <= Tmp | IP3 ; end // Read before write => f l i p −f l o p always @( posedge Clock ) begin OP1 <= Tmp | IP3 ; Tmp = ~(IP1 & IP2 ) ; end
Szakasz 1: Always latch
215
Programlista VII.10: Flip-flopok kikényszerítése - blokkoló értékadás // A nem−blokkoló értékadás minding f l i p −f l o p o t hoz l é t r e always @( posedge Clock ) begin Tmp <= ~(IP1 & IP2 ) ; OP1 <= Tmp | IP3 ; end // Az értékadás sorrendje nem számít always @( posedge Clock ) begin OP1 <= Tmp | IP3 ; Tmp <= ~(IP1 & IP2 ) ; end
Szakasz 1: Always latch
216
Mivel nem-blokkoló értékadás használata esetén mindig flipflop állítódik elő, ajánlatos mindig nem-blokkoló értékadást használni, amikor flipflopokra van szükségünk. Megjegyezzük, hogy amikor nem-blokkoló értékadást használunk, az utasítások sorrendje nem számít, mivel nem-blokkoló értékadással értéket kapó reg-ek értéke nem azonnal frissül. Ha egy reg blokkoló értékadásban kap értéket, és azon az always blokkon kívül is használjuk, amelyben értéket kap, akkor flipflop generálódik, még akkor is, ha nem is kell tárolni a reg értékét az órajelek között. A fenti példában Tmp értékét folytonos értékadással kapja meg OP2, így flip-flop generálódik. Nem igazán célszerű azonban ilyen módon előállítani flipflopokat, mivel ilyenkor szimulációs versenyhelyzet állhat elő. Helyesebb a harmadikként megadott módon újraírni a kifejezést, mivel ebből világosan látszik, hogy
Szakasz 1: Always latch
Programlista VII.11: Flipflop előállítás - blokkoló értékadással // Olvasás e l ő t t i írás : => nincs FF always @( posedge Clock ) Tmp csak az always belsejében begin Tmp = ~(IP1 & IP2 ) ; OP1 <= Tmp | IP3 ; end // Reg az always−en k í v ü l : k e l l FF always @( posedge Clock ) Tmp csak az always belsejében begin Tmp = ~(IP1 & IP2 ) ; OP1 <= Tmp | IP3 ; end assign OP2 = Tmp; //Tanácsos így ú j r a í r n i : always @( posedge Clock ) begin Tmp <= ~(IP1 & IP2 ) ; OP1 <= Tmp | IP3 ; end assign OP2 = Tmp;
217
Szakasz 1: Always latch
218
• ebből flipflopot kell generálni • nem áll elő szimulációs versenyhelyzet Programlista VII.12: Flipflop előállítás - blokkoló értékadással always @( posedge CLOCK) begin i f ( !RESET) COUNT = 0 ; else COUNT = COUNT + 1 ; OUT <= COUNT[ 7 ] ; end
A flipflopok szintézisére vonatkozó fenti szabályokat követve 8 flipflopot állítunk elő reg COUNT, és egyet reg OUT esetén. Megjegyezzük azonban, hogy COUNT[7] és OUT értéke mindig azonos, így az azoknak megfelelő flipflopok D kimenete össze van kötve.
Szakasz 1: Always latch
219
Sok szintézis segédprogram lehetővé teszi, hogy a flipflopok D kimeneteit összekössük, és ebben a helyzetben valóban 9 flipflopot állítsunk elő. Más segédprogramok azonban automatikusan összevonják azokat a flipflopokat, amelyeknek a D kimenete össze van kötve, így csak 8 flipflop generálódik. Programlista VII.13: Flipflop előállítás - blokkoló értékadással always @( posedge Clock ) begin Q <= Data ; QB <= not Data ; end
A fenti példában Q és QB egyaránt flipflop! Óvatosnak kell lennünk RTL Verilog programunk írásakor, ha el akarjuk kerülni nem kívánt flipflopok használatát. A szintézis programok nagyon buták tudnak lenni, ha flipflopokról van szó. Nekünk kell eldöntenünk, hány flipflopra van szükségünk és azokat hogyan kell összeszervezni, és
Szakasz 1: Always latch
220
ennek megfelelően megírni a Verilog kódot. Programlista VII.14: Kézi optimalizálás always @( posedge Clock ) Q <= Data ; assign QB = !Q;
Hogy ebben a szituációban megszabaduljunk a nemkívánatos flipflopoktól, a Q és QB egyikének adhatunk értéket az órajelvezérelt always blokkon belül. A megoldás, hogy a QB értékadását az always blokkon kívülre visszük és folyamatos értékadássá tesszük, így elkerülhetjük a QB külön flipflopját. Ha Q és QB kimenetű flipflopok elérhetők a cél eszköz technológiájában, a szintézis eszköznek tudnia kell azokat használni. A legtöbb CPLD és FPGA eszköz esetén csak a Q kimenet elérhető, így ez az optimalizálás nem hajtódik végre.
Szakasz 1: Always latch
221
Operátorok
fejezet VIII: Operátorok
222
Szakasz 1: Operátor
223
1. Operátorok Már találkoztunk bitenkénti operátorokkal, de csak skaláris operandusok esetén. Vektorok esetén a bitenkénti operációk külön-külön kezelik a vektor operandusok egyes bitjeit. Például, egy két vektorra vonatkozó bitenkénti and (&) vektor eredményt hoz létre, ahol az eredmény minden egyes bitje logikai and kapcsolatban van a két operandus megfelelő bitjeivel. Hasonlóképpen, egy vektor bitenkénti inverziója (˜) egyszerűen invertálja a vektor valamennyi bitjét; más szavakkal előállítja annak egyes komplemensét. Redukciós operátorok Eléggé zavaró módon, &, | és ^ unáris redukciós operátorként is használhatók. Egy redukciós operátornak egyetlen –vektor– operandusa van, és egybites eredményt hoz létre. Például, az and (&) redukciós operátor egy vektor bitjeit hozza
Szakasz 1: Operátor
VIII.1. ábra. Bitenkénti és redukciós operátorok
c
2004, Doulos Verilog
224
Szakasz 1: Operátor
225
and kapcsolatba. Az eredmény mindig 1’b0, hacsak nem minden egyes bit értéke ’1’. Az egyetlen valóban használható redukciós operátor ^, ami paritásbitet állít elő. A bitenkénti operátoroktól eltérően, a logikai operátorok logikai (bool) mennyiségeknek tekintik operandusaikat. Egy skalárt TRUE értékűnek tekintünk, ha az nem nulla, és FALSE értékűnek, ha nulla. Az X és Z értékű bitek ismeretlen értékűek (sem nem TRUE, sem nem FALSE). Egy logikai operátorokat tartalmazó kifejezés értéke lehet 1’b1 (TRUE), 1’b0 (FALSE) vagy 1’bX (ismeretlen). Amikor egy ismeretlen értéket vizsgálunk, például egy if utasításban, annak értékét FALSE-nak tekintjük. A not operátor (!) olyankor hasznos, amikor azt akarjuk megvizsgálni, hogy egy kifejezés értéke nulla-e. Megjegyezzük, hogy egybites mennyiségek esetén a &&, || és ! operátorok egyenértékűek a megfelelő &, | és ˜ operátorokkal.
Szakasz 1: Operátor
VIII.2. ábra. Logikai operátorok
c
2004, Doulos Verilog
226
Szakasz 1: Operátor
VIII.3. ábra. Összehasonlító operátorok
c
2004, Doulos Verilog
227
Szakasz 1: Operátor
228
A Verilog kétféle egyenlőség operátort használ. A == és != logikai egyenlőség operátorok vektorok értékét hasonlítják össze. Az ilyen összehasonlítás eredménye TRUE (ha az értékek megegyeznek), FALSE (ha nem egyeznek) vagy ismeretlen (ha valamelyik vektor X vagy Z értéket tartalmaz). === és !== a bitpozíciók egyenlőségét vizsgáló operátorok. Két vektor akkor és csak akkor egyenlő, ha valamennyi bitjük azonos, beleértve az X és Z értékű biteket is. == ugyanazt az eredményt adja, mint ===, valamint != is ugyanaz, mint !==, ha az összehasonlított vektorokban nem szerepel X és/vagy Z értékű bit. Ettől eltérő esetben == és != eredménye ismeretlen, míg === és !== határozott eredményt ad. Az {} összeillesztés operátort használjuk arra, hogy két vektort (vagy egybites értéket) egy hosszabb vektorrá illesszünk össze. A két vektor végpontját egymás után illesztjük, lásd a VIII.4 ábrán az F = {A, B}; értékadást, ahol A értékét az F baloldali nyolc bitjébe másolódik, B értéke pedig az F jobboldali bitjeibe. Megjegyezzük,
Szakasz 1: Operátor
VIII.4. ábra. Összeillesztés (Concatenation)
c
2004, Doulos Verilog
229
Szakasz 1: Operátor
230
hogy hibának számít, ha egy összeillesztésben méret nélküli egész értéket használunk. Az ábra második része egy folyamatos értékadás bal oldalán használt összeillesztést mutat. A COUT vezeték fogadja az értékadás jobb oldalán álló összegből származó átvitel bitet. Ennek oka, hogy az (A + B + CIN) összeg szélességét az értékadás baloldalának ( {COUT, SUM} ) szélessége már megadja. A másolatkészítés operátora az összeillesztés operátorának kiterjesztése. Ez is kapcsos zárójeleket (pontosabban két párat) használ. A belsõ összeillesztés az első zárójel után megadott számúszor ismétlődik. A belső kapcsos zárójelek egy összeillesztést rejtenek: legálisak az olyan kifejezések is, mint {10{A,B}}. Az alsó ábra azt mutatja, hogyan írhatjuk le egy nyolc bites reg előjelének kiterjesztését 16 bitre. Ez az előjelkiterjesztés biztosítja, hogy a kisebb reg előeles értéke a kiterjesztés után is helyes legyen. A kettes komplemenst (előjeles számokat) használó Verilog modellekben
Szakasz 1: Operátor
VIII.5. ábra. Másolatkészítés (Replication)
c
2004, Doulos Verilog
231
Szakasz 1: Operátor
232
gyakran van szükség előjelkiterjesztésre. Vegyük észre, hogy a wordnek értéket adó kifejezés egy {8{byte[7]}} másolat és egy egyszerű kifejezés (byte) összeillesztése. A léptető regiszterek is összeillesztéssel írhatók le, mint látható. Az értékadás jobb oldala egy nyolcbites vektor, amelynek SR[6] az MSB és SerialIn az LSB bitje; ezt az értéket kapja meg a baloldal (SR). Így SR[6] bemásolódik SR[7]be, SR[5] pedig SR[6]ba, stb, végül SerialIn pedig SR[0]ba. A Verilog két léptető operátorral rendelkezik: << balra léptet, >> pedig jobbra. A bal operandust a jobb oldalon megadott operandusnak megfelelő számú bittel eltolja. Az üresen maradó bitek nullával töltődnek fel. Ebben a példában az összeillesztés operátor felhasználásával az eltolás egyetlen utasítással írható le, de a shift operátor használatával két utasítás szükséges. Szigorúan véve, a fordítóprogram direktívái nem a Verilog nyelv részei, hanem a Verilog fordítóprogramnak szóló utasítások. A
Szakasz 1: Operátor
VIII.6. ábra. Léptető regiszterek (shift register)
c
2004, Doulos Verilog
233
Szakasz 1: Operátor
VIII.7. ábra. Léptető operátorok (shift operators)
c
2004, Doulos Verilog
234
Szakasz 1: Operátor
VIII.8. ábra. ‘define és ‘include
c
2004, Doulos Verilog
235
Szakasz 1: Operátor
236
fordítóprogram direktíváit egy fordított egyes idézőjel (apostrophe, "back-tick", grave accent character) (‘) előzi meg, és ezek mindegyike külön sort alkot. Legyünk figyelemmel arra, hogy ez nem azonos a normál idézőjellel avagy a fordított vesszővel (’), amit olyan számokban használunk, mint 8’bx. Egy Verilog forráskódban (és néhány segédprogram esetén még a fájl határokon túl is) egy fordítóprogram direktíva mindaddig hatályos, amíg egy másik fordítóprogram direktíva felül nem írja. ‘define A ‘define egy szöveges makrót deklarál. Egy szöveges makró egyszerűen egy karakter sztring. Amikor a makró név, amit szintén egy fordított apostrophe (‘) karakternek kell bevezetnie, megjelenik egy Verilog forráskódban, a fordítóprogram azt a megfelelő makró sztringre cseréli. Ne feledjük, hogy a makró sztring végén nincs pontosvessző (;). Ha lenne, azt egyszerűen a sztring részének kellene tekinteni és ennek megfelelően be is helyettesíteni! Ha megfelelelően használjuk, a ‘define használatával Verilog forráskódunk könnyebben
Szakasz 1: Operátor
237
olvashatóvá és karbantarthatóvá válik. Ez a direktíva az ‘ifdef direktívával kapcsolatban is használható, lásd később. ‘include Az ‘include utasítást kettős idézőjelek közé tett fájlnév követi. A fordítóprogram egyszerűen az ‘include utasítás helyére írja a fájl tartalmát. Ez olyan, mintha egy szövegszerkesztõvel erre a helyre beillesztenénk a fájl tartalmát. Az ‘include utasítás egy szokásos használata, hogy a definíciókat csak egyszer írjuk le és szükség szerint több helyen is felhasználjuk. ‘ifdef Az ‘ifdef feltételes fordítást tesz lehetővé, amikor is a Verilog forráskód bizonyos sorai csak bizonyos feltételek megléte esetén fordítódnak le. Az ‘ifdef egyik felhasználása, hogy egy bizonyos alkalmazás tartalmazzon-e diagnosztikai kódot. Az ‘ifdef utasítást egy szöveg makró neve követi. (megjegyezzük,
Szakasz 1: Operátor
238
VIII.9. ábra. ‘ifdef
c
2004, Doulos Verilog
Szakasz 1: Operátor
239
hogy amikor az ‘ifdef utasításban használunk makrót, a fordított aposztróf nem szükséges.) Ha a makrót már definiáltuk, a következő utasítások egészen a következő ‘endif utasításig lefordulnak, a makró aktuális értékétől függetlenül. ‘else Amikor egy ‘else direktíva is jelen van, az ‘ifdef és ‘else közötti részek fordulnak le, ha a makro definiálva van, különben pedig az ‘else és ‘endif közötti részek fordulnak le. Az ‘ifdef utasítások egymásba ágyazhatók Egy Verilog utasítássorban definiálhatunk egy ‘define makrót, és esetleg értéket is adhatunk neki. A részletek a használt Verilog szimulátortól függenek. A fenti példa a Cadence Verilog-XL szimulátor esetére vonatkozik.
Szakasz 2: Paraméterek
240
2. Paraméterek A paramétereket modulokban definiáljuk, és azok megőrzik értéküket a szimuláció során. Deklarációkban és kifejezésekben a paraméterek a literális konstansok helyett használhatók. A paraméterek és a ‘define makrók általában felcserélhetők. A paraméterek jobban használhatók, míg a ‘define makrók globálisak is lehetnek. A paraméterek felülírhatók, lásd később. Íme néhány példa, ahol paramétereket használunk a literális értékek helyett. Helyes gyakorlat, hogy fontos literális konstansoknak (ezeket néha "mágikus számnak’" is nevezik) nevet adunk. Ettõl Verilog kódunk egyszerűbben olvashatóvá és karbantarthatóvá válik. Erre a célra mind ‘define makrók, mind paraméterek használhatók. Vegyük észre a másolat használatát, amikor új értéket adunk a BusWire változónak. Paraméternév nem használható egy szám méretének megadására, helyette az ábrának megfelelően használjunk
Szakasz 2: Paraméterek
VIII.10. ábra. Paraméterek
c
2004, Doulos Verilog
241
Szakasz 2: Paraméterek
VIII.11. ábra. A paraméterek használata
c
2004, Doulos Verilog
242
Szakasz 2: Paraméterek
243
másolatkészítést. A modulokat általában hardver blokkok vagy teszt fixtúrák ábrázolására használjuk. A modulokat használhatjuk arra, hogy összetartozó értékeket (pl. paramétereket) egy helyre csoportosítsunk, amikre azután hierarchikus nevekkel hivatkozhatunk. A hierarchikus neveket úgy képezzük, hogy a paraméter neve elé írjuk a modul nevét (Codes) és egy pontot. Például, Codes.Add a Codes modul Add paraméterére hivatkozik. Ezt a modult a terv többi részében szimuláljuk, azt sehol sem kell példányosítani. Megjegyezzük, hogy a szintézis eszközök általában nem támogatják a hierarchikus neveket. A módszer egy alternatívája az ‘include direktíva használata. A paraméter meghatározásokat egy különálló fájlban is elhelyezhetjük és az ‘include direktíva használatával beszúrhatjuk ott, ahol az szükséges.
Szakasz 2: Paraméterek
VIII.12. ábra. Paraméterek használata más modulokban
c
2004, Doulos Verilog
244
Szakasz 2: Paraméterek
VIII.13. ábra. Paraméterek egy különálló fájlban
c
2004, Doulos Verilog
245
Szakasz 2: Paraméterek
246
Megjegyezzük, hogy (a C #define utasításától eltérően) az ‘include utasítások a modulon kívül és azon belül is elhelyezhetők. Ez a Decode modul az input paraméter szélességére az A, az output szélességére a F paramétert használja. Az OutWidth paramétert Width értékéből származtatjuk, és a Width és Polarity paraméterek után deklaráljuk, aminek okát rövidesen látni fogjuk. Van egy, a dekóder polaritását megadó paraméter is: ha Polarity értéke 1, a modul egy "one-hot" dekódert ír le (egy bit 1 értékű, az összes többi nulla); ha Polarity értéke 0, egy "one-cold" dekódert írunk le. Figyeljük meg a másolás használatát, hogy F valamennyi bitjét 1 értékűvé állítsuk, amikor Polarity értéke 0. A paraméterek ilyen használata megkönnyíti a modul részleteinek módosítását - csak a paraméterek értékét kell megváltoztatnunk. Amit szintén érdemes észrevenni, hogy – bár a Verilogban nincs hatványozás ("power of") operátor – a kettő hatványai leírhatók a balra léptető operátor használatával.
Szakasz 2: Paraméterek
VIII.14. ábra. Paraméterezett modul
c
2004, Doulos Verilog
247
Szakasz 2: Paraméterek
VIII.15. ábra. Paraméterek felülírása
c
2004, Doulos Verilog
248
Szakasz 2: Paraméterek
249
Amikor egy paramétereket tartalmazó modult példányosítunk, azok értékét felülírhatjuk a (#) operátor használatával. A fenti példában #(4, 0) azt jelenti, hogy a D1 példány esetében Width (a Decode modulban deklarált első paraméter) a 4 értéket kapja 1 helyett; a második paraméter, Polarity pedig a 0 értéket kapja. A Decode többi példánya változatlan marad. Figyeljünk arra, hogy ne írjunk felül olyan származtatott paramétereket, mint OutWidth: a modul példánya nem fog helyesen működni! Ezért kell a származtatott paramétereket a felülírható paraméterek után deklarálni. A név szerinti leképezés (ami akkor használható, amikor vezetékeket és reg-eket portokhoz kapcsolunk egy modul példányban) nem használható paraméter felülírás esetén. defparam Paramétereket a defparam használatával is felülírhatunk. A defparam utasítások általában a paraméterek hierarchikus nevét
Szakasz 2: Paraméterek
250
VIII.16. ábra. defparam
c
2004, Doulos Verilog
Szakasz 2: Paraméterek
használják.
251
Szakasz 3: Hierarchia
252
3. Hierarchia Mint már láttuk, a paraméterekre hierarchikus nevükkel is hivatkozhatunk. Valójában a modul hierarchián belül akárhonnét hivatkozhatunk bármely, névvel rendelkezõ Verilog objektumra. Ez lehetővé teszi, hogy Verilog modulokba és elnevezett blokkokba „betörhessünk”, azaz az ezekben deklarált adatokat kívülről, portok és argumentumok nélkül használjuk. Ez általában nagyon rossz gyakorlat, kivéve a teszt fixtúrákat, ahol rendkívül hasznos. Egy adat teljes hierarchikus neve a legfelső modul nevével kezdődik, majd a hierarchiában lefelé haladva, a példány nevek és blokk nevek következnek. Egy hierarchikus nevet úgy is képezhetünk, hogy a modul példány vagy az elnevezett blokk nevével kezdünk. Például, egy teszt fixtúrában egy $display utasítás úgy hivatkozhat a szimulált terv hierarchiájában az adatokra, hogy hierarchikus neveket használ. Egy Verilog szimulációban egynél több felső szintű modul is
Szakasz 3: Hierarchia
VIII.17. ábra. Hierarchikus nevek
c
2004, Doulos Verilog
253
Szakasz 3: Hierarchia
254
lehet! Két ilyen hierarchia egymással hierarchikus neveket használva kommunikálhat. Az ábrán az Annotate modulban levő nevek a terv Test nevű felső szintű moduljában levő paraméterekre hivatkoznak. Az eddig látott hierarchikus nevek mind teljes, vagy pedig lefelé irányuló hivatkozások voltak: Ezek a hierarchikus nevek vagy a felső szintű modulból indulnak vagy pedig annak a modulpéldánynak a nevével vagy a modulbeli blokk nevével, amelyben a hierarchikus nevet használjuk. A Verilog a felfelé irányuló név hivatkozásokat is támogatja. Ez azt jelenti, hogy olyan modulra vagy elnevezett blokkra is hivatkozhatunk, amelyik a hierarchiában magasabban helyezkedik el, a modul példányának nevét, a modul nevét vagy egy blokk nevét használva.
Szakasz 3: Hierarchia
VIII.18. ábra. Felfelé irányuló név hivatkozások
c
2004, Doulos Verilog
255
Szakasz 3: Hierarchia
256
Automaták
fejezet IX: Automaták
257
Szakasz 1: RTL always utasítás
1. RTL always utasítás IX.1. ábra. Véges állapotú automaták
c
2004, Doulos Verilog
258
Szakasz 1: RTL always utasítás
259
A véges állapotú automata a digitális logikai áramkörtervezés hasznos segédeszköze. Mint a neve is mutatja, egy véges állapotú automata (Finite State Machine, FSM) olyan digitális logikai áramkör, amelynek véges számú belső állapota van. A FSM bemeneteinek értékét és pillanatnyi állapotát használja arra, hogy meghatározza kimeneteinek értékét és következő állapotát. Csak a szinkron állapotgépekkel foglalkozunk, ahol az állapot az órajel aktív élének hatására változik. Egy véges állapotú automata hardverként egy állapotregiszterből, valamint a következő állapot kiszámítására szolgáló kombinációs logikából és a kimenetek értékének kiszámítására szolgáló kombinációs logikából áll. Moore kimenetek A Moore kimenetek csak az automata pillanatnyi állapotától függenek, azaz a kimenetek csak akkor változnak, amikor az automata állapotot vált. Mealy kimenetek A Mealy kimenetek az automata pillanatnyi állapotán kívül
Szakasz 1: RTL always utasítás
260
a bemenetek pillanatnyi értékétől is függenek, azaz a bemenetek értékének megváltozása (valamennyi kombinációs késleltetés után) a kimenetek megváltozását okozhatja, anélkül, hogy meg kellene várnunk a következő állapotátmenetet. Egy véges állapotú automatát jól ábrázolhatunk az IX.2 ábra szerinti állapotátmenet diagramokkal. A diagramon szereplő "buborékok" az automata különböző belső állapotait ábrázolják. A buborékokat összekötő nyilak az állapotátmeneteknek felelnek meg, a nyilakhoz fűzött magyarázó szövegek (pl. Start = 1) pedig azokat a bemeneti feltételeket adják meg, amelyek hatására az adott állapotátmenet végbemegy. Az egyes állapotoknak megfelelő buborékoknak van egy szimbolikus neve (pl. Idle) és szerepelnek bennük az abban az állapotban érvényes kimeneti értékek. Ezek Moore kimenetek, mivel a kimeneti érték csak attól az állapottól függ, amelyben az automata éppen van. A Mealy kimenő értékeket az átmenetet ábrázoló nyilakhoz fűzött szövegben mutatnánk. Az állapotdiagramok használata néha előnyösebb, mint a rövid leírásoké. Például, azok az átmenetek, amelyek nem egy másik állapot-
Szakasz 1: RTL always utasítás
IX.2. ábra. Véges állapotú automaták
c
2004, Doulos Verilog
261
Szakasz 1: RTL always utasítás
262
ból, hanem egy pontból indulnak (pl. Reset = 1), globális átmenetek. Ezeknek magasabb a prioritása, mint a többi bemeneti feltételnek, és hatásukra az automata bármelyik másik állapotból közvetlenül a megadott állapotra ugrik. Egy másik jelölésbeli rövidítés, hogy ezen az állapotátmenet diagramon a kimeneteket nulla alapértelmezett értékűnek tekintjük. A diagram csak a nem-nulla értékű kimeneteket mutatja explicit módon. A Verilog nyelven általában úgy írunk le egy véges állapotú automatát, hogy egy always utasításba case utasításokat teszünk. Az automata állapotát egy állapotregiszterben tároljuk, és valamennyi lehetséges állapotot paraméter értékekkel írunk le. Inicializálás A véges állapotú automatákat explicit módon inicializálni kell egy reset jellel. Enélkül nincs garancia arra, hogy az automata valamiféle ismert állapotba kerül, így az állapotot egyenlőségét sem vizsgálhatjuk. A véges állapotú automatát a most adott leírás szerint egy órajel
Szakasz 1: RTL always utasítás
263
Programlista IX.1: Az automata állapotainak explicit megadása parameter I d l e = 2 ’ b00 , Go1 = 2 ’ b01 , Go2 = ’ b10 ; Az 2állapotgépben reg [ 1 : 0 ] State ; kell reset ... lehetőségnek lenni always @( posedge Clock or posedge Reset ) i f ( Reset ) begin State <= I d l e ; F <= 0 ; G <= 0 ; end else case ( State ) I d l e : i f ( Start ) begin State <= Go1 ; F <= 1 ; end Go1 : begin State <= Go2 ; F <= 0 ; G <= 1 ; end Go2 : begin State <= I d l e ; G <= ’ 0 ’ ; end endcase
Szakasz 1: RTL always utasítás
IX.3. ábra. Az automata felépítése
c
2004, Doulos Verilog
264
Szakasz 1: RTL always utasítás
265
vezérelt always utasítás, a State állapotvektort ábrázoló Verilog regiszterek, valamint az F és G kimenetek írják le. A szintézis ezekhez a regiszterekhez flip-flopokat állít elő, amelyek biztosítják, hogy mind az állapotátmeneteket, mind a kimenetek értékét az órajel szinkronizálja. A kimenetek esetén azonban nemkívánatos lehet a flip-flopok jelenléte! A tankönyv szerinti Moore automata csak az állapotvektort tartalmazó regiszterekből, a következő állapotot dekódoló kombinációs logikából és az adott állapotból származó kimenetekből áll. A nemkívánatos flipflopok kiküszöbölésére át kell írnunk a Verilog kódot. A flip-flopok kiküszöbölésére a leírást legalább két always utasítással kell megvalósítanunk. Ennek elérésre különböző módszerek vannak, a két leginkább használatosat az előző és a következő ábrák tartalmazzák. Az IX.2 listán szereplő kód az előző állapotgép egy olyan kódolását mutatja, amelyik két always utasítást használ. Az első leírja az állapotvektort és az állapotátmeneteket, a másik pedig a kimenet dekódoló logikát. Mivel a kimeneti always utasítás így már tisztán
Szakasz 1: RTL always utasítás
266
Programlista IX.2: Az automata állapotainak explicit megadása parameter I d l e = 2 ’ b00 , Go1 = 2 ’ b01 , Go2 = 2 ’ b10 ; reg [ 1 : 0 ] State ; ... always @( posedge Clock or posedge Reset ) i f ( Reset ) State <= I d l e ; else case ( State ) I d l e : i f ( Start ) State <= Go1 ; Go1 : State <= Go2 ; Go2 : State <= I d l e ; endcase ... always @( State ) begin F = 0; G = 0; i f ( State == Go1) F = 1 ; e l s e i f ( State == Go2) G = 1 ; end
Szakasz 1: RTL always utasítás
267
kombinációs, az extra flip-flopok eltűnnek. Az állapotautomaták leírásának egy másik szokásos stílusa, hogy a kombinációs logikából eltávolítjuk a regisztereket. Az IX.3 listán mutatott példa ugyanazt az állapotgépet mutatja. Ebben egy always utasítás írja le az állapotregisztereket, egy másik pedig a következő állapotot és a kimenetet dekódoló logikát. Ebben az esetben egy további regiszter, NextState, szükséges ahhoz, hogy a kombinációs always utasítás tudjon kommunikálni az órajelvezérelt always utasítással. Mivel a NextState állapot State és Start kombinációs függvénye, annak teljesen meghatározottá kell válnia az always blokkban (különben latch áramkörökre van szükség). A NextState = State értékadás biztosítja, hogy ilyen latch áramkörökre ne legyen szükség. Ez az utasítás a szimulációt nem befolyásolja: csak arra kell, hogy a szintézis eszközök megfelelõen értelmezzék az always blokkot. A "szeparált kimenetdekódolás" stílus egy lehetséges előnye, hogy a következő állapotot és a kimenetet dekódoló logika közös részeit nem
Szakasz 1: RTL always utasítás
268
Programlista IX.3: Regiszterek eltávolítása a kombinációs logikából parameter I d l e = 2 ’ b00 , Go1 = 2 ’ b01 , Go2 = 2 ’ b10 ; reg [ 1 : 0 ] State , NextState ; ... always @( posedge Clock or posedge Reset ) i f ( Reset ) State <= I d l e ; else State <= NextState ; ... always @( State or Start ) begin F = 0 ; G = 0 ; NextState = State ; case ( State ) Idle : i f ( Start ) NextState = Go1 ; Go1 : begin F = 1 ; NextState = Go2 ; end Go2 : begin G = 1 ; NextState = I d l e ; end endcase end
Szakasz 1: RTL always utasítás
269
kell duplikálni, ezáltal a kód rövidebbé és az optimalizáló munkája egyszerűbbé válik. A "szeparált kimenetdekódolás" stílus egy lehetséges hátránya, hogy a szimuláció kevésbé hatékonnyá válik, ha a következõ állapot logikája a bemenetektől is függ (ez nem áll fenn az előbbi esetben), mivel ilyenkor további alkalmakkor is végre kell hajtani a kombinációs always utasítást, amikor egy bemenet megváltozik. Mindkét leírási stílus korrekt viselkedést és korrekt szintézist eredményez, mindkettőnek vannak követői. Ízlés szerint bármelyiket választhatjuk. Néha teljesen ki akarjuk küszöbölni a kimenet dekódoló logikát a véges állapotú automata tervéből, és ennek érdekében az egyes állapotok állapotvektorának kódolását az állapot kimeneti értékeivel egyenértékűvé tesszük. Ennek elérésére megfelelő kódolást kell kidolgoznunk és megfelelően deklarálni az állapotparamétereket. Végül pedig, az állapotvektor bitjeit át kell másolnunk a kimenetekbe. Az FPGA-ban megvalósított FSM esetén általában jó választás a
Szakasz 1: RTL always utasítás
Programlista IX.4: Kimeneti dekódolás nélkül parameter I d l e = 3 ’ b100 , Go1 = 3 ’ b010 , Go2 = 3 ’ b001 ; reg [ 2 : 0 ] State ; ... always @( posedge Clock or posedge Reset ) i f ( Reset ) State <= I d l e ; else case ( State ) I d l e : i f ( Start ) State <= Go1 ; Go1 : State <= Go2 ; default : State <= I d l e ; endcase assign F = State [ 1 ] ; assign G = State [ 0 ] ;
270
Szakasz 1: RTL always utasítás
271
Programlista IX.5: Regiszterek eltávolítása a kombinációs logikából parameter I d l e = 0 , Go1 = 1 , Go2 = 2 ; reg [ 2 : 0 ] State ; always @( posedge Clock or posedge Reset ) i f ( Reset ) begin State <= 3 ’ b000 ; State [ I d l e ] <= 1 ’ b1 ; end else begin State <= 3 ’ b000 ; case ( 1 ’ b1 ) // synopsys parallel_case State [ I d l e ] : i f ( Start ) State [ Go1 ] <= 1 ’ b1 ; e l s e State [ I d l e ] <= 1 ’ b1 ; State [ Go1 ] : State [ Go2 ] <= 1 ’ b1 ; State [ Go2 ] : State [ I d l e ] <= 1 ’ b1 ; endcase end "Manual output decoding" always @( State ) begin F = State [ Go1 ] ; G = State [ Go2 ] ; end
Szakasz 1: RTL always utasítás
272
"one-hot state encoding", mivel az jól illik az FPGA felépítéséhez. Az IX.5 programlistán valójában nem is "one-hot state encoding" technikát használunk, de a szintézis eszköz ezt nem ismeri fel és teljesen dekódolja az állapot flip-flop értékeket az állapot előállítására. A példában az Idle, Go1 és Go2 paraméterek már nem az állapotok flip-flop értékei, hanem a megfelelő állapot-flipflop pozíciója az állapot-flipflopok között. Például, az Idle állapotban State[Idle] értéke 1’b1 lesz, továbbá State[Go1] és State[Go2] értéke egyaránt 1’b0. Az IX.5 listán bemutatott példa egy konstans (1’b1) case kifejezéssel működő case utasítást használ, ahol a változó case értékek a State bitjei. A parallel_case direktíva azt közli a szintézis eszközzel, hogy a (State[Idle], State[Go1] or State[Go2]) case kifejezésnek csak egyetlen értéke felel meg az 1’b1 case kifejezésnek, azaz a kódolás "onehot". Megjegyezzük, hogy a kimeneteket manuálisan kódoljuk. Az IX.6 ábrán az első kód kevéssé hatékony logikát eredményez, mivel a szintézis eszköz nem fog "one-hot" kódolást feltételezni. Amint
Szakasz 1: RTL always utasítás
273
Programlista IX.6: Regiszterek eltávolítása a kombinációs logikából always @( State ) begin Ez a kód kevéssé hatékony F = 0; logikát eredményez G = 0; i f ( State == Go1) F = 1; e l s e i f ( State == Go2) G = 1; end always @( State ) Használhatjuk viszont a begin parallel_case directívát F = 0; G = 0; case ( 1 ’ b1 ) // synopsys parallel_case State [ Go1 ] : F = 1 ; State [ Go2 ] : G = 1 ; endcase end
Szakasz 1: RTL always utasítás
274
a második kódrészlet mutatja, használhatunk azonban egy másik case utasítást, egy parallel_case direktívával, az állapotlogika always blokkjában használthoz hasonló módon. Egy szintézis eszköz az egyes állapotokat általában az állapot nevének megfelelő paraméterek értékének felhasználásával kódolja. Bizonyos szintézis eszközök felismerik, hogy egy állapotgépre vonatkozik a leírás, és a kódolást automatikusan ennek megfelelőre módosítják. Más eszközök akkor módosítják a kódolást, ha bizonyos szintézis direktívák vagy pragmák jelen vannak a Verilog kódban. A fenti példa egy olyan szintaxist mutat, amelyet a Synopsis szintézis eszközök használnak arra, hogy az eszközöknek engedélyezzék az állapotkódolás megváltoztatását. Más eszközök hasonló szintaxist támogatnak, lásd a megfelelő dokumentációkat. Az új kódolás (a target technológiától függően) vagy bináris, vagy "one-hot" lesz. Bináris esetén a paraméterlista első állapota lesz a bináris 0, a következő a bináris 1 és így tovább. A "one-hot" kódolás esetén állapotonként van egy flip-flop, és minden egyes állapotban csak
Szakasz 1: RTL always utasítás
IX.4. ábra. Állapotkódolás
c
2004, Doulos Verilog
275
Szakasz 1: RTL always utasítás
276
egyetlen flip-flop lehet 1 állapotú. Sok szintézis eszköz lehetővé teszi a felhasználóknak, hogy standard kódolási módszerekből (Gray, One Hot, Adjacency, Random, LFSR, stb) válasszanak, Sok eszköz kínál egy automatikus kódolási módszert, amelyik "okos" algoritmusok alapján a "legjobb" kódolást választja. A gyakorlatban azonban a legjobb megoldást a mérnöki tapasztalat eredményezi, a kódolást kézzel kiválasztva. (Ebben a példában a // synopsys enum States direktíva azt közli a szintézis eszközzel, hogy a Idle, Go1 és Go2 paraméterek az enum típusú States tagjai. Ezután a reg State típusát megadhatjuk States típusúként. Ez azt jelenti, hogy ez a változó csak a Idle, Go1 és Go2 értékek valamelyikét veheti fel – nem adhatunk numerikus értéket, csak ezeket a paraméter neveket. A másik direktíva - // synopsys state_vector State - azt közli a segédprogrammal, hogy State egy állapotgép állapotvektora. A segédprogram ezt automatikusan feldolgozza. Megjegyezzük, hogy paraméter deklarációban vektor értéktar-
Szakasz 1: RTL always utasítás
277
tományt megadni az IEEE 1364 Verilog standard szerint nem megengedett. Néhány szintézis eszköz viszont ezt megköveteli, így a legtöbb segédprogram támogatja ezt a nyelvi kiterjesztést. Amikor egy állapotgépet hardverben valósítunk meg, az állapotok száma 2N lesz, ahol N az állapotvektor bitjeinek száma. Ha az állapotgépnek 2N állapotnál kevesebb állapota van, a fennmaradó állapotok nem érhetők el az automata normál logikai működése során. A bekapcsoláskor azonban az automata kerülhet az elérhetetlen állapotok valamelyikébe is, vagy eljuthat ilyen állapotba szélsőséges működési feltételek hatására. Don’t care Ha nem definiáljuk, hogy az állapotgép hogyan viselkedjék egy elérhetetlen állapotban, a szintézis eszköz kihasználhatja azt a tényt, hogy a tervező számára nem fontos, mi történik ilyen állapotban. Másrészt viszont, ha ellenőrizni szeretnénk az állapotgép viselkedését elérhetetlen állapotokban (pl. közvetlenül ide jutni reset állapotban), akkor annak viselkedését explicit módon meg kell adnunk
Szakasz 1: RTL always utasítás
IX.5. ábra. Elérhetetlen állapotok
c
2004, Doulos Verilog
278
Szakasz 1: RTL always utasítás
IX.6. ábra. Elérhetetlen állapotok vezérlése
c
2004, Doulos Verilog
279
Szakasz 1: RTL always utasítás
280
a Verilog forráskódban mind a 2N esetre. Ez jelentheti azt, hogy extra paraméter neveket kell hozzáadnunk, hogy az értékek számát 2 hatványra egészítsük ki, majd hozzá kell adnunk a megfelelő kódot az állapotátmenet definiálásához. Ha valóban fontos a viselkedés elérhetetlen állapotokban, akkor fontos, hogy ne engedjük meg az FSM ekvivalens állapotainak optimalizálását. Ha több üres állapotot tartalmazó FSM esetén megengedjük az optimalizálást, akkor ennek során ezek az állapotok összevonódhatnak, ami néhány jól definiált állapothoz vezet (amelyek egyike elérhetetlen), meg létrejön néhány valóban definiálatlan elérhetetlen állapot! Több szintézis eszközt befolyásol az a Verilog kód, amit a case utasítás alapértelmezett részébe írunk; függetlenül attól, hogy van-e más eset!
Szakasz 1: RTL always utasítás
281
Aritmetika és számlálók szintézise
fejezet X: Aritmetika és számlálók szintézise
282
Szakasz 1: Aritmetikai műveletek
283
1. Aritmetikai műveletek A Verilog számos olyan operátort tartalmaz, amelyik az általánosan használt aritmetikai műveletek elvégzésére alkalmas. Általában véve, ezek az operátorok úgy működnek, ahogyan azt elvárjuk. A számokat összeadhatjuk, kivonhatjuk, szorozhatjuk, oszhatjuk, vagy vehetjük a modulusukat. Van azonban pár dolog, amire figyelnünk kell. Amikor egy szám túlcsordul, az érték "átfordul", hasonlóan, mint egy hardver regiszterben. Ezért, ha a 4’b1111 értékhez az 1 értéket hozzáadjuk, és az eredményt egy 4-bites regiszterben tároljuk, annak eredménye 4’b0000 lesz. Emellett, nem történik automatikusan előjel kiterjesztés, amikor ’negatív’ (kettes komplemens) számokat használunk. Ezzel még részletesen foglalkozunk kicsit később. A Verilogban a számokat előjel nélküli mennyiségként kezeljük, kivéve, ha regiszter típusú egész számokat használunk. (Az integer szám egy 32-bites előjeles szám.) Például, az
Szakasz 1: Aritmetikai műveletek
X.1. ábra. Aritmetikai operátorok
c
2004, Doulos Verilog
284
Szakasz 1: Aritmetikai műveletek
X.2. ábra. Előjeles és előjel nélküli
c
2004, Doulos Verilog
285
Szakasz 1: Aritmetikai műveletek
286
byte = −1; értékadás a 8’b11111111 értéket adja egy reg bájtnak. Bár ez a -1 kettes komplemensű ábrázolása, valójában azonban elvész, hogy ez egy előjeles szám. Azaz, amikor egy nagyobb reg wordnek adunk értéket: word = byte; a bájt értékét 0-val terjesztjük ki, és a szó értéke 16’b0000000011111111 lesz, ami már jól láthatóan nem a -1 érték kettes komplemens ábrázolása. Amikor egy kifejezésben számokat használunk, az előjel nélküli egészeket előjelesként kezeljük, de a mérettel rendelkező számokat előjel nélküliként, még ha a méret nincs is megadva. Azaz, -10 előjeles szám, míg -’d10 egy 32-bites előjel nélküli szám, aminek nagy pozitív értéke van. Az X.3 ábra mutatja, hogyan szintetizálhatók a Verilog aritmetikai és összehasonlító operátorai. Csak az összeadás (+) és kivonás (), valamint az összehasonlítások (= != < <= > >=) a biztosan szintetizálható műveletek. Pár szintézis eszköz teljes körűen kezeli
Szakasz 1: Aritmetikai műveletek
X.3. ábra. Aritmetikai operátorok szintézise
c
2004, Doulos Verilog
287
Szakasz 1: Aritmetikai műveletek
288
szorzást, egy kombinációs szorzó áramkört állítva elő. A / és % operátorok általában nem szintetizálhatók. Szintetizálhatók azonban akkor, ha az operandusok állandók, vagy ha azok eltolási vagy maszkolási műveletre használatosak. Ilyen eset, amikor az operandus a 2 egy állandó hatványa (pl. A*2), A/4, N%2). Bár néhány szintézis eszköz képes lehet használni a (=== and !==) case egyenlőségi operátorokat, ennek használata nem ajánlott, érdemes a normál egyenlőségi operátorokat használni. Érdemes, sőt sokszor kell is, zárójeleket használni annak meghatározására, hogy milyen sorrendben kell egy kifejezés operátorait kiértékelni. A gyakorlatban a szintézis eszköztől függ, hogy mi történik az aritmetikai operátorokkal. Néhány eszköz az operátorokat diszkrét kapukra képezi le, mások a cél-technológiára optimalizált makrocellákat használnak. Egyes segédprogramok alapértelmezetten hierarchikus blokkokat készítenek, mások egyszintű struktúrákat. A legtöbb RTL szintézis eszköz beépített, technológia-független formátumú aritmetikai könyvtárakkal (azaz logikai egyenletekkel) dolgozik, amit aztán a cél-architektúrára képeznek le.
Szakasz 1: Aritmetikai műveletek
X.4. ábra. Mi történik a F <= A + B utasítás hatására?
c
2004, Doulos Verilog [2]
289
Szakasz 1: Aritmetikai műveletek
X.5. ábra. Makrocellák használata
c
2004, Doulos Verilog [2]
290
Szakasz 1: Aritmetikai műveletek
291
Az RTL szintézis eszközök vagy logikai kifejezésekké (azaz kapukká) alakítják az aritmetikai operátorokat, vagy pedig közvetlenül makrocellára képezik le a műveletet. Az FPGA eszközök számára alapvető fontosságú a makrocellák optimalizált használata, hogy megfelelő működési sebességet és terület kihasználást tudjanak elérni. Ha a szintézis eszköz nem tudja az aritmetikai operátort egy optimalizált makro cellára leképezni, a tervezőnek kell a Verilog kódban a makrocellából példányt készíteni. (A készítő cégek szoktak segédeszközöket biztosítani hogy "copy&paste" módszerrel tegyünk be kódrészleteket saját kódunkba, de ettől a kód cégspecifikussá válik.) Bizonyos makrocellák, például dekóderek, nem következtethetők ki logikailag, ezért csak a technológia specifikus példánykészítés jöhet szóba. Az összeadás műveletét többféle hardver architektúrával valósíthatjuk meg hardverben, a sebesség és a terület felhasználás szempontjait figyelembe véve. Az egyik szélső eset, hogy ASIC tervezés esetén a "ripple carry" a legkisebb és leglassúbb lehetőség, míg a "carry select adder" a legnagyobb és a leggyorsabb. (Az FPGAk
Szakasz 1: Aritmetikai műveletek
X.6. ábra. Az összeadók kiválasztása
c
2004, Doulos Verilog [2]
292
Szakasz 1: Aritmetikai műveletek
293
esetén, amelyekben dedikált átviteli lánc (carry chain) található, a "ripple carry" összeadók jelentik a leggyorsabb és legkisebb eszközt, amelyekkel legfeljebb 32 bites összeadókat tudunk megvalósítani.) Bizonyos ASIC szintézis eszközök automatikusan ki tudják következtetni a tervezési kényszerekből a megfelelő architektúrát, mások pedig a kézi választást teszik lehetővé. Az FPGA szintézis eszközök általában nem kínálnak választási lehetőséget, mivel az összeadó típusát a technológiára optimalizált makrók már meghatározzák. A szintézis eszközök a legtöbb szituációban nem optimalizálják az aritmetikai struktúrákat. Emiatt különösen óvatosnak kell lennünk, amikor aritmetikai operátort tartalmazó kódot írunk. Annak oka, hogy a szintézis eszközök nehezen boldogulnak az aritmetikai struktúrák optimalizálásával, abban rejlik, ahogyan a segédeszközök a függvényeket belsőleg ábrázolják. A legtöbb szintézis algoritmus a függvényeket egyszintű szorzat összeg logikai egyenletekkel ábrázolja, ebben az ábrázolásban azonban az aritmetikai függvények rengeteg szorzat tagot tartalmaznak. Ez az ábrázolási mód
Szakasz 1: Aritmetikai műveletek
X.7. ábra. Az aritmetika nem optimalizált
c
2004, Doulos Verilog [2]
294
Szakasz 1: Aritmetikai műveletek
295
azonban nagyon korlátozza a lehetséges optimalizálást. Az if utasítás egy 8-bites inkrementáló és egy 8-bites multiplexer áramkörként szintetizálódik. A legtöbb szintézis eszközben a beállításokkal és a kényszerekkel való akármilyen ügyeskedés sem változtatja meg lényegesen az architektúrát. Újra kell írnunk a megfelelő kódrészt, hogy a szintézis eszköz azt csinálja, amit szeretnénk, lásd X.7 ábra. Az X.8 ábra az aritmetikai optimalizálás hiányának klasszikus bizonyítéka. A beállításoktól és kényszerektők függetelnül, az legtübb RTL szintézis eszköz nem fogja redukálni az aritmetikát tartalmazó áramkört, de a logikai kifejezést különösebb erőfeszítés nélkül egy vezetékre csökkenti. A logikai egyenletek szintézisétől eltérően, aritmetika esetén tényleg azt kapod, amit látsz!. Emiatt érdemes figyelmesen megtervezni, milyen architektúrát is akarunk előállítani az aritmetikai operátorok használatával. Ha például a kifejezésben A + B > C szerepel, akkor az összeadás még az összehasonlítás előtt elvégződik, és az A vagy B esetén a
Szakasz 1: Aritmetikai műveletek
X.8. ábra. Az aritmetika tényleg nem optimalizált
c
2004, Doulos Verilog [2]
296
Szakasz 1: Aritmetikai műveletek
X.9. ábra. Aritmetikai azt kapod, amit látsz
c
2004, Doulos Verilog [2]
297
Szakasz 1: Aritmetikai műveletek
298
késletetés meghaladja a C késleltetését, ami teljesen rendben van, ha C van a kritikus útvonalon. Ha az egyenletet újraírjuk a A > C - B formára, akkor az A késleltetése lesz a rövidebb, ami akkor jó, ha A van a kritikus útvonalon. Az erőforrás megosztás olyan magas szintű optimalizálás, ami ahhoz szükséges, hogy hatékonyan készíthessünk aritmetikai függvényeket a logikai optimalizálási technikák korlátozott lehetőségei ellenére. Az erőforrás megosztás összehangolja egyetlen hardver erőforrás (pl. egy összeadó) használatát több különböző művelet között, amit csak akkor használhatunk, amikor a műveleteket egymást kizáró útvonalakon hajtjuk végre, a Verilog forráskódjának if vagy case utasításaiban. Az X.10 példában az if művelet két ágában a két "+" művelet közösen használhat egyetlen összeadót, ha a bemeneteket megfelelően multiplexeljük. A legtöbb szintézis eszköz lehetőséget nyújt az erőforrás megosztás manuális vezérlésére. Ezt végrehajthatjuk a Verilog HDL forráskódjában direktívákkal, vagy a grafikus interfészben ábrázolt áramkörön.
Szakasz 1: Aritmetikai műveletek
X.10. ábra. Resource sharing
c
2004, Doulos Verilog [2]
299
Szakasz 1: Aritmetikai műveletek
Programlista X.1: A integer típus 32 bitté szintetizálódik 32 flip-flop
integer I ; always @( posedge Clock ) i f ( Reset | | ( I == 2 5 5 ) ) I <= 0 ; 32 bit incrementor else I <= I + 1 ; 8 bit számláló
wire [ 7 : 0 ] Count = I ;
300
Szakasz 1: Aritmetikai műveletek
301
A Verilogban egy int (egész) regiszter típusú, ami 32-bites egész számot ábrázol. Amikor egy egészt úgy használunk, hogy abból flipflopokat kell készíteni, akkor 32 flip-flopot állítunk elő! Programlista X.2: A integer típus 32 bitté szintetizálódik 32- bites ciklus változó, optimalizált
integer I ;
32 bites állandó, optimalizált
parameter Size = 8 ; wire [ Size − 1 : 0 ] A; always @(A) f o r ( I = 0 ; I < Size ; I = I + 1 ) i f (A[ I ] ) N = N + 1;
A szintézis eszközök némelyike, de nem mindegyik, képes arra, hogy a felesleges flip-flopokat eltávolítsa a tervből. Emiatt általában kerüljük a szintézisre szánt tervekben az egészek használatát. Érdemes
Szakasz 1: Aritmetikai műveletek
302
az egészek helyett regeket használni, explicit szélességgel (pl. reg [15:0] R;). A ciklusváltozók, paraméterek, és vektor indexek kivételt jelentenek, ahol 32-bites buszok előállításának kockázata nélkül használhatunk egész típusú változókat. A vektorokat a Verilog előjel nélküli mennyiségként kezeli. Amennyiben a tervben a regek valóban előjel nélküli számokat tárolnak, akkor a különböző hosszúságú regeket különleges elbánás nélkül is összeadhatjuk és kivonhatjuk. Különösen fontos, hogy amennyiben a bal oldalon levő reg elég nagy, akkor a kifejezés által előállított átvitel vagy túlcsordulás bit nem vész el. Ugyanez érvényes a vezetékekre folytonos értékadás esetén. Néha azonban furcsa dolgok is történnek. A TwoBits = 1’b1 + 1’b1 ;
értékadásban az összefűzés "elrejti" a TwoBits méretét, ami az átvitel bit elvesztésével jár! A TwoBits = 2’b01 + 2’b01 ; művelet eredménye viszont TwoBits = 2’b10;.
Szakasz 1: Aritmetikai műveletek
Programlista X.3: A integer típus 32 bitté szintetizálódik i r e g [ 3 : 0 ] A; reg [ 2 : 0 ] B; reg [ 4 : 0 ] F ; OK for unsigned arithmetic
F = A + B; //Carry b i t s dont get l o s t ! ( usually ) reg OneBit ; reg [ 1 : 0 ] TwoBits ; => 2’b0 !!
TwoBits = { 1 ’ b1 + 1 ’ b1 } ; => 1’b0
OneBit = 1 ’ b1 + 1 ’ b1 ; => 2’b10
TwoBits = 1 ’ b1 + 1 ’ b1 ;
303
Szakasz 1: Aritmetikai műveletek
X.11. ábra. Előjel kiterjesztés
c
2001, Doulos [2]
304
Szakasz 1: Aritmetikai műveletek
305
Tegyük fel, hogy két előjeles (azaz kettes komplemens ábrázolású) reg számot akarunk összeadni, lásd X.11 ábra. Hogy helyes eredményt kapjunk, az operandusokon előjel kiterjesztést kell végrehajtanunk, hogy mindkettőnek ugyanaz legyen a mérete, mint az eredménynek. Ezt az összefűzés (konkatenáció) operátorral tehetjük meg, lásd X.11 ábra. Egy számlálót egy Verilog kódban úgy írhatunk le, hogy a kód egy órajel él hatására a "+1" műveletet végzi el, lásd X.12 ábra. A + művelet egy olyan összeadóvá szintetizálódik, amelyiknek egyik bemenete egy artimetikai 1-re van kötve, így az always utasítás egy aszinkron reset-tel rendelkező szinkron számlálóvá szintetizálódik. Ehhez már könnyen hozzáadhatunk olyan változtatásokat, mint szinkron reset, párhuzamos betöltés, engedélyezés vagy fel-le számláló. FPGA szintézis Az FPGA-val foglalkozó cégek közül sok biztosít olyan makrókat, amelyek hatékonyan implementálják az aritmetikai és számláló szerkezeteket. Bizonyos Verilog szintézis eszközök egy számláló makrót
Szakasz 1: Aritmetikai műveletek
X.12. ábra. Számlálók szintézise
c
2001, Doulos [2]
306
Szakasz 1: Aritmetikai műveletek
307
állítanak elő, ha a Verilog kódból azt következtetik, hogy a kód funkcionalitása egy rendelkezésre álló makrónak felel meg. Egyes szintézis eszközök egy összeadó makrót állítanak elő a "+1" függvényből, mások viszont nem használnak összeadót, amikor az egyik operandus állandó. ASIC szintézis A legtöbb ASIC szintézis eszköz lehetővé teszi, hogy a "+" szintézisét egy előre definiált összeadóval oldjuk meg, vagy pedig a műveletet logikai kapukká fejtsük ki. Amikor ezt szinkron bináris számlálókkal tesszük, egy olyan hagyományos számlálót kapunk, mint amilyen a X.12 ábra alján látható. Vegyük észre a hagyományos, ANDkapukból álló átvitel láncot. A szintézis eszközt rákényszeríthetjük arra, hogy a logikát órajel-kényszer figyelembe vételével implementálja újra. Az optimalizálás hatására át-strukturálódik az átvitel generálás, és a logika gyorsabb lesz, de több területet használ. A szintézis eszközök általában szinkron implementációt használnak. Például, egy szinkron számláló engedélyezése szinkron engedélye-
Szakasz 1: Aritmetikai műveletek
X.13. ábra. Számlálók szintézise
c
2001, Doulos [2]
308
Szakasz 1: Aritmetikai műveletek
309
zésként lesz implementálva, amelynek hatására a számláló nem végez számlálást, amikor a számlálás tiltva van. Bizonyos szintézis eszközök, különösen amelyek egy bizonyos FPGA architektúrára vannak hangolva, a technológiába beépített dedikált órajel engedélyezést használják. Hogy ilyen tulajdonságokat ki tudjunk használni, a Verilog kódnak pontosan tükröznie kell a dedikált hardver viselkedését. Ez azt jelenti, hogy a bemenő jelek szintjének és prioritásának meg kell felelniük az adatlapnak. Amennyiben a szintek vagy a prioritások nem megfelelők, akkor a dedikált órajel engedélyezést nem fogjuk tudni használni, és további logikai blokkokat fogunk elhasználni a funkcionalitás megvalósítására. A szintézis eszközök valószínűbben használják a dedikált órajel engedélyezést, ha az órajel vezérelt always utasításokat ilyen template szerint írjuk: always @(posedge Clock or posedge Reset) if (Reset) // Reset actions
Szakasz 1: Aritmetikai műveletek
310
else if (Enable) // Synchronous actions (Itt csak az elveket próbáljuk magyarázni; a részletek a a használt technológiától és segédprogramtól függenek; lásd az adatlapokat és a felhasználói kézikönyveket.) Programlista X.4: Kapuzott órajelek assign GatedClock = Clock & Enb ; always @( posedge GatedClock or posedge Reset ) i f ( Reset ) Q <= 0 ; else Q <= Q + 1 ; //
Ha biztosak akarunk lenni abban, hogy kapuzott órajelet szintetizálunk, azt explicit módon kell kódolnunk, lásd X.4 programlista.
Szakasz 1: Aritmetikai műveletek
311
Természetesen, ilyenkor a mi felelősségünk, hogy elkerüljük az órajelek kapuzásából fakadható időzítési problémákat, mint például az órajelek csúszkálása (skew) és recsegése (glitches). Ezt a fajta trükközést időnként hibás tervezési gyakorlatnak tartják. Másrészt viszont nagyon hatékony mód az energia felhasználás mérséklésére. Mindenesetre, FPGA és CPLD technológiák használata esetén jobb azt elkerülni; helyette használjuk a dedikált órajel engedélyezést. Programlista X.5: Aszinkron tervezés assign Q = ! ( S & QB) ; assign QB = ! (R & Q) ; //
A szintézis lehetővé teszi, hogy aszinkron terveket valósítsunk
Szakasz 1: Aritmetikai műveletek
312
meg. A természetéből fakadóan aszinkron terveket a Verilog aszinkron hardverré szintetizálja. Az aszinkron tervek azonban gondos tervezést igényelnek, hogy helyesen működjenek. A szintézis eszköz nem nyúj garanciát arra, hogy a szintetizált hardver helyesen fog működni, mivel nem érti a terv aszinkron időzítéseinek vonatkozásait. Az aszinkron tervezésből fakadó problémák azonosítása, javítása, ellenőrzése teljes egészében a tervező feladata! Jó tervezői gyakorlat a tervet elkülönített szinkron blokkokra felosztani, ahol mindegyik blokknak egyetlen órajele van. Ezután célszerű explicit szinkronizálási pontokat bevezetni, hogy elkerüljük a blokk interfészeknél a metastabilitási problémákat. Az elkülönített blokkokban külön kell kezelnünk az aszinkron logikákat. és azokat alaposan tesztelni kell.
Szakasz 1: Aritmetikai műveletek
313
Nem-blokkoló értékadás
314
Függelék A: Nem-blokkoló értékadás A Verilog egyik leggyakrabban félreértett konstrukciója a feltételes értékadás. Még a tapasztalt Verilog tervezők sem teljesen értik, hogyan ütemeződnek a nem-blokkoló értékadások az IEEE szabványt követő szimulátorokban és nem értik azt sem, mikor és miért kell nemblokkoló értékadást használni. Ez a szakasz annak részleteit mutatja meg, hogyan ütemeződnek a Verilog blokkoló és nem-blokkoló értékadásai, továbbá fontos kódolási útmutatásokat ad ahhoz, hogy helyes szintetizálható logikát készíthessünk, és elkerülhessük a szimuláció során a versenyhelyzeteket. [3]
Szakasz 1: Bevezetés
315
1. Bevezetés Két jól ismert kódolási útmutató logikai modellezéshez: • Használjon blokkoló értékadást azokban az always blokkokban, amelyekben kombinációs logikát akar előállítani • Használjon nem-blokkoló értékadást azokban az always blokkokban, amelyekben szekvenciális logikát akar előállítani De miért is? Általában véve, a válasz a szimulációval függ össze. A fenti két szabályt figyelmen kívül hagyva, korrekt logikát hozhatunk létre, de a szintézis előtti szimuláció eredménye nem feltétlenül felel meg a szintetizált áramkör viselkedésének. Hogy a fenti két szabály mögött rejlő okokat megértsük, teljes mértékben meg kell értenünk a Verilog blokkoló és nem blokkoló értékadásának működését és ütemezését. Ebben a fejezetben a következő jelöléseket fogjuk használni: RHS - az egyenlőség jobb oldalán álló kifejezést vagy vagy változót RHS kifejezésnek vagy RHS változónak jelöljük LHS - az egyenlőség bal oldalán álló kifejezést vagy vagy változót LHS
Szakasz 1: Bevezetés
kifejezésnek vagy LHS változónak jelöljük
316
Szakasz 2: Versenyhelyzetek a Verilogban
317
2. Versenyhelyzetek a Verilogban A IEEE Verilog Standard [4] meghatározza, melyik utasítások esetén van garantált végrehajtási sorrend ("Determinism", section 5.4.1), és melyek esetében nincs ("Nondeterminism", section 5.4.2 & "Race conditions", section 5.5). A Verilogban akkor áll elő versenyhelyzet, amikor két vagy több utasítás végrehajtás ugyanarra az idő lépésre van ütemezve, ami különböző eredményt hozna, ha az utasítások végrehajtásának sorrendjét – amint azt a IEEE Verilog sztenderd lehetővé teszi – megváltoztatnánk. A versenyhelyzetek elkerülése érdekében fontos, hogy megértsük a Verilog blokkoló és nem-blokkoló értékadásainak ütemezését.
Szakasz 3: Blokkoló értékadás
318
3. Blokkoló értékadás A blokkoló értékadás operátor jele az egyenlőség jel ("="). A blokkoló értékadás arról kapta a nevét, hogy a blokkoló értékadás RHS argumentumait úgy kell kiértékelni és az értékadást végrehajtani, hogy azt más Verilog utasítás ne szakíthassa meg. Ezért mondjuk, hogy az az értékadás "blokkolja" a többi értékadást, amíg a folyó értékadás be nem fejeződik. Az egyetlen kivétel ez alól az az értékadás, amikor a blokkoló értékadás jobb oldalán (RHS) időkésleltetés történik, ami viszont egyértelműen hibás programozási stílus. A blokkoló értékeadás végrehajtását egy két-lépéses folyamatnak tekinthetjük • kiértékeljük a jobb oldalt (RHS) • frissítjük a bal oldalt (LHS) úgy, hogy közben más Verilog utasítás nem hajtódik végre. A blokkoló utasítás "blokkolja" az ugyanazon blokkban található további utasítások végrehajtását amíg a folyó értékadás be nem fejeződik. A
Szakasz 3: Blokkoló értékadás
319
blokkoló értékadással kapcsolatos probléma akkor fordul elő, amikor az egyik procedurális blokk RHS változója egy másik blokkban LHS változó, és mindkét értékadás végrehajtását ugyanabban a szimulációs lépésben (például ugyanarra az órajelre) kell elvégezni. Ha a blokkoló értékadások nem a megfelelő sorrendben vannak, versenyhelyzet állhat elő. Amikor blokkoló utasításokat ugyanabban az időlépésben kell végrehajtani, a végrehajtás sorrendje nem ismert. A A.1 programlista egy blokkoló értékadással megvalósított visszacsatolt oszcillátor megvalósítását mutatja. A IEEE Verilog Standard szerint a két always blokk bármely sorrendben beütemezhető. Ha resetelés után az első blokk hajtódik végre, y1 és y2 egyaránt 1 értéket vesz fel. Ha resetelés után a második blokk hajtódik végre, y1 és y2 egyaránt 0 értéket vesz fel. Azaz, érthetően egy Verilog versenyhelyzet áll elő.
Szakasz 3: Blokkoló értékadás
320
Programlista A.1: Visszacsatolt oszcillátor blokkoló értékadással module fbosc1 ( y1 , y2 , clk , r s t ) ; output y1 , y2 ; input clk , r s t ; reg y1 , y2 ; always @( posedge clk or posedge r s t ) i f ( r s t ) y1 = 0 ; // reset e l s e y1 = y2 ; always @( posedge clk or posedge r s t ) i f ( r s t ) y2 = 1 ; // preset e l s e y2 = y1 ; endmodule
Szakasz 4: Nemblokkoló értékadás
321
4. Nemblokkoló értékadás A nemblokkoló értékadás jele megegyezik a "kisebb vagy egyenlő" operátoréval ("<="). A nemblokkoló értékadás arról kapta a nevét, hogy a nemblokkoló értékadás az időlépés kezdetén kiszámítja az RHS értékét, és beütemezi, hogy az LHS értékadás történjen meg az időlépés végén. Az RHS kifejezés kiértékelése és az LHS kifejezés frissítése között más nemblokkoló Verilog RHS kifejezések (utasítások) is kiértékelődhetnek és LHS frissítések beütemeződhetnek. A nemblokkoló értékadás nem blokkolja más Verilog utasítások végrehajtását. A nemblokkoló értékadás végrehajtását egy két-lépéses folyamatnak tekinthetjük • az időszakasz kezdetén kiértékeljük a nemblokkoló értékadás jobb oldalát (RHS) • az időszakasz végén frissítjük a nemblokkoló értékadás bal oldalát (LHS)
Szakasz 4: Nemblokkoló értékadás
322
A nemblokkoló értékadásokat csak regiszter adattípusokkal hajthatjuk végre, ezért csak procedurális (initial és always) blokkok belsejében lehet használni. Nemblokkoló értékadás nem használható folytonos értékadásban sem. Programlista A.2: Visszacsatolt oszcillátor nemblokkoló értékadással module fbosc2 ( y1 , y2 , clk , r s t ) ; output y1 , y2 ; input clk , r s t ; reg y1 , y2 ; always @( posedge clk or posedge r s t ) i f ( r s t ) y1 <= 0 ; // reset e l s e y1 <= y2 ; always @( posedge clk or posedge r s t ) i f ( r s t ) y2 <= 1 ; // preset e l s e y2 <= y1 ; endmodule
Szakasz 4: Nemblokkoló értékadás
323
Az A.2 programlista egy nemblokkoló értékadással megvalósított visszacsatolt oszcillátor megvalósítását mutatja. A IEEE Verilog Standard szerint a két always blokk ebben az esetben is bármely sorrendben beütemezhető. Attól függetlenül, hogy melyik blokk indul el a reset után, mindkét nemblokkoló RHS kifejezés az időszakasz kezdetén kiértékelődik és mindkét nemblokkoló LHS változó értéke az időszakasz végén frissül. A felhasználó szempontjából a két nemblokkoló értékadás végrehajtása párhuzamosan történik.
Szakasz 5: Verilog kódolási útmutató
324
5. Verilog kódolási útmutató Mielőtt a blokkoló és nemblokkoló értékadásra vonatkozóan további magyarázatokat és példákat látnánk, ismerkedjünk meg nyolc olyan kódolási irányelvvel, amelyik hozzásegít ahhoz, hogy Verilog nyelven modellezett hardvert pontosan tudjunk szimulálni. Ezeket az egyszerű irányelveket betartva, a Verilog versenyhelyzet hibák 90-100 %-át kiküszöbölhetjük. A Verilog kódolási irányelvei
Szakasz 5: Verilog kódolási útmutató
1 Amikor szekvenciális logikát tervez, használjon nemblokkoló értékadásokat. 2 Amikor latch-eket modellez, használjon nemblokkoló értékadást. 3 Amikor kombinációs logikát always blokkal modellez, használjon blokkoló értékadást. 4 Amikor szekvenciális és kombinációs logikát is használ ugyanabban az always blokkban, használjon nemblokkoló értékadást. 5 Ne keverje ugyanabban az always blokkban a blokkoló és nemblokkoló értékadásokat. 6 Ne adjon értéket ugyanannak a változónak egynél több always blokkban. 7 Használja a $strobe értéket nemblokkoló értékadással adott értékek megjelenítésére. 8 Ne használjon #0 késleltetésű időzítéseket.
325
Szakasz 5: Verilog kódolási útmutató
326
Ezeknek a szabályoknak a magyarázatát később fogjuk látni. A kezdő Verilog programozóknak javasolt a fenti irányelveket memorizálni, amíg megértik a mögöttük rejlő funkcionalitást. Életet menthet!
Szakasz 6: A Verilog réteges eseménysora
6. A Verilog réteges eseménysora A.1. ábra. Verilog "réteges esemény sora"
c
2000, Cummings[3]
327
Szakasz 6: A Verilog réteges eseménysora
328
A Verilog "réteges eseménysor"ának (lásd A.1. ábra) vizsgálata segít a Verilog blokkoló és nemblokkoló értékadásai működésének megértésében. A "réteges eseménysor" szemléletes név a Verilog különböző esemény soraira, amelyeket a szimulációs események ütemezésére használ. A Verilog sztenderd ezt csak koncepcióként definiálja, a céges megvalósítások befolyásolják a szimulátor hatékonyságát. A Verilog sztenderd 5.3 szakasza szerint a "réteges eseménysor" az éppen aktuális szimulációs időben logikailag négy különálló sorra van felosztva, és további sorok csatlakoznak a jövőbeli szimulációs időkre vonatkozóan. Az aktív események sorában van ütemezve a legtöbb Verilog esemény, közöttük a blokkoló és nem-blokkoló értékadások, a $display utasítás, a példányok és primitívek bemeneteinek értékelése, amit a példányok és bemenetek kimeneteinek frissítése követ, valamint a nemblokkoló RHS kifejezések értékelése. A nemblokkoló értékadások LHS nem frissül az aktív események sorban. Az események (a sztenderd szabta kereteken belül) bármelyik sorhoz hozzáadódhatnak, de azok csak az aktív események sorából
Szakasz 6: A Verilog réteges eseménysora
329
távolítódnak el. A többi eseménysor eseményei esetlegesen "aktiválódhatnak", vagy előléphetnek az aktív események sorába. A sztenderd 5.4 szakasza mutatja be azt az algoritmust, hogy mikor "aktiválódnak" a további esemény sorok. Két további elterjedten használt jelen szimulációs időbeli eseménysor a nemblokkoló értékadás frissítési eseménysora és a monitor események sora. A nemblokkoló értékadás frissítési eseménysorában a nemblokkoló értékadás LHS kifejezések frissítése ütemeződik. Az RHS kifejezések véletlenszerű sorrendben értékelődnek a szimulációs időlépés kezdetén, a többi aktív eseménnyel együtt. A monitor eseménysor ban a $strobe és $monitor kijelző utasítások ütemeződnek. Ezek az utasítások azokat a frissített értékeket mutatják, amelyeket a változók a szimulációs időszakasz végén kapnak, miután valamennyi, az arra az időszakaszra vonatkozó értékadás befejeződött. A szabványban leírt negyedik sor az inaktív események sora, ahol a
Szakasz 6: A Verilog réteges eseménysora
330
#0-késleltetésű események ütemeződnek. A#0-késleltetésű események használata hibás gyakorlat, amit azok a tervezők használnak, akik ugyanannak a változónak különböző modulokból próbálnak meg értéket adni, és úgy próbálják meg kiküszöbölni a Verilog versenyhelyzetet, hogy az egyik eseményt még ugyanabban a szimulációs időszakaszban, de egy pici késleltetéssel ütemezik be. A #0-késleltetésű értékadások a Verilog modellekben szükségtelenül komplikálják a beütemezett események analízisét. Ezek az esetek könnyen helyettesíthetők másféle, hatékonyabb kódolási megoldásokkal, nem érdemes ilyen megoldást választani. (lásd a #8 kódolási irányelvet. ) Az A.1. ábrán szereplő réteges eseménysorra ebben a szakaszban még sokszor fogunk hivatkozni, amikor a kódolási példákat tárgyaljuk.
Szakasz 7: Ön-triggerelő always blokkok
331
7. Ön-triggerelő always blokkok Programlista A.3: Nem öntriggerelő oszcillátor blokkoló értékadás használatával module osc1 ( clk ) ; output clk ; reg clk ; i n i t i a l #10 clk = 0 ; always @( clk ) #10 clk = ~clk ; endmodule
Általában véve, egy Verilog always blokk nem triggerelheti saját magát. Tekintsük az A.3 programlista szerinti oszcillátort. Ez az oszcillátor blokkoló értékadásokat használ. A blokkoló értékadások kiértékelik RHS-t és frissítik LHS-t, megszakítás nélkül. A blokkoló értékadásnak be kell fejeződnie, mielőtt a @(clk) él-trigger beütemeződne. Miután a trigger esemény beütemeződött, a blokkoló clk
Szakasz 7: Ön-triggerelő always blokkok
332
értékadás befejeződik; ezért nem származik trigger esemény az always blokkon belülről, ami triggerelné a @(clk) triggert. Programlista A.4: Öntriggerelő oszcillátor nemblokkoló értékadás használatával module osc2 ( clk ) ; output clk ; reg clk ; i n i t i a l #10 clk = 0 ; always @( clk ) #10 clk <= ~clk ; endmodule
Tekintsük az A.4 programlistán szereplő oszcillátort. Ez az oszcillátor viszont nemblokkoló értékadást használ. Az első @(clk) trigger után, a nemblokkoló értékadás jobb oldala kiértékelődik és a nemblokkoló értékadás LHS értéke beütemeződik a nemblokkoló értékadás frissítés eseménysorba. Mielőtt a nemblokkoló értékadás frissítés az eseménysorban "aktiválódik", a szimulátor a @(clk) trigger
Szakasz 7: Ön-triggerelő always blokkok
333
utasítást megtalálja, és az always blokk ismét érzékennyé válik a clk jel változására. Amikor ugyanebben a lépésben később az LHS érték is frissítődik, a @(clk) ismét triggerelődik. Az osc2 példa öntriggerelő, de attől még nem ajánlott programozási stílus.
Szakasz 8: Csővezeték modellezés
334
8. Csővezeték modellezés A.2. ábra. Szekvenciális csővezeték regiszter
c
2000, Cummings[3]
Az A.2 egy egyszerű szekvenciális csővezeték regiszter blokkdiagramját mutatja. A következő négy példa négy különböző megközelítést mutat, amelyeket a tervezőmérnök választhat arra, hogy ezt a csővezetéket blokkoló értékadással megvalósítsa.
Szakasz 8: Csővezeték modellezés
335
Programlista A.5: Hibás blokkoló értékadásos szekvenciális kódolási stílus #1 module pipeb1 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) begin q1 = d ; q2 = q1 ; q3 = q2 ; end endmodule
Szakasz 8: Csővezeték modellezés
336
Az A.5 programlistán látható, a pipeb1 kód szerint a szekvenciálisan rendezett blokkoló értékadások hatására a d bemenő értéket helyezzük mindegyik regiszter kimenetére a következő clk órajelre. Minden egyes órajel élre, a bemenő jel késleltetés nélkül közvetlenül a q3 kimenetére kerül. Ez egészen biztosan nem a kívánt csővezetékes regisztert modellezi, és valójában egyetlen regiszterré szintetizálódik! Az A.6 programlistán látható a pipeb2 példa. Ebben a példában a blokkoló értékadásokat megfelelően átrendeztük, így a szimuláció tényleg úgy viselkedik, mint egy csővezeték regiszter. Ez a modell a betét ábrának megfelelő csővezetékes regiszterré szintetizálódik. Az A.7 programlistán a harmadik megközelítés látható. A pipeb3 példában a blokkoló értékadás utasításokat különálló always blokkokra vágtuk szét. A Verilog bármilyen sorrendben szimulálhatja az always blokkokat, aminek eredményeként a szimuláció hibás lehet. Ez is egy Verilog versenyhelyzet! Az always blokkokat eltérő sorrendben végrehajtva, különböző eredményre jutunk. A Verilog kód azonban a helyes csővezetékes regiszterré szintetizálódik. Ez azt is jelenti, hogy
Szakasz 8: Csővezeték modellezés
337
Programlista A.6: Csúnya, de működő blokkoló értékadásos szekvenciális kódolási stílus #2 module pipeb2 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) begin q3 = q2 ; q2 = q1 ; q1 = d ; end endmodule
Szakasz 8: Csővezeték modellezés
338
Programlista A.7: Hibás szekvenciális kódolási stílus blokkoló értékadással #3 module pipeb3 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) q1=d ; always @( posedge clk ) q2=q1 ; always @( posedge clk ) q3=q2 ; endmodule
Szakasz 8: Csővezeték modellezés
339
szintézis előtti és utáni szimulációk eredménye is eltérhet. Programlista A.8: Hibás szekvenciális kódolási stílus blokkoló értékadással #4 module pipeb4 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) q2=q1 ; always @( posedge clk ) q3=q2 ; always @( posedge clk ) q1=d ; endmodule
Az A.8 ábrán a harmadik megközelítés látható, eltérő sorrendben használt always utasításokkal. A pipeb4 példa, vagy az ott szereplő always blokkok bármely más sorrendje esetén szintén helyes csővezetékezett logikát kapunk, de előfordulhat, hogy nem tudjuk azt helyesen
Szakasz 8: Csővezeték modellezés
340
szimulálni. Ha az előbbi négy, blokkoló értékadással megvalósított példát újraírjuk, ezúttal nemblokkoló értékadással, azok mindegyike helyesen szimulálható és szintetizálható lesz. Programlista A.9: Helyes szekvenciális kódolási stílus nemblokkoló értékadással #1 module pipen1 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) begin q1 <= d ; q2 <= q1 ; q3 <= q2 ; end endmodule
Szakasz 8: Csővezeték modellezés
341
Programlista A.10: Helyes szekvenciális kódolási stílus nemblokkoló értékadással #2 module pipen2 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) begin q3 <= q2 ; q2 <= q1 ; q1 <= d ; end endmodule
Szakasz 8: Csővezeték modellezés
342
Programlista A.11: Helyes szekvenciális kódolási stílus nemblokkoló értékadással #3 module pipen3 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) q1<=d ; always @( posedge clk ) q2<=q1 ; always @( posedge clk ) q3<=q2 ; endmodule
Szakasz 8: Csővezeték modellezés
343
Programlista A.12: Helyes szekvenciális kódolási stílus nemblokkoló értékadással #4 module pipen4 ( q3 , d , clk ) ; output [ 7 : 0 ] q3 ; input [ 7 : 0 ] d ; input clk ; reg [ 7 : 0 ] q3 , q2 , q1 ; always @( posedge clk ) q2<=q1 ; always @( posedge clk ) q3<=q2 ; always @( posedge clk ) q1<=d ; endmodule
Szakasz 8: Csővezeték modellezés
344
Az ebben a szakaszban bemutatott csővezeték kódolási stílusokat összefoglalva: • blokkoló értékadással a négyből csak egy kódolási stílus volt képes helyesen szimulálni • blokkoló értékadással a négyből három stílus helyes szintézist eredményezett • nemblokkoló értékadással mind a négy kódolási stílus garantálta a szimuláció helyességét • nemblokkoló értékadással mind a négy kódolási stílus garantálta a helyes szintézist Megfelelő sorrendben használt blokkoló értékadó utasításokkal lehetséges helyesen kódolni a csővezetékes modellt. Másrészt viszont nagyon könnyű nemblokkoló utasításokkal kódolni a csővezetékes logikát; valójában a nemblokkoló értékadást használó stílusok mindegyike működik szintézis és szimuláció esetén is.
Szakasz 9: Blokkoló értékadás és egyszerű példák
345
9. Blokkoló értékadás és egyszerű példák Sok, Verilogról és Verilog szintézisről szóló könyvben szerepelnek egyszerű példák, amelyeket sikeresen kódoltak blokkoló értékadások használatával. Az A.13 programlistán szereplő flipflop modell a legtöbb Verilog könyvben szerepel. Programlista A.13: Egyszerű, de hibás D-flipflop model - blokkoló értékadással module dffb ( q , d , clk , r s t ) ; output q ; input d , clk , r s t ; reg q ; always @( posedge clk ) i f ( r s t ) q = 1 ’ b0 ; else q = d; endmodule
Ha a tervező valamennyi modult egyetlen always blokkra akarja
Szakasz 9: Blokkoló értékadás és egyszerű példák
346
Programlista A.14: D flip-flop modell javasolt modellezése nemblokkoló értékadásokkal module dffx ( q , d , clk , r s t ) ; output q ; input d , clk , r s t ; reg q ; always @( posedge clk ) i f ( r s t ) q <= 1 ’ b0 ; e l s e q <= d ; endmodule
Szakasz 9: Blokkoló értékadás és egyszerű példák
347
korlátozni, a blokkoló értékadások használhatók arra, hogy helyesen modellezzük, szimuláljuk és szintetizáljuk a kívánt logikát. Sajnos, ez ahhoz a rossz megszokáshoz vezet, hogy más, összetettebb szekvenciális logikákban levő always blokkokban is blokkoló értékadásokat fogunk használni, ami a korábban megismert versenyhelyzetek kialakulásához vezet. Ezért érdemes megszokni, hogy minden always blokkban, még ha csak egy blokkból áll is, mindig nemblokkoló értékadást használunk, mint az A.14 programlistán is.
Szakasz 10: Szekvenciális visszacsatolás modellezése
348
10. Szekvenciális visszacsatolás modellezése A visszacsatolt léptetőregiszter (Linear Feedback Shift-Register, LFSR) olyan szekvenciális logika, amelyben visszacsatolás is van. A visszacsatolás megvalósítása komoly kihívást jelent annak a mérnöknek, aki helyes sorrendben elhelyezett blokkoló értékadásokkal szeretné azt megvalósítani, lásd A.15 programlista. Az A.15 programlistán csak úgy lehet az értékadásokat sorba rendezni, hogy időleges változót is használunk. Csoportosíthatjuk ezeket az értékadásokat egysoros étékadássá, hogy elkerüljük időleges változó használatát, lásd A.16 programlista. Ettől viszont nehezen olvashatóvá ("rejtjelezetté") válik a kód. Nagyobb példák esetén az egysoros értékadásokat nehezen lehet kódolni és azokban hibát keresni. Nem feltétlenül érdemes törekedni egysoros értékadások használatára. Ha az előző két példában (A.15 és A.16 programlisták) a blokkoló értékadásokat nemblokkoló értékadásokkal helyettesítjük (lásd A.17 és A.18 programlisták), minden szimulációnk az elvártnak megfelelően
Szakasz 10: Szekvenciális visszacsatolás modellezése
349
Programlista A.15: Nem működő lineáris visszacsatoló regiszter blokkoló értékadással module l f s r b 1 ( q3 , clk , pre_n) ; output q3 ; input clk , pre_n ; reg q3 , q2 , q1 ; wire n1 ; assign n1 = q1 ^ q3 ; always @( posedge clk or negedge pre_n) i f ( ! pre_n) begin q3 = 1 ’ b1 ; q2 = 1 ’ b1 ; q1 = 1 ’ b1 ; end e l s e begin q3 = q2 ; q2 = n1 ; q1 = q3 ; end endmodule
Szakasz 10: Szekvenciális visszacsatolás modellezése
350
Programlista A.16: Működő, de "rejtjelezett" LFSR blokkoló értékadásokkal module l f s r b 2 ( q3 , clk , pre_n) ; output q3 ; input clk , pre_n ; reg q3 , q2 , q1 ; always @( posedge clk or negedge pre_n) i f ( ! pre_n) {q3 , q2 , q1} = 3 ’ b111 ; e l s e {q3 , q2 , q1} = {q2 , ( q1^q3 ) , q3 } ; endmodule
Szakasz 10: Szekvenciális visszacsatolás modellezése
351
fog működni. Az előző szakaszokban bemutatott példákból leszűrhetjük azt a tanulságot, hogy ajánlatos minden szekvenciális logikát nemblokkoló értékadásokkal modellezni. Hasonló módon az is kimutatható, hogy biztonságosabb a latcheket is nemblokkoló értékadásokkal modellezni. Ez indokolja a vonatkozó kódolási irányelveket: 1 Amikor szekvenciális logikát tervez, használjon nemblokkoló értékadásokat. 2 Amikor latch-eket modellez, használjon nemblokkoló értékadást.
Szakasz 10: Szekvenciális visszacsatolás modellezése
352
Programlista A.17: Működő LFSR nemblokkoló értékadásokkal module l f s r n 1 ( q3 , clk , pre_n) ; output q3 ; input clk , pre_n ; reg q3 , q2 , q1 ; wire n1 ; assign n1 = q1 ^ q3 ; always @( posedge clk or negedge pre_n) i f ( ! pre_n) begin q3 <= 1 ’ b1 ; q2 <= 1 ’ b1 ; q1 <= 1 ’ b1 ; end e l s e begin q3 <= q2 ; q2 <= n1 ; q1 <= q3 ; end endmodule
Szakasz 10: Szekvenciális visszacsatolás modellezése
353
Programlista A.18: Működő, de rejtjelezett LFSR nemblokkoló értékadásokkal module l f s r n 2 ( q3 , clk , pre_n) ; output q3 ; input clk , pre_n ; reg q3 , q2 , q1 ; always @( posedge clk or negedge pre_n) i f ( ! pre_n) {q3 , q2 , q1} <= 3 ’ b111 ; e l s e {q3 , q2 , q1} <= {q2 , ( q1^q3 ) , q3 } ; endmodule
Szakasz 11: Kombinációs logika - használjon blokkoló értékadást
354
11. Kombinációs logika - használjon blokkoló értékadást A Verilogban sokféle módon kódolhatunk egy kombinációs logikát, de ha a kombinációs logika megvalósításához always blokkot használunk, akkor blokkoló értékadást érdemes használnunk. Ha csak egyetlen értékadás szerepel az always blokkban, a blokkoló és a nemblokkoló értékadás is működni fog; a helyes kódolás megszokása érdekében célszerű mindig blokkoló értékadást használni kombinációs logikákban. Néhány Verilog tervező azt javasolja, hogy a nemblokkoló értékadást ne csak szekvenciális, hanem kombinációs logika esetén is használjuk. Egyszerű kombinációs always blokk kódolása esetén ez működik is, de ha több értékadás is szerepel az always blokkban, mint amilyet a A.19 listán is láthatunk, a késleltetés nélküli nemblokkoló értékadások vagy nem szimulálhatók helyesen, vagy további elemeket igényelnek az érzékenységi listában vagy pedig a helyes működéshez az always blokk többszöri végrehajtása lenne szükséges. Az utóbbi szimulációs idő szempontjából nagyon rontja a hatékonyságot.
Szakasz 11: Kombinációs logika - használjon blokkoló értékadást
355
Az A.19 listán látható kódban az y kimenet három szekvenciálisan végrehajtott utasításból épül fel. Mivel a nemblokkoló értékadások az RHS kifejezést az LHS változók frissítése előtt értékelik ki, a tmp1 és tmp2 értéke az lesz, amivel az always blokkba belépve rendelkeztek, nem pedig azok az értékek, amelyek csak a szimulációs időlépés végén frissítődnek. Az y kimenet a tmp1 és tmp2 régi értékét fogja tükrözni, nem pedig azt, amit az always blokk jelenlegi végrehajtása során számítódik ki. Az A.20 listán bemutatott kód azonos az A.19 programlista kódjával, kivéve, hogy a tmp1 és tmp2 változókat is hozzáadtuk az érzékenységi listához. Mint korábban láttuk, amikor a nemblokkoló értékadások frissítik az LHS változókat a nemblokkoló értékadás eseménysor ában, az always blokk önmagát triggereli és az újonnan kiszámított tmp1 és tmp2 értékekkel frissíti az y kimenet értékét. Az y kimenet értéke az always blokk kétszeri végrehajtása után már helyes lesz. Az always blokk többszöri végrehajtása azonban rontja a szimuláció hatékonyságát, és el kell kerülni, ha van más, ésszerű lehetőség.
Szakasz 11: Kombinációs logika - használjon blokkoló értékadást
356
Programlista A.19: Hibás kombinációs logikai kódolási stílus nemblokkoló utasítások használatával module ao4 ( y , a , b , c , d) ; output y ; input a , b , c , d ; reg y , tmp1, tmp2 ; always @( a or tmp1 <= a & tmp2 <= c & y <= tmp1 | end endmodule
b or c or d) begin b; d; tmp2 ;
Szakasz 11: Kombinációs logika - használjon blokkoló értékadást
357
Programlista A.20: Nem hatékony többmenetes kombinációs logikai kódolási stílus nemblokkoló utasítások használatával module ao5 ( y , a , b , c , d) ; output y ; input a , b , c , d ; reg y , tmp1, tmp2 ; always @( a or tmp1 <= a & tmp2 <= c & y <= tmp1 | end endmodule
b or c or d or tmp1 or tmp2) begin b; d; tmp2 ;
Szakasz 11: Kombinációs logika - használjon blokkoló értékadást
358
Sokkal jobb fejlesztői szokás, ami nem követeli meg az always blokk többszöri végrehajtását, hogy csak blokkoló értékadást használunk egy kombinációs logikát modellező always blokkban. Programlista A.21: Hatékony kombinációs logikai kódolási stílus blokkoló értékadások használatával module ao2 ( y , a , b , c , d) ; output y ; input a , b , c , d ; reg y , tmp1, tmp2 ; always @( a tmp1 = a tmp2 = c y = tmp1 end endmodule
or b or c or d) begin & b; & d; | tmp2 ;
Az A.21 programlista kódja megegyezik az A.19 programlista kódjával, kivéve, hogy a nemblokkoló értékadásokat blokkoló érték-
Szakasz 11: Kombinációs logika - használjon blokkoló értékadást
359
adásokkal helyettesítettük, ami biztosítja azt, hogy az y kimenet az always blokk egyszeri végrehajtása után már a helyes értéket kapja meg. Innét származik a következő irányelv: 3 Amikor kombinációs logikát always blokkal modellez, használjon blokkoló értékadást.
Szakasz 12: Kevert logika – használjon nemblokkoló értékadást
360
12. Kevert logika – használjon nemblokkoló értékadást Néha kényelmes egyszerű kombinációs értékadást szekvenciális logikai értékadással együtt használni. Amikor egy always blokkban kombinációs és szekvenciális kódot kombinál, kódolja az always blokkot nemblokkoló értékadásokkal, lásd A.22 programlista. Programlista A.22: Kombinációs és szekvenciális logika egy always blokkon belül module nbex2 ( q , a , b , clk , rst_n ) ; output q ; input clk , rst_n ; input a , b ; reg q ; always @( posedge clk or negedge rst_n ) i f ( ! rst_n ) q <= 1 ’ b0 ; e l s e q <= a ^ b ; endmodule
Szakasz 12: Kevert logika – használjon nemblokkoló értékadást
361
4 Amikor szekvenciális és kombinációs logikát is használ ugyanabban az always blokkban, használjon nemblokkoló értékadást. Az A.22 programlistán bemutatott logikát két különálló always blokként is implementálhatjuk. Itt az egyik always blokk tisztán kombinációs logika blokkoló értékadással, a másik pedig tisztán szekvenciális logika nemblokkoló értékadással, lásd A.23 ábra. A Verilog ugyan megengedi, hogy hogy egy always blokkon belül szabadon keverjük a blokkoló és nemblokkoló értékadásokat, az mégis hibás kódolási stílusnak tekintendő. Az A.24 programlistán bemutatott kód helyesen szimulálható és szintetizálható, mivel a blokkoló és nemblokkoló értékadás nem ugyanarra a változóra vonatkozik. Bár működhet, nem érdemes ezt a stílust követni. Az A.25 programlista kódja általában helyesen szimulálható, de
Szakasz 12: Kevert logika – használjon nemblokkoló értékadást
362
Programlista A.23: Kombinációs és szekvenciális logika egy always blokkon belül module nbex1 ( q , a , b , clk , rst_n ) ; output q ; input clk , rst_n ; input a , b ; reg q , y ; always @( a or b) y = a ^ b; always @( posedge clk or negedge rst_n ) i f ( ! rst_n ) q <= 1 ’ b0 ; e l s e q <= y ; endmodule
Szakasz 12: Kevert logika – használjon nemblokkoló értékadást
363
Programlista A.24: Blokkoló és nemblokkoló értékadás ugyanabban az always blokkban – általában rossz ötlet! module ba_nba2 ( q , a , b , clk , rst_n ) ; output q ; input a , b , rst_n ; input clk ; reg q ; always @( posedge clk or negedge rst_n ) begin : f f reg tmp ; i f ( ! rst_n ) q <= 1 ’ b0 ; e l s e begin tmp = a & b ; q <= tmp ; end end endmodule
Szakasz 12: Kevert logika – használjon nemblokkoló értékadást
364
van olyan szintézis eszköz (Synopsys tools) amelyik szintaxis hibát jelez, mivel ugyanaz a változó blokkoló és nemblokkoló értékadásban kap egyidejűleg értéket. Ilyenkor módosítani kell a kódot, hogy az szintetizálható legyen. Programlista A.25: Szintézis szintaxis hiba: blokkoló és nemblokkoló értékadás ugyanannak a változónak module ba_nba6 ( q , a , b , clk , rst_n ) ; output q ; input a , b , rst_n ; input clk ; reg q , tmp ; always @( posedge clk or negedge rst_n ) i f ( ! rst_n ) q = 1 ’ b0 ; // blocking assignment to "q" e l s e begin tmp = a & b ; q <= tmp ; // nonblocking assignment to "q" end endmodule
Szakasz 12: Kevert logika – használjon nemblokkoló értékadást
5 Ne keverje ugyanabban az always blokkban a blokkoló és nemblokkoló értékadásokat.
365
Szakasz 13: Többszörös értékadás ugyanannak a változónak
366
13. Többszörös értékadás ugyanannak a változónak Amikor ugyanannak a változónak több always blokkban többször is értéket adunk, az mindenképpen Verilog versenyhelyzetet idéz elő; még ha nem blokkoló értékadásokat használunk is. Az A.26 programlistában a két always blokk mindegyike értéket ad a q kimenetnek, és mindkettő nemblokkoló értékadásokat használ. Mivel ezek az értékadások bármilyen sorrendben ütemeződhetnek, a szimulációban versenyhelyzet áll elő. Amikor a Synopsys eszközök olvassák az A.26 kód példát, a következő figyelmeztetést adják: Warning: In design ’badcode1’, there is 1 multiple-driver net with unknown wired-logic type. Ha a figyelmeztetést elhanyagoljuk, és az A.26 kódot lefordítjuk, két flipflopot állítunk elő, amelyek kimenete egy két bemenetű and kapu bemeneteire kerül. Ez esetben a szintézis előtti és utáni szimuláció eredményei teljesen eltérőek lehetnek.
Szakasz 13: Többszörös értékadás ugyanannak a változónak
367
Programlista A.26: Versenyhelyzetet előidéző kódolási stílus nemblokkoló értékadás használatával module badcode1 ( q , d1 , d2 , clk , rst_n ) ; output q ; input d1 , d2 , clk , rst_n ; reg q ; always @( posedge clk or negedge rst_n ) i f ( ! rst_n ) q <= 1 ’ b0 ; e l s e q <= d1 ; always @( posedge clk or negedge rst_n ) i f ( ! rst_n ) q <= 1 ’ b0 ; e l s e q <= d2 ; endmodule
Szakasz 13: Többszörös értékadás ugyanannak a változónak
6 Ne adjon értéket ugyanannak a változónak egynél több always blokkban.
368
Szakasz 14: Városi legendák a nemblokkoló értékadásról
369
14. Városi legendák a nemblokkoló értékadásról Legenda: “#0 az értékadást a szimulációs időlépés végére kényszeríti” Valóság: #0 az értékadást az "inaktív eseménysor"ba kényszeríti Programlista A.27: Az A.28 listán bemutatott szimuláció eredménye 0ns : 0ns : 0ns : 0ns : 1ns : 1ns : 1ns : 1ns :
$display : a=0 #0 : a=0 b=1 $monitor : a=1 $strobe : a=1 $display : a=1 #0 : a=1 b=0 $monitor : a=1 $strobe : a=1
b=1 b=0 b=0 b=0 b=0 b=0
Az A.29 szimuláció eredménye az A.30 listán látható: a $display
Szakasz 14: Városi legendák a nemblokkoló értékadásról
370
utasítás még az aktív események között végrehajtódott, mielőtt még a nemblokkoló értékadás eredményének frissítése végrehajtódott volna. Az A.29 szimuláció eredménye az A.30 listán látható: a $display utasítás még az aktív események között végrehajtódott, mielőtt még a nemblokkoló értékadás eredményének frissítése végrehajtódott volna. Legenda: “A $display utasítás nem használható nemblokkoló értékadások eredményének megjelenítésére” Valóság: A nemblokkoló értékadások hatása csak az összes $display utasítás végrehajtása után jut érvényre
Szakasz 14: Városi legendák a nemblokkoló értékadásról
371
Programlista A.28: A nemblokkoló értékadások hatása csak az összes $display utasítás végrehajtása után jut érvényre module nb_schedule1 ; reg a , b ; i n i t i a l begin a = 0; b = 1; a <= b ; b <= a ; $monitor ( "%0dns : \$monitor : a=%b b=%b" , $stime , a , b) ; $display ( "%0dns : \$display : a=%b b=%b" , $stime , a , b) ; $strobe ( "%0dns : \$strobe : a=%b b=%b\n" , $stime , a , b) ; #0 $display ( "%0dns : #0 : a=%b b=%b" , $stime , a , b) ; #1 $monitor ( "%0dns : \$monitor : a=%b b=%b" , $stime , a , b) ; $display ( "%0dns : \$display : a=%b b=%b" , $stime , a , b) ; $strobe ( "%0dns : \$strobe : a=%b b=%b\n" , $stime , a , b) ; $display ( "%0dns : #0 : a=%b b=%b" , $stime , a , b) ; #1 $ f i n i s h ; end endmodule
Szakasz 14: Városi legendák a nemblokkoló értékadásról
372
Programlista A.29: A nemblokkoló értékadások hatása csak az összes $display utasítás végrehajtása után jut érvényre module display_cmds ; reg a ; i n i t i a l $monitor ( "\$monitor : a = %b" , a ) ; i n i t i a l begin $strobe ( "\$strobe : a = %b" , a ) ; a = 0; a <= 1 ; $display ( "\$display : a = %b" , a ) ; #1 $ f i n i s h ; end endmodule
Programlista A.30: Az A.29 listán bemutatott szimuláció eredménye $display : a = 0 $monitor : a = 1 $strobe : a = 1
Szakasz 14: Városi legendák a nemblokkoló értékadásról
Legenda: “Making multiple nonblocking assignments to the same variable in the same always block is undefined” Valóság: Making multiple nonblocking assignments to the same variable in the same always block is defined by the Verilog Standard. The last nonblocking assignment to the same variable wins! Quoting from the IEEE 1364-1995 Verilog Standard [2], pg. 47, section 5.4.1 - Determinism "Nonblocking assignments shall be performed in the order the statements were executed. Consider the following example: initial begin a <= 0; a <= 1; end When this block is executed, there will be two events added to the nonblocking assign update queue. The previous rule requires that they be entered on the queue in source order; this rule requires that they be taken from the queue and performed in source order as well. Hence, at the end of time-step 1, the variable a will be assigned 0 and then 1."
373
Szakasz 14: Városi legendák a nemblokkoló értékadásról
374
Irodalomjegyzék [1] Zainalabedin Navabi, Verilog Digital System Design, McGrawHill, 2006. 10, 15, 17, 21, 27 [2] DOULOS Ltd/Mentor Graphics, Comprehensive Verilog: Training Workbook, Unpublished, 2001. 30, 35, 39, 43, 44, 47, 48, 65, 89, 90, 289, 290, 292, 294, 296, 297, 299, 304, 306, 308 [3] Clifford E. Cummings, „Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!”, in SNUG-2000, San Jose, CA, 2000, http://www.sunburst-design.com/papers. 314, 327, 334 [4] IEEE Computer Society, IEEE Standard Hardware Description Language Based on the Verilog Hardware Description Language, IEEE Std 1364-1995. 317
Szakasz 14: Városi legendák a nemblokkoló értékadásról
375
Ábrák jegyzéke I.1. Programozható logikák tervezési folyama . . . . . . . .
10
I.2. Szimuláció testbench és hullámforma szerkesztő használatával . . . . . . . . . . . . . . . . . . . . . . . . . .
15
I.3. Verilog Szimuláció testbench használatával . . . . . . .
17
I.4. A fordítás és szintézis folyamata . . . . . . . . . . . .
21
I.5. Példa a szintézisre . . . . . . . . . . . . . . . . . . . .
27
II.1. A Verilog története. . . . . . . . . . . . . . . . . . . .
30
II.2. A Verilog használati köre . . . . . . . . . . . . . . . .
35
II.3. Absztrakciós szintek . . . . . . . . . . . . . . . . . . .
39
II.4. A tervezési folyam . . . . . . . . . . . . . . . . . . . .
43
II.5. A tervezési folyam . . . . . . . . . . . . . . . . . . . .
44
Szakasz 14: Városi legendák a nemblokkoló értékadásról
376
II.6. Szinkron tervezés . . . . . . . . . . . . . . . . . . . . .
47
II.7. Időzítési kényszerek . . . . . . . . . . . . . . . . . . . .
48
III.1. Az eddigi nyelvi szabályok . . . . . . . . . . . . . . . .
65
III.2. Példa logikai érték használatára . . . . . . . . . . . . .
89
III.3. Példa vektor részének kiválasztására . . . . . . . . . .
90
IV.1. Modul határok és a reg/wire átmenet
. . . . . . . . . 134
IV.2. Inout port értelmezése . . . . . . . . . . . . . . . . . . 137 IV.3. Vezeték és regiszter kapcsolata . . . . . . . . . . . . . 138 V.1. Nem-teljes értékadás latch tároló nélkül . . . . . . . . 153 VII.1.Szintézis minták . . . . . . . . . . . . . . . . . . . . . 207
Szakasz 14: Városi legendák a nemblokkoló értékadásról
377
VII.2.Szintézis minták . . . . . . . . . . . . . . . . . . . . . 208 VII.3.RTL szintézis . . . . . . . . . . . . . . . . . . . . . . . 210 VIII.1. Bitenkénti és redukciós operátorok . . . . . . . . . . . 224 VIII.2. Logikai operátorok . . . . . . . . . . . . . . . . . . . . 226 VIII.3. Összehasonlító operátorok . . . . . . . . . . . . . . . . 227 VIII.4. Összeillesztés (Concatenation) . . . . . . . . . . . . . . 229 VIII.5. Másolatkészítés (Replication) . . . . . . . . . . . . . . 231 VIII.6. Léptető regiszterek (shift register) . . . . . . . . . . . 233 VIII.7. Léptető operátorok (shift operators) . . . . . . . . . . 234 VIII.8. ‘define és ‘include . . . . . . . . . . . . . . . . . . . . 235 VIII.9. ‘ifdef . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 VIII.10. Paraméterek . . . . . . . . . . . . . . . . . . . . . . . . 241
Szakasz 14: Városi legendák a nemblokkoló értékadásról
378
VIII.11. A paraméterek használata . . . . . . . . . . . . . . . . 242 VIII.12. Paraméterek használata más modulokban . . . . . . . 244 VIII.13. Paraméterek egy különálló fájlban . . . . . . . . . . . 245 VIII.14. Paraméterezett modul . . . . . . . . . . . . . . . . . . 247 VIII.15. Paraméterek felülírása . . . . . . . . . . . . . . . . . . 248 VIII.16. defparam . . . . . . . . . . . . . . . . . . . . . . . . . 250 VIII.17. Hierarchikus nevek . . . . . . . . . . . . . . . . . . . . 253 VIII.18. Felfelé irányuló név hivatkozások . . . . . . . . . . . . 255 IX.1. Véges állapotú automaták . . . . . . . . . . . . . . . . 258 IX.2. Véges állapotú automaták . . . . . . . . . . . . . . . . 261 IX.3. Az automata felépítése . . . . . . . . . . . . . . . . . . 264 IX.4. Állapotkódolás . . . . . . . . . . . . . . . . . . . . . . 275
Szakasz 14: Városi legendák a nemblokkoló értékadásról
379
IX.5. Elérhetetlen állapotok . . . . . . . . . . . . . . . . . . 278 IX.6. Elérhetetlen állapotok vezérlése . . . . . . . . . . . . . 279 X.1. Aritmetikai operátorok . . . . . . . . . . . . . . . . . . 284 X.2. Előjeles és előjel nélküli . . . . . . . . . . . . . . . . . 285 X.3. Aritmetikai operátorok szintézise . . . . . . . . . . . . 287 X.4. Mi történik a F <= A + B utasítás hatására? . . . . 289 X.5. Makrocellák használata . . . . . . . . . . . . . . . . . 290 X.6. Az összeadók kiválasztása . . . . . . . . . . . . . . . . 292 X.7. Az aritmetika nem optimalizált . . . . . . . . . . . . . 294 X.8. Az aritmetika tényleg nem optimalizált . . . . . . . . . 296 X.9. Aritmetikai azt kapod, amit látsz . . . . . . . . . . . . 297 X.10.Resource sharing . . . . . . . . . . . . . . . . . . . . . 299
Szakasz 14: Városi legendák a nemblokkoló értékadásról
380
X.11.Előjel kiterjesztés . . . . . . . . . . . . . . . . . . . . . 304 X.12.Számlálók szintézise . . . . . . . . . . . . . . . . . . . 306 X.13.Számlálók szintézise . . . . . . . . . . . . . . . . . . . 308 A.1. Verilog "réteges esemény sora" . . . . . . . . . . . . . 327 A.2. Szekvenciális csővezeték regiszter . . . . . . . . . . . . 334
Szakasz 14: Városi legendák a nemblokkoló értékadásról
381
Táblázatok jegyzéke III.1. A Verilog fenntartott azonosítói . . . . . . . . . . . . .
69
III.2. Az összekötések típusai . . . . . . . . . . . . . . . . . .
75
Szakasz 14: Városi legendák a nemblokkoló értékadásról
382
A programlisták jegyzéke III.1 A ’Hello Világ’ program (Nem minden platformon működik ebben az egyszerű alakban) . . . . . . . . . . III.2 Egy modul ami AND-OR-INVERT kaput ír le . . . . . III.3 Megjegyzések használata a Verilog programokban . . . III.4 Azonosítók használata a Verilog programokban . . . . III.5 Egy modul ami AND-OR-INVERT kaput ír le; belső jelekkel . . . . . . . . . . . . . . . . . . . . . . . . . . III.6 Az AND-OR-INVERT modul többszörös értékadással III.7 Az AND-OR-INVERT modul többszörös deklarációval III.8 Példa modul hierarchiára sorrendi leképezéssel . . . . III.9 Példa modul hierarchiára név szerinti leképezéssel . . . III.10Példa modul hierarchiára primitívek használatával . . III.11Példa be nem kötött portok használatára . . . . . . . III.12Példa vektor port használatára . . . . . . . . . . . . . III.13A MUX4 modul tesztelésére szolgáló teszt fixtúra . . . III.14Példa a tesztjelek előállítására . . . . . . . . . . . . . . III.15Példa regiszterek használatára . . . . . . . . . . . . . .
59 63 67 67 71 72 73 79 80 81 85 86 93 95 97
Szakasz 14: Városi legendák a nemblokkoló értékadásról
III.16Példa eredmények kiíratására . . . . . . . . . . . III.17A teljes testbench modul . . . . . . . . . . . . . . IV.1 Számok megadása a Verilogban . . . . . . . . . . IV.2 Számok csonkítása és kiterjesztése . . . . . . . . IV.3 Megtévesztő bináris számok . . . . . . . . . . . . IV.4 Számok, 32 biten túl . . . . . . . . . . . . . . . . IV.5 Formattált kimenet . . . . . . . . . . . . . . . . . IV.6 Formattált kimenet . . . . . . . . . . . . . . . . . IV.7 Időskála . . . . . . . . . . . . . . . . . . . . . . . IV.8 Többszörös időskála; A modul . . . . . . . . . . . IV.9 Többszörös időskála; B modul . . . . . . . . . . . IV.10Többszörös időskála; C modul . . . . . . . . . . . IV.11Az időkiírás formátumának megadása . . . . . . IV.12Példa az always procedurális blokk használatára IV.13A $stop és $finish direktívák hatása . . . . . . . IV.14Regiszterek használata . . . . . . . . . . . . . . . IV.15Vezetékek használata . . . . . . . . . . . . . . . . IV.16Modul határok és a reg/wire átmenet . . . . . . IV.17Inout port használata . . . . . . . . . . . . . . .
383
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
99 102 105 107 109 110 114 116 117 120 120 121 122 125 127 130 132 133 135
Szakasz 14: Városi legendák a nemblokkoló értékadásról
Az always utasítás két tipikus használati módja . . . . Az always utasítás két tipikus használati módja . . . . A begin-end utasításpár használata . . . . . . . . . . Egymásba ágyazott else-if utasítások hatása . . . . . . Egymásba ágyazott else-if utasítások hatása begin-end nélkül . . . . . . . . . . . . . . . . . . . . . . . . . . . V.6 Egymásba ágyazott else-if utasítások hatása begin-end használatával . . . . . . . . . . . . . . . . . . . . . . . V.7 Nem-teljes értékadás használata . . . . . . . . . . . . . V.8 Ismeretlen és lényegtelen értékek használata . . . . . . V.9 A feltételes operátor használata . . . . . . . . . . . . . V.10 A feltételes értékadás operátor használata . . . . . . . V.11 Tristate állapot megvalósítása . . . . . . . . . . . . . . VI.1 A case utasítás használati módja . . . . . . . . . . . . VI.2 A case utasítás további tulajdonságai . . . . . . . . . VI.3 A casez utasítás tulajdonságai . . . . . . . . . . . . . VI.4 A casex utasítás tulajdonságai . . . . . . . . . . . . . VI.5 Prioritás enkóder case utasítás használatával . . . . . VI.6 Prioritás enkóder ekvivalens if utasítások használatával V.1 V.2 V.3 V.4 V.5
384
141 144 147 148 149 150 152 155 157 159 160 165 166 167 170 172 173
Szakasz 14: Városi legendák a nemblokkoló értékadásról
VI.7 A parallel case programlista . . . . . . . . . . . . . . . VI.8 Prioritás enkóder casez használatával . . . . . . . . . VI.9 "One-Hot" Dekóder megvalósítása casez használatával VI.10for ciklus megvalósítása HW ismétléssel . . . . . . . . VI.11for ciklusváltozó deklarálása . . . . . . . . . . . . . . . VI.12for ciklusváltozó deklarálása . . . . . . . . . . . . . . . VI.13Egyéb ciklusutasítások . . . . . . . . . . . . . . . . . . VI.14Blokk végrehajtás megszakítása . . . . . . . . . . . . . VI.15Elnevezett blokk és a deklaráció . . . . . . . . . . . . . VI.16Kombinációs always blokk . . . . . . . . . . . . . . . . VII.1always – latch tárolóval . . . . . . . . . . . . . . . . . VII.2Él-vezérelt Flip-Flop . . . . . . . . . . . . . . . . . . . VII.3Szimulációs versenyhelyzetek elkerülése . . . . . . . . . VII.4Nem-blokkoló értékadás . . . . . . . . . . . . . . . . . VII.5Aszinkron értékadás és törlés . . . . . . . . . . . . . . VII.6Szinkron és aszinkron akciók . . . . . . . . . . . . . . VII.7Technológia-specifikus tulajdonságok . . . . . . . . . . VII.8Flip-flopok kikényszerítése . . . . . . . . . . . . . . . . VII.9Flip-flopok kikényszerítése - blokkoló értékadás . . . .
385
174 175 176 180 182 183 185 187 189 191 195 196 198 200 202 203 205 212 214
Szakasz 14: Városi legendák a nemblokkoló értékadásról
VII.10Flip-flopok kikényszerítése - blokkoló értékadás . VII.11Flipflop előállítás - blokkoló értékadással . . . . . VII.12Flipflop előállítás - blokkoló értékadással . . . . . VII.13Flipflop előállítás - blokkoló értékadással . . . . . VII.14Kézi optimalizálás . . . . . . . . . . . . . . . . . IX.1 Az automata állapotainak explicit megadása . . . IX.2 Az automata állapotainak explicit megadása . . . IX.3 Regiszterek eltávolítása a kombinációs logikából . IX.4 Kimeneti dekódolás nélkül . . . . . . . . . . . . . IX.5 Regiszterek eltávolítása a kombinációs logikából . IX.6 Regiszterek eltávolítása a kombinációs logikából . X.1 A integer típus 32 bitté szintetizálódik . . . . . . X.2 A integer típus 32 bitté szintetizálódik . . . . . . X.3 A integer típus 32 bitté szintetizálódik . . . . . . X.4 Kapuzott órajelek . . . . . . . . . . . . . . . . . X.5 Aszinkron tervezés . . . . . . . . . . . . . . . . . A.1 Visszacsatolt oszcillátor blokkoló értékadással . . A.2 Visszacsatolt oszcillátor nemblokkoló értékadással
386
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
215 217 218 219 220 263 266 268 270 271 273 300 301 303 310 311 320 322
Szakasz 14: Városi legendák a nemblokkoló értékadásról
A.3 Nem öntriggerelő oszcillátor blokkoló értékadás használatával . . . . . . . . . . . . . . . . . . . . . . . . . . A.4 Öntriggerelő oszcillátor nemblokkoló értékadás használatával . . . . . . . . . . . . . . . . . . . . . . . . . . . A.5 Hibás blokkoló értékadásos szekvenciális kódolási stílus #1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.6 Csúnya, de működő blokkoló értékadásos szekvenciális kódolási stílus #2 . . . . . . . . . . . . . . . . . . . . . A.7 Hibás szekvenciális kódolási stílus blokkoló értékadással #3 . . . . . . . . . . . . . . . . . . . . . . . . . . . A.8 Hibás szekvenciális kódolási stílus blokkoló értékadással #4 . . . . . . . . . . . . . . . . . . . . . . . . . . . A.9 Helyes szekvenciális kódolási stílus nemblokkoló értékadással #1 . . . . . . . . . . . . . . . . . . . . . . . . A.10 Helyes szekvenciális kódolási stílus nemblokkoló értékadással #2 . . . . . . . . . . . . . . . . . . . . . . . . A.11 Helyes szekvenciális kódolási stílus nemblokkoló értékadással #3 . . . . . . . . . . . . . . . . . . . . . . . .
387
331 332 335 337 338 339 340 341 342
Szakasz 14: Városi legendák a nemblokkoló értékadásról
388
A.12 Helyes szekvenciális kódolási stílus nemblokkoló értékadással #4 . . . . . . . . . . . . . . . . . . . . . . . . 343 A.13 Egyszerű, de hibás D-flipflop model - blokkoló értékadással . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 A.14 D flip-flop modell javasolt modellezése nemblokkoló értékadásokkal . . . . . . . . . . . . . . . . . . . . . . 346 A.15 Nem működő lineáris visszacsatoló regiszter blokkoló értékadással . . . . . . . . . . . . . . . . . . . . . . . . 349 A.16 Működő, de "rejtjelezett" LFSR blokkoló értékadásokkal350 A.17 Működő LFSR nemblokkoló értékadásokkal . . . . . . 352 A.18 Működő, de rejtjelezett LFSR nemblokkoló értékadásokkal . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 A.19 Hibás kombinációs logikai kódolási stílus nemblokkoló utasítások használatával . . . . . . . . . . . . . . . . . 356 A.20 Nem hatékony többmenetes kombinációs logikai kódolási stílus nemblokkoló utasítások használatával . . . . 357 A.21 Hatékony kombinációs logikai kódolási stílus blokkoló értékadások használatával . . . . . . . . . . . . . . . . 358
Szakasz 14: Városi legendák a nemblokkoló értékadásról
A.22 Kombinációs és szekvenciális logika egy always blokkon belül . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.23 Kombinációs és szekvenciális logika egy always blokkon belül . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.24 Blokkoló és nemblokkoló értékadás ugyanabban az always blokkban – általában rossz ötlet! . . . . . . . A.25 Szintézis szintaxis hiba: blokkoló és nemblokkoló értékadás ugyanannak a változónak . . . . . . . . . . . . . A.26 Versenyhelyzetet előidéző kódolási stílus nemblokkoló értékadás használatával . . . . . . . . . . . . . . . . . A.27 Az A.28 listán bemutatott szimuláció eredménye . . . A.28 A nemblokkoló értékadások hatása csak az összes $display utasítás végrehajtása után jut érvényre . . . . A.29 A nemblokkoló értékadások hatása csak az összes $display utasítás végrehajtása után jut érvényre . . . . A.30 Az A.29 listán bemutatott szimuláció eredménye . . .
389
360 362 363 364 367 369 371 372 372