Sunteți pe pagina 1din 45

Lab1-OC

A: Tutorial de Verilog.
B: Introducere in FPGA-uri. Plăci Altera DE2

Part A: Tutorial de Verilog

1. Introducere
1.1. Ce este Verilog-ul ?
1.2. Cateva caracteristici
1.3. Modelarea unui system digital cu Verilog
1.3.1. Modelul flux de date
1.3.2. Modelarea comportamentala
1.3.3. Modelarea structurala
1.3.4. Modelarea mixtă

2. Elemente si artificii ale limbajului Verilog HDL prezentate prin


exemple
2.1. Modulul Verilog
2.2. Atribuiri de tip blocking şi non-blocking
2.3. Tipuri de date
2.3.1. Tipuri net
2.3.2. Tipuri variabilă
2.3.3. Diferenţa între reg şi wire
2.3.4. Semnale pe mai mulţi biţi
2.4. Operatori Verilog HDL
2.5. Clauza initial
2.6. Instrucţiuni procedurale
2.6.1. Instrucţiuni decizionale
2.6.2. Instrucţiuni repetitive
2.6.3. Instrucţiuni de tip bloc secvenţial
2.7. Alte tipuri de instrucţiuni
2.8. Funcţii şi task-uri Verilog HDL
2.9. Task-urile şi funcţiile sistem

3. Verificarea şi simularea unui design Verilog HDL


3.1. Modelarea întârzierilor
3.2. Modulul de test (testbench)

Copyright @ Oana Boncalo & Alexandru Amaricai


Part A: Tutorial de Verilog

Obiective:
dobândirea unor cunostinţe de bază prinvind modelarea sistemelor digitale
folosind limbajul de descriere hardware Verilog. În cele ce urmează, prezentul
material oferă o descriere succintă a principalelor noţiuni (subset Verilog) în
vederea modelării şi sintezei unor sisteme digitale de complexitate medie.

1. Introducere

1.1. Ce este Verilog-ul ?

Verilog-ul este un limbaj de descriere hardware (HDL) care permite modelarea unui
sistem digital la diferite niveluri de abstractizare: algoritmic, comportamental, portă
logică, nivelul comutator (switch level). Acesta se bucură de o mare popularitate în
rândurile designerilor de hardware şi a producătorilor de tool-uri CAD. Din acest
considerent, Verilog HDL a fost transformat într-un standard IEEE, ultima acţiune de
acest tip concretizându-se prin standardul IEEE 1364-2001.

1.2. Câteva caracteristici

O scurtă enumerare a principalelor trăsături ale limbajului Verilog HDL cuprinde:


• Primitive logice elementare, precum porţi logice and, or şi nand sunt definite în
cadrul limbajului;
• Este un standard IEEE;
• Permite trei stiluri diferite de modelare: flux de date, comportamental, şi
structural;
• Prezintă două tipuri de date: net-ul care modelează conexiunea fizică între
elementele de structură (firul/traseul fizic) şi tipul variabilă (un element de
memorare al informatiei abstract);
• Permite mai multe niveluri de abstractizare, pornind de la nivelul comutator, la
nivelul poartă logică, nivelul transfer la nivel de regiştri (register transfer level –
RTL), la nivel de algoritm;
• Pot fi scrise entităţi de test (testbench-uri) şi generaşi stimuli pentru procesul de
verificare a designurilor;
• Trăsături precum execuşia concurentă şi întârzierile (timpul) pot fi modelate;

Copyright @ Oana Boncalo & Alexandru Amaricai


• Facilităţi de manipulare a fişierelor;

1.3. Modelarea unui sistem digital folosind Verilog HDL

Există mai multe stiluri de a modela un sistem în Verilog. Elementul central in Verilog
este constituit de noţiunea de modul. Acesta poate fi folosit pentru a modela atât elemente
simple, cât şi sisteme complexe. În esenţă un design Verilog complex poate fi asimilat cu
o multitudine de module interconectate, reprezentând subansamblele sale. În cadrul unui
modul se pot întalni următoarele stiluri de modelare:
• Modelarea sub formă de flux de date;
• Modelarea comportamentală;
• Modelarea structurală;
• Modelarea mixtă;
Acestea vor fi succint prezentate prin nişte exemple în cele ce urmează.

1.3.1. Modelul flux de date


Mecanismul care este folosit pentru modelul flux de date se bazează pe instrucţiunea
assign, de atribuire continuă. Aceasta realizează evaluarea continuă (reevaluează expresia
de fiecare dată când unul dintre operanzi îşi modifică valoarea) a expresiei din dreapta
egalului, şi atribuie noua valoare unui net (vector de tip net). În dreapta putem avea atât
tipul net cît si tipul variabilă. Sintaxa este următoarea:

assign [delay] tipul_net = expresie;

Specificarea unei întârzieri este opţională. Ea trebuie să fie precedată de o directivă de


compilator care asociază o unitate de timp. Aceasta apare înaintea declaraţiei de modul.

`timescale 2ns/1ns

Această directivă stabileşte că unitatea de timp este egală cu 2 ns şi că se lucrează cu o


precizie de 1 ns (toate întârzierile sunt rotunjite la 1 ns). Aşadar un exemplu de atribuire
continuă este următorul:

assign #2 a=b;

Atribuie lui a valoarea lui b cu o întârziere de 2 cuante de timp (pentru directiva


timescale din exemplul anterior după 4 ns).
Atribuirea continuă reprezintă o modalitate naturală de modelare a logicii
combinaţionale. Operanzii corespunzători În exemplul următor este prezentat codul
Verilog HDL pentru o celulă sumator completă cu 3 intrări de date şi două ieşiri. De
asemenea sunt prevăzute întârzieri pentru operaţiile logice din celula de însumare.

Copyright @ Oana Boncalo & Alexandru Amaricai


s = a ⊕ b ⊕ cin
cout = ab + acin + bcin

Figura 1: Celulă sumator pe 1 bit. Cod Verilog HDL pentru modelul de tip flux de
date

Operanzii corespunzători acestiu tip de descriere sunt de nivel jos:


• Atribuire condiţionată:

(expresie_condiţie)? (valoare?adevărat): (valoare_fals);

Ex.: assign out= sel? in1: in2;


• Operatori logici: ~, &, |, ^ (negare, şi, sau, xor);
• Operatori aritmetici: +, +, *;

Există şi posibilitatea folosirii de construcţii încuibate.

Trebuie insistat asupra faptului că aceste instrucţiuni se execută concurent (nu contează
ordinea în care au fost scrise, ele bucurându-se de calitatea de execuţie paralelă, - aspect
similar cu execuţia portilor logice dintr-un design hardware).

Copyright @ Oana Boncalo & Alexandru Amaricai


1.3.2. Modelarea comportamentală
Are la bază construcţii de tip always. Acestea permit o descriere de nivel înalt a
comportamentului unui dispozitiv hardware prin folosirea unor instrucţiuni care se
execută secvenţial similare cu cele întâlnite în limbajul C precum cele de decizie (if,
case), repetitive (for, while), etc..
Există două tipuri de blocuri de cod Verilog HDL care sunt executate secvenţial:
• initial – este executat o singură dată, de regulă la instanţierea unei componente de
tipul celei definite de modulul în care se află blocul initial. Rolul lui este de a
realiza o iniţializare a componentei;
• always – folosit pentru a descrie comportamentul unui dispozitiv (părţi a acestuia)
hardware;

Pentru a exemplifica acet tip de modelare vom folosi acelaşi dipozitiv – celula de
însumare completă.

Figura 2: Celulă sumator pe 1 bit. Cod Verilog HDL pentru modelarea comportamentală

O clauză always trebuie să fie prevăzută cu un fel de control pentru timp. Acesta poate fi
reprezentat fie de întârzieri (wait – aşteaptă un interval de timp), fie de producerea unui
eveniment (wait – aşteaptă modificarea unui semnal din lista de senzitivităţi). În lipsa
acestora, codul este executat la infinit.

Foarte important este şi cuvântul cheie reg. Acesta nu face referire la regiştri întâlniţi în
calculul digital. Aceştia din urmă îşi modifică valoarea sincron cu un semnal de tact.

Copyright @ Oana Boncalo & Alexandru Amaricai


Registri – reg – Verilog HDL nu necesită tact, nu li se induce o valoare într-un mod
analog firelor fizice, şi îşi pot modifica valoarea în orice moment al simulării.

În Verilog HDL, cuvântul cheie reg denotă o variabilă care poate memora o valoare la
un moment dat.

1.3.3. Modelarea structurală


Verilog HDL suportă porţi logice elementare (primitive):
• and, nand, or, nor, xor, xnor, not, buf
• cu intrări multiple nanad3in(out, in1, in2, in3);
• buif1 şi buif0 sunt bufere de tip tristate;

Copyright @ Oana Boncalo & Alexandru Amaricai


Figura 3: Celulă sumator pe 1 bit. Cod Verilog HDL pentru modelarea structurală

Acestea sunt instanţiate în componentele unui design, şi sunt conectate prin net-uri.
Acestea se declară folosind cuvântul cheie wire. Reprezintă analogul firelor/traseelor din
design-urile fizice.

1.3.4. Modelarea mixtă


De multe ori in descrierea HDL este folosita o abordare mixta, care presupune folosirea a
oricăror doua modalităţi de descriere hardware în cadrul unui modul. În cele ce urmează
extindem exemplul cu celula de însumare pentru modelarea mixtă.

Figura 4: Celulă sumator pe 1 bit. Cod Verilog HDL pentru modelarea mixtă

Copyright @ Oana Boncalo & Alexandru Amaricai


Dacă instanţierea altor tipuri de subsisteme declarate prin alte module nu ridică probleme,
este important de realizat diferenţa dintre o atribuire la un reg, şi o atribuire la un wire.
Astfel această diferenţă este punctată în cele ce urmează:
• Pentru reg – atribuirea se face la fiecare execuţie a codului (de regulă dintr-o
clauză always); aşadar ea este condiţionată de apariţia evenimentului dorit la unul
sau mai multe semnale de gardă (front crescător de tact, modificarea palierului
unui semnal – funcţie de realitatea modelată);
• Pentru wire – atribuirea/evaluarea expresiei din dreapta este una continuă;

Exemplele prezentate în această secţiune şi-au propus să realizeze o scurtă trecere în


revistă a unui sub-set din trăsăturile şi posibilităţile oferite de Verilog HDL pentru
descrierea dispozitivelor logice.

2. Elemente şi artificii ale limbajului Verilog HDL prezentate prin


exemple

2.1 Modulul Verilog


Reprezintă unitatea de structură în Verilog HDL. Acesta prezintî urmîtoarea sintaxă:

module nume_modul (listă_porturi);


declaraţii_şi_instrucţiuni
endmodule

Lista de porturi defineşte interfaţa unui modul – maniera prin care comunică cu
exteriorul. Porturile pot fi de trei tipuri:
• Intrare – input
• Ieşire – output
• Bidirecţionale – inout
Un port este în mod implicit de tip net (reamintim că tipul net este asimilat unei
conexiuni fizice – vezi secţiunea 2.3.1). Opţional (dacă este folosit în cadrul unei
construcţii always) porturile de ieşire pot si declarate de tip reg.

Declaraţiile de porturi se pot face în cadrul listei de porturi, fir pot constitui parte a
modulului (module port declaration style versus module port list style). În cele ce
urmează cele două stiluri vor fi exemplificate pentru a se evidenţia diferenţele.

În Verilog HDL, pentru claritate şi lizibilitate este proferabil să sie folosit module port
declaration style, întrucât atât porturile cât şi tipurile de date sunt specificate într-un
singur loc.

Din punct de vedere al corectitudinii codului, cele două tipuri de realizare a declaraţiilor
sunt echivalente.

Copyright @ Oana Boncalo & Alexandru Amaricai


Figura 5: Exemplificarea modalităţiilor de realizare a declaratiei porturilor unui modul.
module port declaration style versus module port list style
Verilog HDL oferă şi posibilitatea parametrizării designurilor, prin declararea unei liste
de parametri aferenţi modulului. Acest lucru este convenabil pentru descrierea unor
elemente de tip regiştri, sumatoare, numărătoare, etc..

module nume_modul
# (parameter param1=valoare1, param2=valoare2, ...
parameter param3=valoare3, ...)
(listă_porturi);

declaraţii_şi_instrucţiuni
endmodule

Un exemplu pentru folosirea parametrilor este prezrntat în cele ce urmează:

Figura 6: Exemplificarea modalităţilor de realizare a parametrilor unui modul

În continuare este prezentat modul în care se face instanţierea unui modul.

nume_modul nume_instanţă(listă_asocieri_porturi);
Asocierea porturilor din listă se poate face implicit, prin poziţia semnalelor asociate, sau
explicit printr-o construcţie de forma (prin nume):

Copyright @ Oana Boncalo & Alexandru Amaricai


.nume_port(expresie_care_se_asociază)

Această expresie poate fi o variabilă sau un net, un câmp sau sub-şir dintr-un şir, o
concatenare a celor anterior precizate, sau o expresie (numai pentru porturile care sunt de
intrare). Trebuie avut în vedere, faptul că nu este admisă folosirea simultană în cadrul
aceleiaşi instanţe a asocierii poziţionale cu cea bazată pe nume. De asemenea, asocierea
prin poziţie reclamă respectarea întocmai a ordinii şi tipului expresiilor asociate
porturilor. Atribuirea prin poziţie, admite schimbarea ordinii expresiilor asociate
porturilor în raport cu declaraţia de modul.

În Verilog HDL porturile neconectate rămân:


• Nefolosite dacă sunt de ieşire;
• Conectate la valoarea z dacă sunt de intrare;

În continuare este prezentat un exemplu pentru un sumator de tip ripple carry pe 4 biţi.
Acesta foloseşte celula de însumare descrisă în secţiunea 1, modificată astfel încât să
permită un parametru de timp – întârzierea pentru o poartă logică.

a)

b)

Figura 7: Sumator cu propagarea serială a transportului (ripple carry adder) a- schemă


bloc; b- structură sumator

Copyright @ Oana Boncalo & Alexandru Amaricai


a)

b)
Figura 8: Sumator cu propagarea serială a transportului (ripple carry adder) a- modul care
descrie celula de însumare; b- modul care descrie sumator cu propagarea serială a
întârzierii. Exemplu pentru diferitele modalităţi de realizare a instanţierii.

Ca şi ultimă remarcă, Verilog HDL este foarte flexibil în ceea ce priveşte declararea şi
instanţierea unui modul, oferind fiecărui programator posibilitatea de a-şi organiza codul
astfel încât să fie cât mai accesibil cu putinţă. Totuşi, este recomandat ca declaraţiile să

Copyright @ Oana Boncalo & Alexandru Amaricai


fie grupate pe cât, şi instanţierile să fie realizate folosind specificarea prin nume – mai
clară, care înlătură pericolul asocierii greşite prin poziţie a semnalelor.

2.1. Clauza always - Atribuiri de tip blocking şi non-blocking

Clauza always reprezintă principalul mecanism de modelare comportamentală a unui


sistem digital. Un modul poate conţine un număr arbitrar de astfel de declaraţii. O
declaraţie always defineşte un black-box, şi conţine descrierea comportamentului unui
sistem prin instrucţiuni procedurale (similare limbajului C - if, case, for, while, etc.). care
se execută in-order similar cu un cod scris într-un limbaj de nivel înalt. Aşadar, nu trebuie
pierdu din vedere faptul că deşi în interiorul unui bloc always instrucţiunile sunt
executate secvenţial (în ordinea în care sunt scrise), în interiorul modulului, instrucţiunile
sunt executate concurent.

always
[control_timp] construcţie_procedurală

O clauză always trebuie să fie prevăzută cu un fel de control pentru timp. Acesta poate fi
reprezentat fie de întârzieri (wait – aşteaptă un interval de timp), fie de producerea unui
eveniment (wait – aşteaptă modificarea unui semnal din lista de senzitivităţi). În lipsa
acestora, codul este executat la infinit. Cea mai folosită construcţie procedurală este
blocul secvenţial de tip - begin … end.

Lista de senzitivităţi defineşte semnalele care determină execuţia unui always. Aceasta
poate fi sensibil la front (crescător sau descrescător), sau la palierul unui semnal (pozitiv
sau negativ). Este important de precizat că o listă de sensitivităţi nu permite atât semnale
care sunt sensibile la front cât şi semnale sensibile la palier. Această diferenţă este
deosebit de importantă în contextul circuitelor secvenţiale. Pentru logica combinaţională
regula este simplă – şi anume – se vor trece în această listă toate semnalele care produc o
modificare a ieşirilor.

Realizăm o scurtă clasificare a principalelor elemente constructive secvenţiale:


• Latch-uri sunt asincrone şi evaluează în mod constant intrările;
• Flip-flop-urile (FF) sunt sincrone cu un semnal extern (tact) şi pot să fie sensibile
pe:
o front
o palier

Regiştri sunt o colecţie de FF. Cu toate că latch-urile sunt folosite pentru a construi
regiştri (configuraţie master-slave), ele apar rareori ca elemente de sine stătătoare în
cadrul unui design digital.

Atribuirile de tip blocking, respectiv non-blocking sunt deosebit de uzitate în descrierea


circuitelor digitale folosind Verilog HDL (dar se regăsesc sub forme similare şi în alte
limbaje de descriere hardware, precum VHDL-ul) prin construcţii de tip always.
Diferenţa dintre cele două tipuri este cel mai uşor de scos in relief în descrierea logicii

Copyright @ Oana Boncalo & Alexandru Amaricai


secvenţiale. Din aceste considerente, în ceea ce urmează, exemplele vor viza în special
circuitele secvenţiale.

Atribuirea de tip:

• Blocking
– uzitează de operatorul “=” şi presupune evaluarea şi actualizarea
imediată (instantanee) a valorii variabilei din stânga egalului;

• Non-blocking
– uzitează de operatorul “<=” şi presupune evaluarea expresiei din
dreapta şi actualizarea ei la sfârşitul pasului de simulare (la
terminarea blocului always);
– toate atribuirile de acest tip din cadrul unui bloc always sunt
evaluate cu valorile variabilelor dinainte de execuţia blocului. Cu
alte cuvinte, dacă avem o variabilă reg care îşi modifică la un
moment dat valoarea în cadrul blocului always, ca urmare a
evaluării expresiei din dreapta, această modificare nu este una
imediată, proximele evaluări folosind valoarea de la intrarea în
blocul always (valoarea veche);

Sunt situaţii în care, în ciuda acestor diferenţe se ajunge la acelaşi rezultat. În continuare
câteva exemple folosind declaraţii de tip always.

Semnal cu perioada de 8 unităţi de timp, care prezintă un control de timp de tip


întârziere.
always
# 4 clk_sig=~ clk_sig;

Figura 9: Exemplu de logică combinaţională cu cele două variante de atribuiri

Copyright @ Oana Boncalo & Alexandru Amaricai


În Verilog HDL instrucţiunile de tip if şi case sunt interpretate ad literam şi pot da
naştere în urma sintezei la o interpretare de logică prioritară.

Un exemplu în acest sens prezintă codul Verilog HDL pentru un codificator binar 4 la 2
(4-to-2 binary encoder), şi rezultatul obţinut în urma sintezei (interpretarea pentru codul
respectiv).

Figura 10: Exemplu de logică combinaţională – codificator 4-la-2. Tabel de adevăr,


schemă logică minimizată de tool-ul de sinteză, cod Verilog HDL

Copyright @ Oana Boncalo & Alexandru Amaricai


O modalitate nefericită de a descrie codificatorul 4-la-2 folosind clauza if determina
interpretarea de către tool-ul de sinteza drept logica prioritară. Interpretarea din exemplul
următor este – dacă i[0] este 1 atunci, independent de celelate intrări, ieşirea este 11.
Aşadar, i[0] este prioritară în raport cu celelalte intrări.

Figura 11: Exemplu de logică combinaţională – descriere eronata codificatorului 4-la-2.


Circuitul inferat de sintetizator în urma unei construcţii eronate pentru clauza if care duce
la sintetizarea de logică prioritară

În Verilog HDL netratarea explicită a tuturor ramurilor în cazul instrucţiunilor de tip if


şi case este interpretată ca şi memorarea stării anterioare, şi are ca rezultat de sinteză
inferarea unui latch (element de memorare) care să reţină starea veche.

În Verilog HDL construcţia always @ * este interpretată ca o listă de semnale care


cuprinde toate semnalele din bloc.

Copyright @ Oana Boncalo & Alexandru Amaricai


Această situaţie este prezentată în proximul exemplu, când pentru instrucţiunea case nu
este precizat cazul default. La un rezultat de sinteză absolut similar se ajunge şi în situaţia
exemplului cu instrucţiunea if, la care se omite ramura de else.

Figura 12: Exemplu de logică combinaţională – descriere eronata codificatorului 4-la-2.


Circuitul inferat de sintetizator în urma unei construcţii eronate pentru clauza case
(datorită netratării tuturor cazurilor pentru instructiunea case) conţine elemente de
memorare de tip latch

În continuare sunt prezentate două exemple de circuite secvenţiale, şi anume un element


de memorare - flip-flop de tip D cu reset sincron, respectiv un flip-flop de tip D cu reset
asincron.

În Verilog HDL lista de sensitivităţi aferentă unei construcţii always nu poate conţine
atât semnale care sunt active pe front cât şi semnale active pe palier, ele sunt active fie
pe front, fie pe palier.

Copyright @ Oana Boncalo & Alexandru Amaricai


a)

b)
Figura 13: Exemplu de logică secvenţială – descriere bistabilului de tip D cu reset - a)
sincron b) asincron

Foarte mare atenţie trebuie acordată diferenţei dintre atribuirea de tip blocking şi cea de
tip non-blocking. În acest sens este prezentat un exemplu simplu cu două flip-flop-uri de
tip, pentru care este prezentat rezultatul obţinut în urma sintezei.

Copyright @ Oana Boncalo & Alexandru Amaricai


Pentru atribuirea de tip non-blocking, comportamentul pentru codul prezentat mai jos este
următorul – valorile de la intrarea în blocul always pentru semnalele a, b, c, f sunt folosite
în toate evaluările. Atribuirile pentru noi valori calculate (pentru f şi g în acest exemplu)
sunt întârziate până la fineşe blocului always.

Figura 14: Exemplu de logică secvenţială – atribuirea de tip non-blocking. Rezultatul


sintezei şi codul Verilog HDL

Pentru atribuirea de tip blocking, comportamentul pentru codul prezentat mai jos este
următorul – valorile folosite pentru toate evaluările sunt cele calculate în interiorul
blocului, şi pot fi diferite de cele de la intrarea în blocul always. De asemenea, atribuirile
se fac in-order şi valoarea nou calculată este actualizată imediat. În exemplul de mai jos,
valoarea pentru g este calculată folosind noua valoare a lui f.

În Verilog HDL atribuirile de tip blocking (folosesc simbolul =) din interiorul


construcţiilor de tip always, se pretează pentru descrierea logicii combinaţionale pe
mai multe niveluri.

Copyright @ Oana Boncalo & Alexandru Amaricai


Figura 15: Exemplu de logică secvenţială – atribuirea de tip blocking (acelaşi cod ca în
exemplul anterior, singura diferenţă fiind atribuirea care este de tip blocking). Rezultatul
sintezei şi codul Verilog HDL

Ultimele două exemple vin să confirme diferenţele majore care pot să apară la rezultatele
de sinteză, ca urmare a unor modificări aparent minore ale sintaxei.

Copyright @ Oana Boncalo & Alexandru Amaricai


2.2. Tipuri de date

2.2.1. Constante

Setul de valori suportat de Verilog HDL cuprinde:

Valoare Comentariu
Verilog HDL
0 0 logic sau FALS
1 1 logic sau ADEVĂRAT
X sau x necunoscută
Z sau z impedanţă ridicată
Tabel 1: Set de valori Verilog HDL

Aceste valori sunt codificate în limbajul Verilog HDL.

Constantele suportate de limbaj sunt:


• Constante întregi;
• Constante reale;
• Constante şiruri;

Simbolul “_” din interiorul unei constante reale sau întregi este ignorat. El se foloseşte de
regulă pentru îmbunătăţirea lizibilităţii.

Constantele întregi pot fi exprimate în două formate:


• simplu

-32
15

• bază de numeraţie

Copyright @ Oana Boncalo & Alexandru Amaricai


Ex.:

⎧ 2 ' b11
Constanta 3 exprimată ⎨ - în binar pe 2 biţi
⎩ 2 ' b11
⎧ 9 ' so72
⎪12 ' so772

Constanta -6 exprimată ⎨ -în octal pe 9, respectiv 12 biţi
⎪ 12 ' so 7 _ 72
⎪⎩12 ' so72

⎧ 3' d − 7

Incorect exprimate ⎨3' so772
⎪(1 + 2) ' so7 _ 72

⎧3' oz
Şir extins de ⎨ echivalent cu zzz, respectiv xxx
⎩3' bx

În situaţia în care valoarea constantei depăşeşte numărul de biţi alocaţi, aceasta va fi


trunchiată începând cu partea stângă, prin renunţarea la bitul de semn dacă este cazul. De
asemenea valoarea constantelor negative este exprimată în complement de 2.

Constantele reale prezintă:


• notaţie zecimală
2.5
5.678
-15.4
45. - greşită

• notaţie ştiinţifică

2_35.1e1 –valoarea 2351


235.1e1 - valoarea 2351
5e-2 - valoarea 0.05

Constantele de tip şiruri sunt definite ca secvenţe de caractere între ghilimele duble. Nu
pot fi scrise pe mai multe rânduri. Pot conţine caractere speciale definite cu ajutorul lui
backslash:

Copyright @ Oana Boncalo & Alexandru Amaricai


Caracter Comentariu
special
\n newline
\t Tab
\” Caracterul „
\\ Caracterul \

”Constanta SIR”

2.2.2. Tipuri net

Tipul net este folosit pentru a descrie o conexiune fizică între mai multe elemente de
structură (componente ale unui design). Valoarea net-ului este determinată de sursele
pentru semnalul pe care îl transportă. Valoarea default în cazul în acre nu există o sursă
este z.

Există mai multe tipuri de net-uri, care în ultima variantă a standardului au fost multe
dintre ele echivalate:

• Wire şi tri
Wire este cel mai folosit în design-urile Verilog HDL. Tradiţional s-a folosit
pentru reprezentarea conexiunilor cu o singură sursă de semnal (signal driver).
Ulterior a devenit identic ca şi semnificaţie cu tri – semnal multisursă (mai multe
declaraţii de tip assign atribuie valoarea aceluiaşi semnal). Valoarea semnalului în
acest caz este decisă cu ajutorul tabelului.

wire sau tri 0 1 x z


0 0 x x 0
1 x 1 x 1
x x x x x
z 0 1 x z

• Wor şi trior
Wor sau trior este echivalentul unui or-cablat între toate sursele unui semnal.
Comportamentul este descris în tabelul ce urmează.

wor sau trior 0 1 x z


0 0 1 x 0
1 1 1 x 1
x x 1 x x
z 0 1 x z

Copyright @ Oana Boncalo & Alexandru Amaricai


• Wand şi triand
Wand sau triand este echivalentul unui şi-cablat între toate sursele unui semnal.
Comportamentul este similar cu cel al lui wor (sau trior), doar că funcţia logică este
de data aceasta ŞI logic.

• Trireg
Modelează noduri capacitive – când toate sursele de semnal sunt impedanţă ridicată,
net-ul reţine ultima valoare.

• Tri0 şi tri1
Sunt folosite pentru a modela funcţii logice cablate, având un tabel de funţionare
asemănător cu cel prezentat pentru wire (sau tri). Diferenţa survine pentru situaţia în
care semnalul sursă nu are driver pe net, caz în care valoarea este 0 logic (pentru tri0),
respectiv 1 logic (pentru tri1).

• Supply0 şi supply1
Supply0 este folosit pentru a modela legătura la masă, iar supply1 modelează legătura
la alimentare.

• Net-uri nedeclarate
Verilog HDL permite nedeclararea tipului de net, situaţie în care în mod automat
(dacă nu se specifică explicit altceva), ea este de tip wire pe un singur bit. Schimbarea
acestui default, se face prin directiva de compilator default_nettype:

`default_nettype tipul_netului //poate fi wand, wor, …

Sau se poate cere specificarea în mod obligatoriu a tipului de net.

`default_nettype none

• Vectored şi scalar nets


Dacă nu se specifică un net este de tip scalar. Dacă este declarat folosind cuvântul
cheie vectored atunci nu pot fi accesate sub-părţi ale vectorului (part select), şi nici nu
se pot face atribuiri la componentele individuale/sub-părţi.

2.2.3. Tipuri variabilă

Variabilele sunt de mai multe tipuri:


• Reg;
• Integer;
• Time;

Copyright @ Oana Boncalo & Alexandru Amaricai


• Real;
• Realtime;

Pot fi iniţializate la declarare, prin atribuirea unei constante.

Variabile de tip reg

Sunt cele mai frecvent folosite, şi se declară astfel:

O variabilă neiniţializată de tip reg are valoarea x.


Exemple de astfel de declaraţii:

Ex.:
reg [3:0] counter_reg, buf_reg; //ieşirea unui numărator, respectiv registru
reg test0; //variabilă pe 1 bit
2.2.4. Diferenţa între reg şi wire
parameter MSB=31, LSB=1; // declararea de parametri în cadrul
//modulului
reg signed [MSB:LSB] adr_bus;
reg signed [MSB:LSB] operand1,operand2;

Un reg reprezintă o variabilă care poate revendica unul sau mai mulţi biţi. Nu trebuie
confundat şirul de biţi cu un array de biţi care reprezintă o memorie.

În Verilog HDL o memorie este reprezentată printr-un vector (array) de variabile de


tip reg.

Copyright @ Oana Boncalo & Alexandru Amaricai


Se declară astfel:
reg [[MSB:LSB]] memory1[ADR_MAX1:ADR_MIN1],
memory2[ADR_MAX2:ADR_MIN2], … ;

Un exemplu de memorie care are 128 de cuvinte pe 32 de biţi este:

parameter ADR_SIZE = 32, WORD_SIZE = 128;


reg [ADR_SIZE-1:0] ram_memory [WORD_SIZE-1:0], data_reg;
//ram_memory este un array de 128 variabile de tip reg pe 32 de biti
//data_reg este o variabila de tip reg pe 32 de biti
//putem adresa variabila reg la nivel de element, sub-şir de biţi
data_reg[0] sau data_reg[2:0] sunt corecte
//şi putem să-i atribuim o valoare
assign data_reg=32’bx; //
//nu şi la o memorie
ram_memory[2:0], ram_memory1=ram_memory2 nu sunt corecte
//atribuirea se face element cu element
ram_memory1[0]=ram_memory2[0]; ….

Se pot folosi task-urile de sistem pentru a iniţializa o memorie.

$readmemb(”ram.dat”, ram_memory); //încarcă date sub formă de numere binare


//dintr-un fişier text în memorie ignorând spaţiile albe şi comentariile
Ex.:
Reg [7:0] rom_mem [3:0];
$readmemb (“rom.dat”,rom_mem);

rom.dat conţine
11011111
11000000
10101010
01010101

$readmemh - pentru valori hexazecimale

Variabile întregi

Acestea sunt uzual folosite pentru a realiza descrieri de nivel înalt folosind Verilog HDL.
Sintaxa pentru declararea unei variabile întreg este:

integer intreg1, intreg2, …, intregn[MSB:LSB];


//msb, lsb specifică indexii unui array de întregi

Copyright @ Oana Boncalo & Alexandru Amaricai


În Verilog HDL o variabilă întreg este asimilată cu o variabilă reg reprezentată pe 32
de biţi.

În baza notei de mai sus, următoarele atribuiri (care sunt însoţite de conversii implicite)
sunt corecte:

integer i;
reg [7:0] reg1;

i=7;
reg1=i; // reg1 ia valoarea 0000…0111 exprimată pe 32 de biţi
//conversia se realizează implicit

Variabile real şi realtime

O variabilă real este identică cu o variabilă de tip realtime. Sintaxa pentru declararea ei
este:
real real1, real2, …, realn[MSB:LSB];
//msb, lsb specifică indexii unui array de numere reale

Valoarea default pentru variabila real este 0. De asemenea daca i se atribuie unei
variabile real un şir de biţi care conţine x şi z, aceştia din urmă vor fi transformaţi în 0
logic.

2.2.5. Semnale pe mai mulţi biţi

Verilog HDL oferă suport pentru lucrul cu semnale pe mai mulţi biţi. Astfel două din
facilităţile cele mai importante vizează operaţia de concatenare, precum şi adresarea unor
sub-şiruri binare. Ele sunt prezentate prin două atribuiri continue în exemplul următor:
assign {a[7:0],a[15:8]}={a[15:8],a[7:0]};
assign {cout,s}=op1+op2+cin;
//rezulatatul este extins la nr. de biţi din partea stângă

2.3. Operatori Verilog HDL

Operatorii Verilog HDL se pot clasifica astfel: operatori aritmetici, operatori relaţionali,
operatori bit-cu-bit (bit-wise), operatori logici, operatori de reducere, operatori de
comparare, operatori de şiftare, operatori de concatenare şi replicare, şi operatorul

Copyright @ Oana Boncalo & Alexandru Amaricai


condiţional. Ei vor fi prezentat succint în cele ce urmează, punctându-se printr-un
exemplu.

Operator Comentariu

Aritmetici

+ (-) Adunare (scădere)


* Înmulţire
/ Împărţire
% Modulo
** Ridicare la putere

Shiftare

<< Shiftare logică la stânga


>> Shiftare logică la dreapta
<<< Shiftare aritmetică la stânga
>>> Shiftare aritmetică la dreapta (se
introduce în locul biţilor shiftaţi
semnul numărului)

Logici

&& Şi logic
|| Sau logic
! Negare logică

Bit cu bit

~ Negare la nivel de bit (not).


& Şi la nivel de bit.
| Sau la nivel de bit.
^ Xor la nivel de bit
~^ sau ^~ Xnor la nivel de bit.

De reducere

~& Şi-nu aplicabil la un şir de biţi.


& Şi aplicat la un şir de biţi.
| Sau aplicat la un şir de biţi.

Copyright @ Oana Boncalo & Alexandru Amaricai


Relaţionali

> Mai mare.


< Mai mic.
>= Mai mare sau egal.
<= Mai mic sau egal.

De egalitate

== Egalitate logică. Dacă apare x sau


y la unul dintre operanzi rezultatul
egalităţii este necunoscut (x).
!= Inegalitate logică (diferit).
=== Egalitate. Compară ad-literam fără
să ţină cont de valori speciale.
Tratamentul pentru y sau x este
acelaşi ca pentru 0 sau 1.
!== Inegalitate.

În cele ce urmează câteva exemple de utilizare a operatorilor Verilog HDL.

Ex.:

my_reg1=8`b00101100;
my_reg2=8`b1111110;

| my_reg1 // este 1
& my_reg2 //este 0

my_reg1 & my_reg2 //este 00101100

my_reg2 <= my_reg1 //este True

my_reg1 >> 2 //este 00001011


my_reg2>>>2 //este 11111111 nr. fiind cu semn

my_r1=4`b11xz;
my_r2=4`b11xz;

my_r1 === my_r2 //este true


my_r1 == my_r2 //este x (undefined)

Copyright @ Oana Boncalo & Alexandru Amaricai


2.4. Clauza initial

Are un comportament asemănător cu clauza always, cu două diferenţe importante:


• o construcţie de tip initial se execută o singură dată;
• execuţia ei are loc la momentul 0 al simulării;

Sintaxa este de forma:

initial
[control_timp] construcţie_procedurală

De regulă aceasta este folosită pentru a realiza o iniţializare, sau generarea unor forme de
undă în cadrul unor entităţi de test.

Ex.:

//generarea unei forme de undă


reg semnalul_meu;

initial
begin
semnalul_meu=1`b1;
#10 semnalul _meu=1`b0 ;
#15 semnalul _meu=1`b1 ;
end

Forma de undă generată este de forma:

Figura : Formă de undă pentru variabila reg semnalul_meu

Un exemplu referitor la folosirea construcţiei initial este oferit în secţiunea 3.2 – sub
forma unui testbench.

Copyright @ Oana Boncalo & Alexandru Amaricai


2.5. Instrucţiuni procedurale

Acestea sunt utilizate pentru descrierile comportamentale în Verilog HDL. În continuare


sunt prezentate instrucţiunile decizionale (case, if), cele repetitive (loop), precum şi cele
bloc (blocuri de tip begin…end).

2.5.1. Instrucţiuni decizionale

Verilog HDL prezintă patru tipuri de instrucţiuni decizionale:


• if
• case

Pentru instrucţiunea if sintaxa este de forma:

if (condiţie_1)
construcţie_procedurală_1
[else
construcţie_procedurală_2]

Dacă condiţie_1 este evaluată la valoarea 1, atunci este executată construcţia_procedu-


rală_1, în caz contrar dacă condiţie_1 este evaluată la una din valorile 0, z sau x atunci se
execută ramura de else (dacă ea există).
if (count_reg < 16)
begin
rdy=1`b1;
count_reg=count_reg+ 4`b0001;
end
else
begin
rdy=1`b0;
count_reg=4`b0000;
end

Pentru instrucţiunea case sintaxa este de forma:


case (expresie_case)

case_item_1 [, case_item_2, …] : construcţie_procedurală_1



case_item_m [, case_item_m+1, …] : construcţie_procedurală_m

[default: construcţie_procedurală]

endcase

Copyright @ Oana Boncalo & Alexandru Amaricai


case (expresie_case)

case_item_1 [, case_item_2, …] : construcţie_procedurală_1



case_item_m [, case_item_m+1, …] : construcţie_procedurală_m

[default: construcţie_procedurală]

endcase

Se recomandă folosirea instrucţiunii case, pentru situaţia în care este modelat un


comportament care revendică decizie multiplă în defavoarea unei construcţii stufoase de
tip if.

module ALU_unit
(input wire [31:0] op_a,
input wire [31:0] op_b,
input wire [3:0] opcode_instr,
output reg rezultat);

//declaraţie parametri locali modulului


localparam
ADD_INSTR=4`b1001,
SUB_INSTR=4`b1101,
MUL_INSTR=4`b1011,
DIV_INSTR=4`b1111;

always @(op_a or op_b or opcode_instr)


Case (opcode_instr)
ADD_INSTR: rezultat<=op_a + op_b;
SUB_INSTR: rezultat<=op_a - op_b;
MUL_INSTR:
Foarte importante sunt rezultat<=op_a
considerentele *legate
op_b; de sinteză - vezi exemplele cu logica
DIV_INSTR: rezultat<=op_a /op_b;
prioritară, precum şi cele legate de logica combinaţională care este sintetizată cu
elementedefault: rezultat<=rezultat;
de memorare de la secţiunea 2.2. // NOP –ieşirea rămâne nemodificată
endcase

endmodule

Se recomandă urmărirea menţiunilor de la secţiunea 2.2 cu privire la sinteză (generarea


de logică prioritară, respectiv introducerea de elemente de memorare de tip latch).

Copyright @ Oana Boncalo & Alexandru Amaricai


2.5.2. Instrucţiuni repetitive

Verilog HDL prezintă patru tipuri de instrucţiuni repetitive:


• forever-loop
• repeat-loop
• while-loop
• for-loop

Forever-loop execută secvenţa de cod la infinit. Din aceste considerente este necesar un
fel de control de timp (de exemplu introducerea unei întârzieri). Pentru instrucţiunea
forever sintaxa este de forma:

forever
construcţie_procedurală

Acesta poate fi folosit pentru generarea unui semnal de tact cu perioada de 10 unităţi de
timp.

initial
begin
semnal_tact=1`b0;

#15 forever //semnalul este negat la fiecare 5 unităţi de timp


#5 semnal_tact=~ semnal_tact;
end

Forma de undă generată este:

Figura 15: Formă de undă pentru semnal_tact descris anterior

Repeat-loop execută secvenţa de cod de un număr specificat de ori. Dacă contorul este x
sau z el este interpretat ca 0. Pentru instrucţiunea repeat sintaxa este de forma:

repeat (contor_nr_iteraţii)
construcţie_procedurală

Copyright @ Oana Boncalo & Alexandru Amaricai


repeat (contor)
sum= sum+1; //adună 1 de contor ori

A nu se confunda cu repeat care este folosit pentru controlul evenimentelor de tipul:

repeat (contor)
@ (posedge tact) sum= sum+1;
//aşteaptă contor fronturi crescătoare ale semnalului de tact
//pe frontul crescător al tactului incrementează sum

While-loop execută secvenţa de cod cât timp este indeplinită condiţia. Dacă contorul este
x sau z el este interpretat ca 0 (FALSE). Pentru instrucţiunea while sintaxa este de forma:

while (condiţie)
construcţie_procedurală

În următorul exemplu se realizează shiftarea registrului acumulator.

while (nr_poz > 0)


begin
acc=acc<<1;
nr_pos=nr_pos+1;
end

For-loop execută secvenţa de cod un număr finit de ori. Pentru instrucţiunea for sintaxa
este de forma:
for (atribuire_iniţializare ; condiţie ; adunare_pas)
construcţie_procedurală

Exemplu pentru anterior în contextul instrucţiunii for este următorul:

integer i;
for (i=0; i<nr_pos; i=i+1)
acc=acc<<1;

2.5.3. Instrucţiuni de tip bloc secvenţial

Copyright @ Oana Boncalo & Alexandru Amaricai


Un bloc secvenţia grupează mai multe instrucţiuni printr-o construcţie de tip begin …
end. Instrucţiunile sunt executate în ordinea în care sunt scrise, iar eventualele întârzieri
de la instrucţiunea i, se raportează la timpul de simulare al instrucţiunii anterioare.

Un bloc poate primi o etichetă, caz in care el poate conţine declaraţii de variabile.
Acestea din urmă sunt statice (au valori valide pe tot parcursul simulării).

Sintaxa pentru un bloc secvenţial este:

begin
[:identificator_bloc [declaraţii_locale_bloc]]
instrucţiuni_procedurale
end

Un exemplu de cod care generează un semnal:

begin
out1= x & y;
//se execută după atribuirea anterioară, pe frontul crescător al semnalului de tact
@ (posedge semnal_tact)
out2=x | y;
end

2.6. Alte tipuri de instrucţiuni

Foarte utile pentru design-urile care necesită replicarea unor subcomponente sunt
instrucţiunile de tip generate. Acestea permit selecţia sau replicarea unor secvenţe de cod
în faza premergătoare simulării (faza de elaborare / elaboration time) în care toate
modulele din design sunt conectate şi referinţele ierarhice sunt soluţionate. Un bloc
generate este înrămat de cuvintele cheie generate … endgenerate.

Trei tipuri de construcţii sunt posibile:


• Generate-loop
• Generate-case
• Generate-condiţionat

Sintaxa pentru blocul generate este următoarea:


generate
//instrucţii generate-loop
//instrucţii genetare-case
//instrucţii generate-condiţionale
//instrucţii generate încuibate
endgenerate

Copyright @ Oana Boncalo & Alexandru Amaricai


Generate-loop este folosit pentru replicare de cod în faza de elaborare a ierarhiei de
module în vederea simulării. Este necesară declararea unei variabile de tip genvar care să
fie folosită drept contor al for-ului din generate-loop.

În continuare un exemplu pentru o poartă xor word-gate.

module xor_w
( input wire [7:0] in1,in2,
output wire [7:0] x
);

//declararea variabilei pentru generate-loop


genvar i;

//generate-loop-ul
generate
for (i=0; i<8; i=i+1)
begin: multiplicare_xor
xor my_xor (x[i], in1[i],in2[i]);
end
endgenerate

endmodule

În faza de elaborare a ierarhiei de module corpul for-ului este replicat pentru fiecare
iteraţie, astfel se obţine:

xor multiplicare_xor[0].my_xor (x[0], in1[0],in2[0]);


xor multiplicare_xor[1].my_xor (x[1], in1[1],in2[1]);
xor multiplicare_xor[2].my_xor (x[2], in1[2],in2[2]);
………
xor multiplicare_xor[7].my_xor (x[7], in1[7],in2[7]);

Trebuie remarcată obligativitatea etichetei de bloc multiplicare_xor, necesară pentru


referenţierea fiecărei instanţe de xor locală generate-loop-ului.

Există şi posibilitatea realizării unei selecţii ăn faza de elaborare, prin testarea unei
condiţii statice (depinde exclusiv de evaluarea unor constante şi/sau parametri). Similar
se poate folosi şi o construcţie de tip case pentru a realiza o selecţie condiţionată
multiplă.

Dacă modificăm exemplul anterior, astfel încât primii 4 biţi sunt rezultatul unui xor, iar
următorii ai unui and logic, codul Verilog HDL se modifică astfel :

Copyright @ Oana Boncalo & Alexandru Amaricai


………
genvar i;
generate
for (i=0; i<8; i=i+1)
begin: multiplicare_xor
if (i<4)
xor my_xor (x[i], in1[i],in2[i]);
else
and my_and (x[i], in1[i],in2[i]);
end
endgenerate
………

2.7. Funcţii şi task-uri Verilog HDL

Verilog HDL permite împachetarea codului care se repetă în task-uri şi funcţii.

Task-urile prezintă următoarele caracteristici:


• pot avea zero, unul sau mai multe argumente, care se transmit prin valoare;
• în interiorul acestora pot exista instrucţiuni de control a timpului de tip întârziere
(delay);
• poate apela alte task-uri şi/sau funcţii;
• seamănă ca şi comportament cu procedurile;

Sintaxa pentru un task este următoarea:

task [automatic] identificator_task;


[declararaţii_argumente_de_intrare]
[declaraţii_variabile_locale]

instrucţiuni_procedurale;

endtask

Trebuie menţionat că un task poate accesa orice variabilă/semnal al modulului în cadrul


căruia a fost definit.

Foarte important este cuvântul cheie automatic. Dacă acesta este prezent, variabilele
locale task-ului sunt alocate dinamic (fiecare apel de task are propriul set de variabile
locale). În caz contrar, ele sunt statice. Trebuie ţinut cont de caracterul concurent al
execuţiei componentelor/blocurilor în Verilog HDL. Acest fapt, poate conduce la situaţia
în care un task este concurent din mai multe părţi ale codului. Fiecare task beneficiază de

Copyright @ Oana Boncalo & Alexandru Amaricai


propriul control în situaţia execuţiei paralele. Dacă este static, toate aceste apeluri
folosesc aceleaşi variabile locale.
Apelul unui task se realizează astfel:
identificator_task [(expresie_1, expresie_2, …, expresie_n)];

Funcţiile Verilog HDL se declară în cadrul unui modul şi pot fi apelate din diferite părţi
ale codului. Prezintă următoarele deosebiri faţă de anterior prezentatele task-uri:
• pot returna o valoare, de tip real, integer, time, real şi realtime;
• nu pot conţine întârzieri;
• pot apela alte funcţii, dar nu pot apela alte task-uri;
• trebuie să aibă cel puţin un argument;

Sintaxa pentru o funcţie este următoarea:


function [automatic] [signed] [range_of_type] identificator_funcţie;
[declararaţii_argumente_de_intrare]
[declaraţii_variabile_locale]

instrucţiuni_procedurale;

endfunction

Cuvântul cheie automatic are aceeaşi semnificaţie ca şi în cazul task-urilor. De asemenea


o funcţie declarată cu automatic poate fi recursivă.
Un exemplu de funcţie care calculează paritatea este prezentat în cele ce urmează.

module exemplu
( input wire [7:0] my_byte,
output wire [8:0] paritate_ext_byte);
function automatic paritate;
input [7:0] data; //input declaration
reg p_bit; //local declaration
2.8.integer i; cu fişiere
Lucrul //local declaration
begin //begin … end block statement

p_bit=1’b0;
for (i=0; i<8; i=i+1)
if (data[i])
p_bit=p_bit ^ data[i];
paritate=p_bit;
end
endfunction
assign paritate_ext_byte={my_byte, paritate(my_byte)};
endmodule

Copyright @ Oana Boncalo & Alexandru Amaricai


2.9. Task-urile şi funcţiile sistem

Verilog HDL pune la dispoziţia programatorului o sumedenie de task-uri şi funcţii


sistem. În cele ce urmeză vom prezenta câteva dintre cele mai uzitate task-uri şi funcţii
predefinite în limbaj.

• Pentru afişare la ieşirea standard: $display, $write.


Formatul de afişare :
‰ %h – hexazecimal;
‰ %d – zecimal;
‰ %o – octal;
‰ %b – binar;
‰ %c – ascii;
‰ %t – timp;
‰ %m – numele din cadrul ierarhiei de module;
‰ %s – şir de caractere;
‰ %u – binar, cu 2 valori binare ;
‰ %z – binar, cu 4 valori binare ;
‰
$display (“Timpul de simulare: %t”, $time);

În exemplul anterior este folosită funcţia sistem $time, care returnează timpul de
simulare.

• Pentru monotorizare:
$monitor – monotorizează lista de argumente continuu şi afişează mesajul
redactat la sfârşitul pasului de timp în care unul sau mai multe semnale din listă
sau modificat. Prin sfârşitul pasului de timp se înţelege momentul de timp la care
toate evenimentele aferente pasului respectiv s-au finalizat.
initial
$monitor (“Timp: %t, semnal tact: %b valoare ieşire: %b”, $time,clk,out1);

Alte task-uri înrudite:


$monitoron – activează monitorul
$monitoroff – dezactivează monitorul

Copyright @ Oana Boncalo & Alexandru Amaricai


• Pentru intrare/ieşire:

Pentru deschiderea respectiv închiderea fişierelor:

$fopen – deschide un fişier şi returnează o valoare de tip întreg care este handler-
ul fişierului respectiv ;

integer handler_fişier=$fopen(nume_fişier, mod);

$fclose – închide un fişier;


$fclose(handler_fişier);

Un fişier poate fi deschis pentru: citire (mod este “r”, “rb”) , scriere (mod este
“w”, “wb”), adăugare (mod este “a”, “ab”), scriere şi citire (mod este “w+”,
“w+b”, “wb+”, “r+”, “r+b”, “rb+”), adăugare şi citire (“a+”, “a+b”, “ab+”).
“b” se referă la fişiere binare.

Pentru scriere într-un fişier:

$fdisplay, $fwrite, $fstrobe, $fmonitor, $fflush


Lista de argumente pentru aceste task-uri este de forma:

$nume_task (handler _fişier,“mesaj şi specificatori format”, listă_argumente);

Exemplu scriere în fişierul fişier.dat:


integer h_fisier;
initial
begin
h_fisier=fopen(“fisier.dat”, “w”) ;
//… alte instr.
$fwrite(h_fisier, “semnalul are valoare %b”, semnalul _meu);
$fclose(h_fisier);
end

Pentru citire dintr-un fişier:


Două task-uri sistem sunt folosite pentru a citi datele într-o memorie.

Copyright @ Oana Boncalo & Alexandru Amaricai


$readmemb - încarcă conţinutul unui fişier cu valori reprezentate în binar într-o
memorie (secţiunea 2.2.3 oferă o descriere a unei memorii Verilog HDL).
$readmemh – încarcă conţinutul unui fişier cu valori reprezentate în hexazecimal
într-o memorie (secţiunea 2.2.3 oferă o descriere a unei memorii Verilog HDL).
Fişierul text conţine numere (binare sau hexazecimale) separate prin spaţii albe,
cu eventuale comentarii. Prima dată se încarcă valori la adresa cea mai din stânga
a indicilor memoriei. Opţional se poate specifica un interval de adrese (de la
adresa adr1, la adresa adr2, adr1 şi adr2 sunt valori care se încadrează în plaja de
indexi a memoriei) care sunt încărcate în memorie. În acest caz primul număr citit
din fişier este încărcat la prima adresă dată (adr1). Sintaxa este de tipul:

$readmemb (“nume_fişier”,nume_memorie,adr1,adr2);

Alte task-uri sistem pentru citirea din fisier:


$fread – citeşte informaţie binară din fişier;
$fgets – citeşte o linie din fişier;
$unget – inserează caracter înapoi în fişier;
$frewind – poziţionează cursor la începutul fişierului;
$fseek – poziţionează cursor la poziţia indicată de offset;
$fscanf, $ftell, $ferror

• Pentru controlul simulării:

$finish – determină terminarea simulării;


$stop – determină suspendarea simulării;

Pentru determinarea timpului de simulare exprimat ca un multiplu de unitatea de timp


a modului:
$time – exprimată ca întreg pe 64 de biţi;
$stime – exprimată ca întreg pe 32 de biţi;
$realtime – exprimată ca număr real;

• De conversie şi formatare:

Conversie:
$rtoi(număr_real) – converteşte un număr real într-un întreg prin trunchiere;

Copyright @ Oana Boncalo & Alexandru Amaricai


$itor(număr_întreg) – converteşte un număr întreg într-un număr real;
$realtobits(număr_real) – reprezentarea pe 64 de biţi în format IEEE 754 de simplă
precizie;
$bitstoreal(şir_biţi) – inversul lui realtobits;
$signed(valoare) – interpretarea valorii ca număr cu semn;
$usigned(valoare) – interpretarea valorii ca număr fără semn;

Formatare şir: $swrite, $sformat, $sscanf

• Altele:

$random, $printtimescale, $timeformat, …

3. Verificarea şi simularea unui design Verilog HDL

3.1. Modelarea întârzierilor

Există mai multe modalităţi de specificare a întârzierilor în Verilog HDL, ele sunt
exemplificate în cele ce urmează:
‰ întârziere prin declaraţia semnalului

wire #15 semnal_întârziat;

Indică faptul că orice modificare a driver-ului semnalului se produce cu o întârziere de 15


unităţi de timp.
‰ întârziere prin atribuirea continuă

assign #15 semnal_întârziat = a & b;

Indică faptul că orice modificare a driver-ului semnalului (în acest caz expresia din partea
dreaptă care este evaluată continuu) este întârziată cu 15 unităţi de timp.
‰ control de tip întârziere pentru descrierea comportamentală

#întârziere instrucţiune _procedurală

Semnificaţia este: aşteaptă întârziere unităţi de tip înainte de a executa instrucţiunea.

Copyright @ Oana Boncalo & Alexandru Amaricai


Dacă întârzierea este calculată într-o expresie atunci aceasta trebuie scrisă între paranteze
rotunde.

#(2 * STALL) ; //aşteaptă 2 * STALL unităţi de timp


#(2+3) out1= a ^ b ^ cin ; //instrucţiunea este executată după ce se introduce în
//o întârziere de 5 unităţi de timp

‰ control de tip întârzierea din cadrul instrucţiunii (intra-statement delay)

out1= #(2+3) a ^ b ^ cin ; //expresia din dreapta este evaluată, apoi este introdusă
//întârzierea, urmată apoi de atribuirea propriuzisă
Comportamentul este identic cu:

begin
aux= a ^ b ^ cin ;
#(2+3) out1=aux ;
end

De asemenea există o formă de control al întârzierilor pentru evenimente care se repetă.

repeat (expresie) @ (expresia_aferentă_evenimentului)

Un exemplu în acest sens:

rezultat= repeat (4) @ (posedge tact) a+b;

Comportamentul este următorul:


• evaluează expresie din dreapta;
• aşteaptă 4 fronturi crescătoare;
• atribuie valoarea calculată;
Din considerente de sincronizare sunt utile instrucţiunile de tipul:
@ (posedge tact);

3.2. Modulul de test (testbench)

Un testbench este în esenţă un modul folosit pentru verificarea funcţionării corecte a unui
design. Acesta are trei sarcini mai importante:
• generarea sau încărcarea dintr-un fişier a stimulilor de test (valorilor de intrare
pentru verificare);

Copyright @ Oana Boncalo & Alexandru Amaricai


• Aplicarea stimulilor modulului care este testat şi preluarea răspunsurilor de la
acesta;
• Compararea răspunsurilor cu valorile corecte sau salvarea acestora în vederea
prelucrării ulterioare.

Generarea stimulilor

Generarea unei secvenţe de valori de intrare poate fi realizată cu ajutorul unei construcţii
de tip initial.
initial
begin
//prima variantă de stimuli pentru a testa o celulă de însumare
a=1’b0;
b=1’b1;
cin=1’b1;
//după 20 unităţi de timp modifică valoarea lui a
#20 a=1’b1;
//după 20 unităţi de timp modifică valoarea lui cin şi a lui b
#20 cin=1’b0;
b=1’b0;
end
Pattern-urile repetitive se pot obţine prin iniţializare, urmată de o atribuire continuă, sau o
construcţie de tip always.
//initializare
initial
tact=1’b0;

//modificare continua
assign #(PERIOADA/2) tact=~tact; // PERIOADA- constanta sau parametru modul

Sau:

//initializare
initial
tact=1’b0;

//modificare continua
always
#(PERIOADA/2) tact=~tact; // PERIOADA- constanta sau parametru modul

Copyright @ Oana Boncalo & Alexandru Amaricai


Pentru situaţiile în care numărul de stimuli este mare şi este baleiat un număr consistent
de cazuri de test, iar testele trebuie refăcute, este indicat ca aceştia să fie salvaţi şi mai
apoi citiţi din fişier. Exemplul anterior cu celula FAC este reluat pentru această situaţie:
module test_FAC;
parameter BITI=5, TEST_NR=3;

//variabilă de memorie pentru stocarea stimulilor


//dimensiunea mem. = nr de cazuri de test
//dimensiune cuvânt= nr biţi pentru simulii aferenţi unui caz de test
reg [BITI-1:0] memorie [0: TEST_NR-1];

//conexiunile dintre blocul de generare stimuli şi instanţa de celulă de însumare FAC


reg a,b,cin ;
wire s,cout ;
//valorile care sunt aşteptate
reg s_c,cout_c;

//variabilă pentru for-ul din blocul initial


integer j;

//generare stimuli

initial
begin
//citeşte stimuli în memorie
$readmemb(“mem.dat”,memorie);

//aplică stimuli, aşteaptă un interval răspunsul şi verifică rezultat


for (j=0; j< TEST_NR; j=j+1)
begin
//aplică stimuli
{a,b,cin, s_c, cout_c}=memorie[j] ;
// aşteaptă un interval răspunsul de la celula de însumare
#20;
// verifică rezultat
if ((s!==s_c) || (cout!==cout_c))
$display(”Eroare- suma: %b, cout: %b la vectorul %b”,s,cout,memorie[j]);
end
end

//instanţă circuit testat

adder_cell_struct uadder_cell(a,b,cin,s,cout);

endmodule

Copyright @ Oana Boncalo & Alexandru Amaricai


Fişierul mem.dat conţine următoarele informaţii:
00 0 0 0
01 0 1 0
01 1 0 1
10 1 0 1
1N 1N 1N 1N 1N
a b cin s cout
Pentru design-uri care au răspunsuri greu de verificat, sau care necesită o analiză
ulterioară, acestea pot fi salvate în fişier.

4. Bibliografie
1. IEEE Standard Verilog Hardware Description Language – IEEE Std 1364-2001
2. S. Kilts – Advanced FPGA Design: Architecture, Implementation and
Optimization – John Willey&Sons, 2007
3. D. Thomas, P. Moorby – The Verilog Hardware Description Language, Fifth
Edition – Kluver Academics, 2002
4. J. Bhasker – A Verilog HDL Primer, Third Edition – Star Galaxy Press, 2005
5. S. Brown, Z. Vranesic – Fundemantels of Digital Logic with Verilog Design –
McGraw-Hill, 2007
6. MIT Introductory Digital Systems Laboratory -
http://ocw.mit.edu/OcwWeb/Electrical-Engineering-and-Computer-Science/6-
111Spring-2006/CourseHome/index.htm

Copyright @ Oana Boncalo & Alexandru Amaricai

S-ar putea să vă placă și