Realizarea Unui Program de Simulare Circuite Descrise In Vhdl
1. Descrierea proiectului
Prezentare generala
Proiectul consta in realizarea unui program care sa simuleze circuite descrise in VHDL.
VHDL (prescurtare de la Vhsic (Very High Speed Integrated Circuits) HDL (Hardware Description Language)) este un limbaj de programare care descrie structuri hardware. (de exemplu un circuit integrat care trebuie construit este descris in acest limbaj de programare, sursa este prelucrata, optimizata prin programe CAD, circuitul transformat in operatii elementare si apoi construit fizic)
VHDL este diferit fata de celelalte limbaje obisnuite de programare – un circuit este exprimat ca a o colectie de subcircuite care opereaza in paralel, variabilele sunt semnale electrice, operatiile descriu unitati functionale. Nu exista functii recursive, structuri de date complicate sau manipulare dinaica a memoriei.
Prezint pe scurt etapele principale ale functionarii proiectului :
1. se scrie sau se importa descrierea structurii
( fisierul sursa al descrierii VHDL se poate scrie intr-un textBox, sau se poate deschide un fisier existent)
2. se realizeaza verificarea corectitudinii sursei
( cu ajutorul programelor Lex&Yacc se parseaza fisierul si se verifica daca e scris conform gramaticii limbajului vhdl)
3. daca se gaseste o eroare se semnaleaza si se revine la pasul 1
( in cazul detectarii unei erori, se afiseaza mesajul Syntax error si linia la care e gasita eroarea)
4. daca sursa este corect descrisa se trece la pasul 5
5. se simuleaza functionarea entitatii descrise (pentru semnale de intrare setate, se vizualizeaza / interpreteaza semnalele de iesire)
Pentru o mai buna intelegere, analizez o entitate cu trei intrari : A,B,CARRY_IN si iesirile SUM si CARRY.
Descrierea VHDL pentru aceasta entitate este urmatoarea:
entity FULLADDER is
port (A,B, CARRY_IN: in bit; – SEMNALELE DE INTRARE
SUM, CARRY: out bit); – SEMNALELE DE IESIRE
end FULLADDER;
architecture STRUCT of FULLADDER is – ARHITECTURA REPREZINTA DESCRIEREA COMPORTAMENTULUI ENTITATII + SI A COMPONENTELOR
component HALFADDER
port (A, B : in bit;
SUM, CARRY : out bit);
end component;
component ORGATE
port (A, B : in bit;
RES : out bit);
end component;
signal W_SUM, W_CARRY1, W_CARRY2: bit;
begin
– SE INITIALIZEAZA SEMNALELE SI LEAGA SUBCOMPONENTELE ENTITATII INTRE ELE, CONFORM SCHEMEI.
MODULE1: HALFADDER
port map( A, B, W_SUM, W_CARRY1 );
MODULE2: HALFADDER
port map ( W_SUM, CARRY_IN,
SUM, W_CARRY2 );
MODULE3: ORGATE
port map ( W_CARRY2, W_CARRY1, CARRY );
end STRUCT;
Pentru HALFADDER,descrierea este urmatoare :
entity HALFADDER is
port(
A, B: in bit;
SUM, CARRY: out bit);
end HALFADDER
architecture RTL of HALFADDER is
begin
SUM <= A xor B;
CARRY <= A and B;
end RTL;
Schema este urmatoarea:
Entitatea HALFADDER este o subcomponenta pentru FULLADDER :
2.Parserul VHDL
2.1 Lex & Yacc
2.1.1 Introducere
Lex este un generator de analizor lexical, instrument care construieste automat un analizor lexical pornind de la specificatiile scrise in limbajul LEX. Limbajul are la baza patternuri definite ca expresii regulate. Pentru un string de intrare unde sunt gasite aceste expresii, sunt specificate actiuni scrise in C.
Yacc este de un generator de parser, instrument care construieste automat un parser avand la baza specificatii scrise in limbajul YAC. Regulile sintactice sunt specificate cu limbajul yacc. Cand o anumita syntax-pattern este recunoscuta, este executa instructiunea sau setul de instructiuni scris in C. Compilatorul de yacc este capabil sa genereze parsere si din gramatici context-free.
LEX si YACC sunt instrumnente puternice, folosite impreuna pentru a genera parsere, pentru a testa sintaxa anumitor specificatii si a determina daca sunt sau nu ambigue.
2.1.2 LEX:
Un program generat de lex primeste un fisier de intrare si recunoaste expresiile regulate din acest fisier. Recunosterea expresiilor are la baza regulile specificate de progrmator in fisierul *.lex . Compilatorul transforma aceste reguli intr-un program de nivel inalt, care este automatul finit ce recunoaste expresiile regulate.
Lex poate fi folosit ca tool individual, doar pentru analiza sursei, dupa recunoasterea expresiilor regulate, se executa actiunile specificate in sursa lex. Acesta pot fi reformatari ale textului, statistici, sau se pot scrie filtre in lex.
Figura de mai jos reprezinta folosirea lex-ului singur. Sursa lexsrc.l este transformata de compilator intr-un program C lex.yy.c, si generat un fisier executabil prin compilare. !!!!!!!!!POZA de introdus …………..
Lex este valorificat la maximum cand este combinat cu yacc, ca pre-procesor. In acest caz, programul in lex executa 'munca de jos' prin detectarea extitatilor de baza, pe care le paseaza parserului generat de yacc. Un analizor lexical nu va mai face nici o actiune.
2.1.3 Sintaxa generala
In general, sursa in lex are urmatoarea sintaxa:
DEFINITII
%%
REGULI
%%
SUBRUTINE
Partea esentiala este cea de definire de reguli. Acesta sectiune include expresiile regulate care trebuiesc recunoscute si eventualele actiuni care se executa. In loc de "expresii regulate" se utilizeaza "pattern".
Ca generalitati, sunt de retinut urmatoarele :
O regula este formata dintr-un pattern si actiunea apartinand acestui pattern. Fiecare pattern incepe cu o noua linie.
Actiunea default este de a copia un input la output.
Daca nici un pattern nu este gasit in stringul de intrare, este copiat textul in totatlitate. Patternul si actiunea asociata trebuiesc separate prin cel putin un spatiu.
Sectiunea de definitii si subrutinele sunt optionale. E de preferat sa se foloseasca subrutine cand analiza este complexa, pentru a nu se ingreuna codul.
2.1.4. Crearea de reguli Lex
Limbajul Lex poate fi modelat in functie de cerinte. Nu este folosit doar pentru potrivirea de stringuri, poate detecta context sensitivity, negatiile, poate numara frecventa de aparitie pentru un anumit pattern.
O parte din aceste facilitati sunt explicate mai jos:
se pot recunoaste stringurile simple
aceste pot fi tiparite direct in specificatiile lex, sau intre ghilimele. Daca se doreste un anumit caracter, se poate pune intre ghilimele sau prin backslash(\)
clasele de caractere sunt speficitate prin [] [aeiou]
in stringul de intrare se cauta vocalele, celelalta litere sunt ignorate.
negatia [^aeiou] – va cauta toate caracterele mai putin a.
secventa de caractere [d-i] este echivalnta cu [defghi]
elementele optionale sunt specificate prin "?" – de exemplu colou?r -> colour sau color – patternul abc[df-h]?ij va cauta stringurile care incep cu abc, urmate de d,f,g, sau h, si terminate cu ij – elementele repeitive
[A-Z][a-z]* inseamna orice numar de elemente repetitive, excluzand zero,incepnad cu litera mare [a-z]+ inseamna toate cuvintele cu litere mici.
alernanta este specificata prin | (red|green|blue)(color|colour) – redcolor, redcolour, bluecolor
2.1.5. Actiunile Lex
Asa cum am mentionat, se poate face orice program C care sa raspunda la patternurile cautate. Aditional, lex ofera mai multe functii si variabile externe care pot fi utile, mai ales cand este folosit cu yacc.
–yytext – contine stringul reprezentand identificatorul actual [A-Za-z][A-Za-z0-9]* va contine orice identificator.
–yyleng – variabila externa care contine lungimea lui yytext
–ECHO – actiune lex care copiaza inputul la output- este folosit cand nu a fost gasit patternul cautat
–yymore(),yyless(n),yychar,input(),output(c) – aceste functii prezinta un interes minor – se folosesc in general pentru gestionarea erorilor.
Consideratii speciale In lex sunt urmatoarele caractere speciale: " \ [] ^ = ? . * + | () $ / {} % < > Daca sunt folosite in stringuri, caractere, trebuies precedate de \. sau intre ghilimele. Simbolul | este folosit pentru a indica aceeasti actiune pentru mai multe patternuri : de exemplu : abc | [\n\t] { do } Comentariile in lex sunt identice cu cele in c /* */
2.1.6. Alte completari
A lucra cu lex este in mod normal destul de simplu. Mai intai, trebuie creata sursa lex in functie de specificatiile dorite. Acesta sursa este transalatata in analizorul lexical in C. (comanda lex sursefile.l) Daca nu sunt erori se genereaza sursa lex.yy.c – acest program conta in chemarea functiei yylex, care este analizorul lexical.
Se asteapta input de la stdin, si trimite la iesire stdout. Daca este folosit impreuna un yacc, include acesta functie si o inglobeaza in entitate.
Daca se foloseste singur, fara yacc, se apeleaza astfel : lex numefile.l => lex.yy.c gcc lex.yy.c -ll => a.out sau, pentru a i se da un alt nume gcc lex.yy.c -ll -o numeexecutabil Acesta cheama repetitiv functia yylex pentru a executa analiza. Daca este gasit un pattern la intrare, se executa actiunea indicata.
2.1.7 Interactiunea Lex si Yacc
Lex si Yacc sunt doua limbaje care se completeaza la crearea unui parser. Interfata dintre ele este destul de simpla. Actiunea Return din C este folosita pentru a intoarce un token pentru yacc, cu mentiunea ca a fost gasit in stringul de intrare. Acesti tokeni sunt declarati in yacc, dar programul generat de lex este inclus in sursa cod pt yacc, deci pot fi folositi in sursa lex.
Un token poate fi un identificator, sau un caracter simplu. Putem comunica in acest fel o valoarea parserului. Tipul definit este int, dar poate fi redefinit in yacc. Valoarea pe care dorim s-o asignam este gloabala – yylval inainte de return statement.
Un token este elementul fundamental al unei gramatici, care nu e mai e compus din nici o entitate. Yacc este un tool pentru generarea de rutine in functie de specificatiile pentru procesul de input specificate de user. Aceste specificatii alcatuisc regulile gramaticale, descrise de structura de input, si codul care se executa cand regulile sunt regasite.
Yacc genereaza a functie yyparse pentru a controla inputul. Acest parser cheama functia de analiza lexicala yylex pentru a regasi tokenii in fisierul de intrare (input stream). Tokenii sunt organizati in functie de regulile gramaticale. Cand una din aceste reguli este gasita, se executa codul pentru acesta regula, actiunea. !!!! de inserat figura 8
O sursa este trnasformata in lex.yy.c de compilatorul lex. Sursa yacc este transformata intr-un program C y.tab.c, care include analizorul lexical yylex. Parserul creat este executabil.
2.1.8 Definitii si declaratii pentru Yacc
Sursa yacc este divizata in trei parti :
DECLARATII
%%
REGULI DE TRANSARE
%%
RUTINE C
Partea de declaratii are doua subsectiuni : una reprezinta declaratii C delimitata %{ %} (acestea sunt copiate exact in sursa C generata) si cealalta parte, declaratiile de tokeni ai yaccului. Sectiunea de declaratii in C contine variabile, fisiere incluse sau definitii. variabilele definite aici sunt globale si pot fi accesate de actiuni sau de analizorul lexical. Tokenii sunt definiti in sectiunea de declaratii %token name1, name2 …… Toti tokenii definiti in partea declaarativa pot fi chemati cu analizorul lexical.
Trebuie definit un simbol de start : %start symbol Regulile de translatare sconstau in non-terminal urmat de : si in partea dreapta o secventa de terminals (tokeni) Partea dreapta poate avea alternative delimitate de '|'. Fiecare se termian cu ';'.
Dupa sau in corpul fiecareia poate fi actiune. Aceste actiuni se delimiteaza de { si }.
2.2 VHDL – ca un limbaj de programare
VHDL seamana cu un limbaj de programare; cei care sint familiarizati cu limbajul de programare Ada vor observa similaritati cu acest limbaj.
2.2.1.Elemente lexicale
a)Comentarii:
Comentarife in VHDL incep cu '–' si se continua pina la sfirsitul rindului. Ele nu au semnificatie intr-o descriere VHDL.
b)Identificatori:
Identificatorii in VHDL sint cuvinte rezervate sau nume definite de programator. Se formeaza dupa regula:
identificator ::= litera {[_ ] litera_sau_cifra}
Nu se face diferenta intre litere mari si litere mici, de izati in functie de regulile gramaticale. Cand una din aceste reguli este gasita, se executa codul pentru acesta regula, actiunea. !!!! de inserat figura 8
O sursa este trnasformata in lex.yy.c de compilatorul lex. Sursa yacc este transformata intr-un program C y.tab.c, care include analizorul lexical yylex. Parserul creat este executabil.
2.1.8 Definitii si declaratii pentru Yacc
Sursa yacc este divizata in trei parti :
DECLARATII
%%
REGULI DE TRANSARE
%%
RUTINE C
Partea de declaratii are doua subsectiuni : una reprezinta declaratii C delimitata %{ %} (acestea sunt copiate exact in sursa C generata) si cealalta parte, declaratiile de tokeni ai yaccului. Sectiunea de declaratii in C contine variabile, fisiere incluse sau definitii. variabilele definite aici sunt globale si pot fi accesate de actiuni sau de analizorul lexical. Tokenii sunt definiti in sectiunea de declaratii %token name1, name2 …… Toti tokenii definiti in partea declaarativa pot fi chemati cu analizorul lexical.
Trebuie definit un simbol de start : %start symbol Regulile de translatare sconstau in non-terminal urmat de : si in partea dreapta o secventa de terminals (tokeni) Partea dreapta poate avea alternative delimitate de '|'. Fiecare se termian cu ';'.
Dupa sau in corpul fiecareia poate fi actiune. Aceste actiuni se delimiteaza de { si }.
2.2 VHDL – ca un limbaj de programare
VHDL seamana cu un limbaj de programare; cei care sint familiarizati cu limbajul de programare Ada vor observa similaritati cu acest limbaj.
2.2.1.Elemente lexicale
a)Comentarii:
Comentarife in VHDL incep cu '–' si se continua pina la sfirsitul rindului. Ele nu au semnificatie intr-o descriere VHDL.
b)Identificatori:
Identificatorii in VHDL sint cuvinte rezervate sau nume definite de programator. Se formeaza dupa regula:
identificator ::= litera {[_ ] litera_sau_cifra}
Nu se face diferenta intre litere mari si litere mici, de exemplu id si Id reprezinta acelasi lucru.
c)Numere:
Numerele sint reprezentate in baza 10 (numere zecimale) sau in alta baza de numeratie (de la 2 la 16). Numerele care contin '.' sint considerate numere reale, celelalte fiind numere intregi. Numerele zecimale sint definite de:
numar_zecimal ::= intreg[.intreg][exponent]
intreg ::= cifra{[_]cifra}
exponent :.= E[+]intreg |E[-]intreg
Exemple:
0 1 123_456 678E9 –numere intregi
0.0 0.1 2.345 67 12.3E-4 –numere reale
Numerele date intr-o baza de numeratie sint definite de:
numar bazat ::= baza#intreg bazat[.intreg-bazat]#[exponent]
baza ::= intreg
intreg_bazat ::= cifra_extinsa{[_]cifra extinsa}
cifra-extinsa ::= cifra | litera
Baza si exponentul sint in baza 10. Exponentul reprezinta puterea la care se ridica baza, cu care va fi inmultit numarul. Literele de la A la F (de la a la f) sint 'cifre extinse' si reprezinta numerele de la 10 la 15.
Exemple:
2# 1100 0100# 16#C4# 4#301 #E1 –nr. intreg 196
2# 1.1111_1111_111 #E+ 11 16#F. FF#E2 –nr. real 4095
d) Caractere:
Caracterele sint delimitate de ’ ’.
Exemple: 'A' 'a'
e)Siruri de caractere:
Sirurile de caractere sint delimitate de "". Pentru a include " intr-un sir, ghilimelele trebuie dublate. Un sir de caractere poate reprezenta valoarea unui obiect care a un vector de caractere.
Exemple:
“Un sir in sir: “”Un sir”” “ –sir care contine "
f)Siruri de biti:
VHDL permite o reprezentare convenabila a vectorilor de biti ('0' sau ' 1'). Sintaxa este:
sir biti ::= baza_de_reprezentare"valoare_bit"
baza de_reprezentare ::= B | O | X
valoare bit ::= cifra extinsa{[ ]cifra_extinsa}
Baza de reprezentare poate fi B (in binar), O (in octal) sau H (in hexazecimal).
Exemple:
B"1010110" –lungimea sirului e 7
O"126" –lungimea a 9, B”001_010_110"
H"56" –lungimea a 8, B"0101_0110"
2.2.2.Tipuri de date si obiecte
In VHDL exista doua feluri de tipuri: tipuri SCALARE si tipuri COMPUSE.
Tipurile scalare includ numere, cantitati fizice si enumerari, si tipuri predefinite. Tipurile compuse sint vectori si inregistrari. In VHDL sint definite si tipurile 'access' (pointeri) si 'file' (fisiere).
declaratie_de_tip :.= type identificator is tip
tip ::= tip_scalar
tip_compus
tip_access
tip_file
tip_scalar ::= tip_enumerare I tip-intreg I tip_real tip_fizic
tip-compus ::= tip_tablou I tip_inregistrare
a)Tip intreg:
Tipul intreg reprezinta o multime de numere intregi dintr-un interval specificat. Sintaxa este:
tip_intreg ::= multime_in_interval
multime_in_interval ::= range
interval interval ::= expresie_simpla directie expresie_simpla
directie :.= to I downto
Expresiile care specifica intervalul trebuie sa aiba valori intregi. Limitele intervalului sint cuprinse intre -2147483647 si +2147483647.
Exemple:
type byte_int is range 0 to 255;
type signed is range -32768 to 32767;
type bit_index is range 31 downto 0;
Exista tipul predefinit 'integer', care reprezinta numere intregi cuprinse intre -2147483647 si +2147483647.
b)Tip fizic:
Tipul fizic este un tip numeric de reprezentare a unor cantitati fizice (lungime, timp, volti). Declaratia de tip include specificarea unei unitati de masura de baza si eventual un numar de unitati de masura secundare, care reprezinta multiplii ai unitatii de baza. Sintaxa este:
tip_fizic ::= constructor_interval
units
unitate_de_baza
{unitati secundare}
end units
unitate_de baza ::= identificator;
unitati_secundare ::= identificator = literal_fizic;
literal_fizic :.= [literal_abstract]nume_unit;
Exemple:
type length is range 0 to 1E9
units
um;
mm = 1000 um;
cm = 10 mm;
m = 1000 mm;
end units;
type resistance is range 0 to 1E8
units
ohms;
kohms = 1000 ohms;
Mohms = 1E6 ohms;
end units;
Exista tipul predefinit 'time', folosit in simulari VHDL pentru specificarea intirzierilor.
type time is range interval_maxim_din_implementare
units
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
end units;
Un numar de tip fizic se scrie: valoare unitate.
Exemple:
10 mm 1200 ohm 23 ns
c)Tip real:
Tipul real reprezinta o aproximare discreta a setului de numere reale ditrun interval specificat. Precizia de aproximare nu a definita de limbajul VHDL standard, dar numarul trebuie sa aiba maxim 6 cifre. Intervalul a cuprins intre -1E38 to + 1E38. Sintaxa: tip real :.= constructor interval
Exemple:
type signal level is range -10.00 to +10.00;
type probability is range 0.0 to 1.0;
Exista un tip predefinit 'real'. Intervalul de valori este predefinit si include valorile cuprinse intre -1E38 si +1E38.
d)Tip enumerare:
Tipul enumerare este o multime ordonata de identificatori sau caractere. Identificatorii si caracterele din cadrul unei enumerari trebuie sa fie distincti, dar pot fi 'refolositi' in enumerari diferite. Sintaxa este:
tip_enumerare ::= (enumeraref{,enumerare})
enumerare ::= identificator I caracter
Exemple:
type logic level is (unknown,low,undriven,high);
type alu_function is (disable,pass,add,substract,multiply,divide);
type octal_digit is (‘0',’1’,’2’,’3’,’4','5',’6’,’7’);
Exista o serie de tipuri de enumerari predefinite:
type severity-level is (note,warning,error,failure);
type boolean is (false,true);
type bit is ('0','1');
type character is (
NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL,
BS, HT, LF, VT, FF, CR, SO, SI,
DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB,
CAN, EM, SUB, ESC, FSP, GSP, RSP, USP,
‘ ‘, ‘!’, … ‘z’, ‘{‘, ‘|’, ‘}’ , ‘~’, DEL);
I
e)Tablouri :
In VHDL, un tablou e o colectie indexata de elemente de acelasi tip. Tablourile pot fi unidimensionale sau multidimensionale. Un tablou poate avea dimensiunea definita la declarare sau nedefinita, urmind ca indicele sa is valori definte ulterior. Sintaxa este:
tip_tablou ::= tablou dim-nedefinita I tablou_dim_definita
tablou dim_nedefinita ::=array (subtip-index{,subtip-index})
of subtip element
tablou dim _definita ::= array multime_index of subtip_element
subtip-index ::= tip range <>
multime_index ::=(interval _discret{,interval_discret})
interval discret ::= subtip discret I interval
Exemple:
type word is array (31 downto 0) of bit;
type memory is array (address) of word;
type transform is array (1 to 4,1 to 4) of real;
type register _bank is array (byte range 0 to 132) of integer;
type vector is array (integer range <>) of real;
Simbolul ' < >' poate fi vazut ca o 'locatie' pentru index, care va fi 'umpluta' atunci cind a folosit vectorul. De exemplu, un obiect a declarat vector de 20 de elemente astfel:
vector(1 to 20).
Exista doua tipuri predefinite de tablouri (vectori):
type string is array (positive range <>) of character;
type bit vector is array (natural range <>) of bit;
Tipul 'bit vector' a folosit la simularea sistemelor digitale.
Un element al tabloului poate referit prin indexarea numelui tabloului. De exemplu, fie a si b doua tablouri de dimensiuni 1 si, respectiv, 2. Atunci a(1) si b(l,1) se refera la elementele tablourilor. Se pot referi si parti continue din tablouri, de exemplu a(8 to 15) este un vector de 8 elemente care a inclus in vectorul a.
Fie un tip de tablou declarat astfel:
type a is array (1 to 4) of character;
si vrem sa scriem un vector de acest tip, care sa contina elementele 'f, 'o', 'o', 'd' in aceasta ordine. Putem scrie astfel: ('f' 'o' 'o' 'd') in care elementele sunt in ordinea crescatoare a indicelui.
O alta varianta ar fi aceasta: (1=>'f',3=>'o',4=>'d',2=>'o')
In acest caz, este dat explicit indicele pentru fiecare element, care pot fi deci in orice ordine. Ambele variante pot fi folosite in cadrul scrierii aceluiasi tablou. Se poate folosi si cuvintul 'others', in locul indexului, care indica spre o valoare folosita pentru toate elementele care nu au fost mentionate explicit.
De exemplu: ('f',4=>'d',others=>'o')
f)Inregistrari:
Inregistrarile in VHDL sint colectii de elemente, care pot avea tipuri diferite. Sintaxa este:
tip_inregistrare ::=
record
element
{element}
end record
element ::= lista_identificatori : subtip_element;
lista identificatori ::= identificatorf{,identificator}
subtip element ::= subtip
Exemple:
type instruction is
record
op_code : processor_op;
address mode : mode;
operand1,operand2 : integer range 0 to 15;
end record;
Fie o inregistrare r si un cimp f in aceasta inregistrare. Acel cimp poate fi referit cu numele 'r.f’
g)Subtipuri:
Subtipurile reprezinta tipuri de baza restrictionate. Sintaxa este:
declaratie subtip ::= subtype identificator is subtip;
subtip ::= [functie de rezolutie] marcaj_tip [constringere]
marcaj_tip ::= nume_tip I nume-subtip
constringere ::= mult:wq :ime_inintervalimultime-index
Exista doua feluri de subtipuri:
1. Subtipul reprezinta valorile unui tip scalar, restrinse la un interval.
subtype pin_count is integer range 0 to 400;
subtype digits is character range '0' to '9';
2 . Subtipul reprezinta valorile unui tablou cu indici nedefiniti, restrins prin definirea indicilor. subtype id is string(1 to 20);
subtype word is bit_vector(31 downto 0);
Exista doua subtipuri numerice predefinite:
subtype natural is integer range 0 to MAXINT
subtype positive is integer range 1 to MAXINT
h)Declaratii de obiecte:
In VHDL exista trei clase de obiecte: CONSTANTE, VARIABILE si SEMNALE. Vom discuta despre constante si variabile. O constanta este un obiect care este initializat cu o valoare cind e creat, care nu mai este ulterior modificata. Sintaxa este:
constanta::=constant lista_identificatori:subtip[:=expresie];
Declaratiile de constante care nu au expresie de initializare se numesc 'constante aminate', si nu apar decit in declaratiile de package. Valoarea de initializare trebuie data bin package body corespunzator.
Exemple:
constant e : real := 2.13455;
constant max-size : natural;
B Variabila e un obiect a carui valoare se poate modifica.
Sintaxa este:
variabila::=variable lista_identificatori:subtip[:=expresie];
Daca expresia de initializare lipseste, i se asociaza variabilei o valoare implicita. Pentru tipurile scalare implicita este cea mai din stinga valoare pentru acel tip: prima dintr-o enumerare, cea mai mica dintr-un interval dat in ordine ascendenta, cea mai mare dintr-un interval dat in ordine descendenta. Daca variabila este un tip compus, valoarea implicita este formata din toate valorile implicite alr tipurilor componente.
Exemple:
variable count : natural := 0;
variable trace : trace_array;
Daca 'trace array' este un vector de booleeni, atunciu valoarea initiala a lui 'trace' este un vector de elemente cu valorile 'false'.
Mind dat un obiect, a posibil sa se defineasca nume alternative pentru obiect sau pentru o parte din el. Sintaxa este:
nume_alternativ::=alias identificator:subtip is nume;
Exemple:
variable instr : bit_vector(31 downto 0);
alias op code : bit vector(7 downto 0) is instr(31 downto 24) ;
In acest exemplu, numele 'op code' este un nume alternativ pentru primii 8 biti din 'instr'.
i) Atribute:
Tipurile si obiectele declarate in VHDL pot avea asociate informatii suplimentare, numite atribute. Un atribut este referit cu '''.
Atribute pentru orice tip sau subtip scalar T:
ATRIBUT REZULTAT
T'left valoarea stinga a lui T
T’right valoarea dreapta a lui T
T'low valoarea cea mai mica a lui T
T'high valoarea cea mai mare a lui T
Atribute pentru orice tip T, X fiind membru al lui T si N un
intreg:
ATRIBUT REZULTAT
T’pos(X) pozitia lui X in T
T’val(N) valoarea de la pozitia N din T
T’leftof(X) valoarea de la stinga lui X in T
T'rightof(X) valoarea de la dreapta lui X in T
T’pred(X) valoarea imediat mai mica decit X in T
T’succ(X) valoarea imediat mai mare decit X in T
Atribute pentru orice tablou sau obiect A, N fiind un intreg cuprins intre 1 si numarul de dimensiuni ale lui A:
ATRIBUT REZULTAT
A'left(N) v al.stinga a interv.index pt.dim.N in A
A’right(N) val.dreapta a interv.index pt.dim.N in A
A'low(N) val.cea mai mica int.index pt.dim.N in A
A'high(N) val.cea mai mare int.index pt.dim.N in A
A’range(N) int.de valori pt.index pt.dim.N in A
A’reverse_range(N) int.valori pt.index, in ordine inversa
A’length(N) lungimea intervalului index pt.dim.N in A
2.2.3.Epresii si operatori
Iata lista operatorilor din VHDL in ordinea descrescatoare a precedentei
** abs not
* / mod rem
+(unar) -(unar)
+ – &
= /= < <= > >=
and or nand nor xor
Operatorii logici AND, OR, NAND, NOR, XOR si NOT opereaza pe valori de tip bit sau boolean si pe vectori (tablouri unidimensionale) de aceste tipuri. Operatiile pe vectori se aplica intre elementele corespondente ale fiecarui vector, rezultatul fiind tot un vector.
Operatorii relationali =, /=, < , < =, > si > = se aplica pe operanzi de acelasi tip si au ca rezultat o valoare booleana. Operatorii de egalitate (= si /=) permit operanzi de orice fel de tip. Ceilalti operatori permit numai operanzi de tip scalar sau vectori uni-dimensionali de tip discret. Operatorii + si – (unari sau binari) au semnificatia uzuala pentru operanzi numerici. Operatorul de concatenare (&) se aplica pe vectori, rezultatul fiind un nou vector format din alipirea celor doi vectori operanzi. Se pot concatena un element si un vector, sau doua elemente care prin concatenare formeaza un nou vector de dimensiune 2. '
Operatorii de inmultire si impartire (* si /) lucreaza pe numere intregi, reale si operanzi de tip fizic. Operatorii cit si rest (MOD si REM) lucreaza numai pe tipuri intregi. Operatorul valoare absoluta (ABS) lucreaza cu operanzi de orice tip. Operatorul ridicare la putere (**) poate avea un intreg sau un real in partea stinga, dar trebuie sa aiba neaparat un intreg in partea dreapta. Un exponent negativ a permis numai daca operandul din partea stinga a tip real.
2.2.4.Instructiuni secventiale
a)Instructiunea de atribuire:
Sintaxa este:
instr atribuire::=tinta:=expresie;
tinta::=nume l lista-elemente
Partea stinga si partea dreapta ale atribuirii trebuie sa aiba acelasi tip de baza. Daca in partea stinga se afla o lista de elemente, atunci ele trebuie sa fie nume de obiecte, iar in partea dreapta trebuie o valoare compusa de acelasi tip. ?Mai intii se se evalueaza lista, apoi expresia si, in final, componentele expresiei sint atribuite.
Exemplu: ( a => r.b , b => r.a ) = r
b)Instructiunea IF:
Sintaxa este:
instr_if::=if conditie then
secventa_instr
{ elsif conditie then
secventa_instr}
[else secventa_instr]
end if;
c)Instructiunea CASE:
Sintaxa este:
instr_case::= case expresie is
alternativa case
{alternativa case}
end case;
alternativa_case::=when alternative => secventa_instr
alternative::=alternativa{, alternativa}
alternativa::=expresie_simpla
interval discret
nume_simplu_element
others
Expresia de selectie trebuie sa aiba tip discret sau sa fie un vector de caractere. Alternativele trebuie sa fie distincte, deci nu trebuie sa existe valori duplicate. Toate valorile trebuie sa fie prezente in lista de alternative, sau OTHERS trebuie inclusa ca ultima alternativa. Daca expresia are ca valoare un vector, atunsi alternativele trebuie sa fie siruri de caractere sau siruri de biti.
Exemple:
case element colour of
when red => instr. red;
when green I blue => instr. green, blue;
when orange to turquoise => instr;
end case;
case opcode of
when X"00" => perform add;
when X"01" => perform substract;
when others => signal_illegal_opcode;
end case;
d)Implementarea ciclurilor:
Sintaxa este:
ciclu::= [eticheta:]
[schema de iteratie] loop
secventa_instr
end loop [eticheta];
schema_de iteratie::= while conditie
| for specificare_parametrii_bucla
specificare_parametrii_bucla::= identificator in interval_discret
Daca lipseste schema de iteratie, avem cazul unei bucle infinite:
loop ceva;
end loop;
Exemplu ciclu WHILE:
while index < length and str(index) /= " loop
index := index + 1;
end loop;
Exemplu ciclu FOR:
for item in 1 to last item loop table(item) := 0;
end loop;
Exista doua tipuri de instructiuni aditionale care pot fi folosite in cadrul unui ciclu. Cu instructiunea 'next' se termina executia iteratiei curente si se trece la iteratia urmatoare. Cu instructiunea 'exit' se termina executia iteratiei curente si se iese din ciclu. Sintaxa este:
instr next::=next[eticheta][when conditie];
instr exit::=exit[eticheta][when conditie];
Daca este omisa eticheta, instructiunile se executa in cadrul buclei celei mai apropiate care le cuprinde, altfel se executa in cadrul buclei specificate de eticheta. Daca clauza WHEN este prezenta, dar conditia asociata a evaluata la FALSE, iteratife continua normal.
Exemple:
for i in 1 to max str_len loop
a(i) .= buf(i);
exit when buf(i) = NUL;
end loop;
outer loop: loop
inner_loop: loop do_something;
next outer_loop when temp = 0;
do_something-else;
end loop inner_loop;
end loop outer loop;
e)Instructiunea vida:
Instructiunea vida nu are nici un efect. Este folosita cind se doreste sa se arate explicit ca in unele cazuri nu se executa nimic. E folosita frecvent in instructiuni CASE, unde trebuie listate toate valorile alternative posibile ale unei expresii, cu toate ca pentru unele dintre ele nu trebuie sa se execute nimic. Sintaxa este: case controller command is when forward => engage motor forward; when reverse => engage-motorreverse; when idle => null; end case;
f)Asertiuni:
Asertiunile sint folosite pentru verificarea unor conditii
specificate si raportarea in caz ca acestea nu sint verificate.
Sintaxa este:
asertiuni::=assert conditie
[report expresie]
[severity expresie];
Daca a prezenta clauza REPORT, rezultatul expresiei trebuie sa fie un sir de caractere. Acesta reprezinta un mesaj ce va fi raportat in cazul in care conditia a evaluata la FALSE. Daca este omis, mesajul implicit este "Assertion violation". Daca e prezenta clauza SEVERITY atunci tipul expresiei trebuie sa fie 'severity-level'. Daca a omisa, valoarea implicita este 'error'. Un simulator termina executia daca o asertiune nu se mai respecta si valoarea specificata de 'severity' a mai mare decit un prag dat, dependent de implementare. De obicei acest prag este dat de catre utilizator.
2.2.5.Subprograme si pachete
Subprogramele VHDL pot fi proceduri sau functii. Exista si modalitati de incapsulare a datelor in package-uri(engl.pachet).
a)Proceduri si functii:
Sintaxa este:
subprogram::=specificare-subprogram;
specificare_subprogram::= procedure nume[(lista parametrii formali)] function nume[lista parametrii-formali)] return marcaj tip
Aceste declaratii de subprograme sint folosite de obicei in specificatii de package, unde corpul subprogramului este dat in corpul package-ului.
lista_parametrii_formali::=lista-interfata parametrii
lista_interfata::=element_interfata{;element interfata}
element_interfata::=declaratie_interfata
declaratie_interfata::=declaratie_constante_interfata declaratie -semnale_interfata declaratie variabile interfata
declaratie_constante_interfata::=
[constant]lista_identificatori:in subtip[:=expresie_statica]
declaratie variabile interfata::=
[variable]lista_identificatori:[mod]subtip[:=expresie_statica]
Exemple:
1) procedure reset;
…
rest; –apel procedura
2) procedure increment_reg ( variable reg : inout word_32;
constant incr : in integer := 1 );
In al 2-lea exemplu, procedura are 2 parametrii: 'reg' si 'incr'. Modul lui 'reg' este INOUT, ceea ce inseamna ca 'reg' poate fi citit si asignat. Alte moduri posibile sint: IN (parametrul poate fi numai citit) si OUT, parametrul nu poate fi decat asignat. Daca modul este INOUT sau OUT, atunci cuvintul VARIABLE poate lipsi, fiind subinteles. Daca modul este IN, atunci CONSTANT poate lipsi. Un apel de procedura contine lista parametrilor actuali, data sub aceiasi forma ca la vectori.
Exemple:
increment reg ( index_reg , offset-2 ); –adaug o
valoare
increment reg ( prog counter ); –
incrementez(1=implicit)
sau
increment_reg ( incr => offset-2 , reg => index_reg );
increment_reg ( reg => prog_counter );
Exemplu de functie:
function byte_to_int ( byte : word-8 ) return integer;
Pentru functii, modul trebuie sa fie IN, si nu mai trebuie specificat. Daca nu se specifica clasa parametrului, se considera implicit CONSTANT.
Sintaxa pentru corpul unui subprogram este:
corp_subprogram::=
subprogram is
declaratii subprogram
begin
instructiuni_subprogram
end [nume];
declaratii_subprogram::={declaratie subprogram}
instructiuni_subprogram::={instructiune secventiala}
declaratie-subprogram::=subprogram
corp_subprogram
tip
subtip
constanta
variabila
alias
Numele listate dupa partea declarativa a subprogramului sint locale si nu sint vizibile in afara subprogramului. Ele chiar "ascund" semnificatia celor declarate in afara. Dupa apel, se executa instructiunile subprogramului pina se ajunge la sfirsit sau la un RETURN:
insructiunea-return::=RETURN[expresie];
Functiile nu trebuie sa aiba efecte laterale. O consecinta importanta a acestei reguli este ca functiile pot fi apelate fara sa aiba efect asupra mediului apelant.
Exemplu corp functie:
function byte_to_int ( byte : word_8 ) return integer is variable result : integer := 0
begin
for index in 0 to 7 loop
result := result * 2 + bit'pos ( byte ( index) );
end loop;
return result;
end byte to_int;
REDENUMIRE: exista posibilitatea ca 2 subprograme sa aiba acelasi nume, diferentiindu-se prin numarul sau tipul parametrilor.
Exemple:
function check limit ( value : integer ) return boolean;
function check-limit ( value : word-32 ) return boolean;
…
testl := check-limit ( 4095 ) – apel prima functie
test2 := check-limit ( X"0000_OFFF") –apel a doua functie
Numele subprogramului poate fi simbol de operator. Se pot redefini astfel operatori pentru tipuri not de operanzi.
Exemple:
function "+"(a,b : word_32) return word_32 is
begin
return inttoword_32(word_32_to_int(a) + word-32 to int(b));
end "+";
…
X"1000_0010" + X"0000_FFDO" –e apelat noul operator
"+"(X"1000_0010",X"0000_FFDO") –notatie echivalenta
b)Package-uri si declaratia corpului pentru package
Un package – pachet – este o colectie de tipuri, constante, subprograme si eventual alte constructii care se folosesc de obicei pentru a implementa un anumit serviciu sau pentru a izola un anume grup de elemente intre care exista o legatura. Mai mult decit atit, detaliile implementarii unui package pot fi "ascunse" fata de utilizatorii acestuia, lasind vizibila pentru restul lumii numai partea de interfata.
Un package poate fi impartit in doua parti: partea de declaratii – care defineste interfata package-ului – si corpul package-ului, in care se define celelalte detalii. Corpul poate fi omis daca nu sint alte detalii nespecificate in partea de declaratii. Sintaxa declararii unui package este urmatoarea:
declaratie_package ::=
package identificator is
partea_declarativa_package
end [ nume-simplu package ] ;
partea_declarativa_package ::=[ element_parte_declarativa ]
element_parte_declarativa ::=declaratie subprogram
declaratie_tip
declaratie_subtip
declaratie_constanta
declaratie_alias
clauza_use
Declaratiile definesc acele elemente care vor fi vizibile pentru utilizatorii packageului si totodata in interiorul corpului package-ului. Trebuie notat de asemenea ca mai sint si alte tipuri de declaratii ce mai pot fi incluse, dar nu vor fi discutate aici.
Exemple:
package data_types is
subtype address is bit_vector(24 downto 0);
subtype data is bit _vector(15 downto 0);
constant vector_table_loc : address;
function data _to_int(value : data) return integer;
function int_to_data(value : integer) return data;
end data_types;
In acest exemplu, declararea valorii constantei vector table precum si corpurile celor doua functii sint aminate, drept pentru care este necesar si corpul package-ului.
Sintaxa pentru corpul unui package este:
corp_package ::=
package body nume_simplu package is
parte declarativa_corp_package
end [ nume-simplu package ] ;
parte declarativa_corp package ::= [element corp package ]
element_package ::=
declaratie subprogram
corp_subprogram
declaratie_subtip
declaratie_constanta
declaratie_alias
clauza_use
In corpul package-ului poate fi inclus corpul unui subprogram, in timp ce in interfata package-ului nu poate fi inclusa decit partea declarativa a unui subprogram.
Exemple:
package body data types is
constant vector_table_loc : address = X"FFFFF00";
function data_to_int(value : data) return integer is corpul functiei data_to_int
end data_to_int;
function int_to_data(value : integer) return data is corpul functiei int_to_data;
end int_to_data;
end data_types;
In corpul package-ului se specifica valoarea pentru constante si sint date corpurile functiilor. Declaratife de subtip nu sint repetate deoarece cele facute in partea declarativa a package-ului sint vizibile si in corpul package-ului.
Odata package-ul declarat, elementele sale pot fi referite prin prefixarea numelui for cu numele package-ului. Pentru exemplele anterioare:
variable PC : data_types.address;
int_vector_loc := data_types.vector_table_loc * 4 int_level;
offset := data types.data to int(offset reg);
Adeseori este convenabil sa poti referi elementele dintr-un package fara a le mai prefixa cu numele acestuia. Pentru aceasta se foloseste clauza use intr-o parte declarativa. Sintaxa acesteia este:
clauza_use ::= use nume selectat { , nume_selectat } ;
nume selectat ::= prefix . sufix
Efectul utilizarii acestei clauze este ca toate numele listate pot fi folosite apoi fara a mai fi nevoie sa le, prefixam cu numele package-ului. Se poate folosi sufixul *all* pentru referirea neprefixata la toate elementele declarate intr-un package.
Exemple:
use data types.all;
2.2. 6. Structura sistemelor in VHDL
6.1. Entitati
Un sistem digital este format dintr-o colectie ierarhica de module. Fiecare modul are un set deporturi care constituie interfata lui cu exteriorul. Un asemenea modul se numeste in VHDL entity.
Sintaxa pentru descrierea unei entitati este:
entitate ::= entity identificator is
antet_entitate
declaratii_entitate
[begin
instructiuni_entitate ]
end [ nume_simplu_entitate ] ;
antet entitate ::=
[ clauze_formale_generice ]
[ clauze_formale_porturi ]
clauze generice :.= generic ( lista generic ) ;
lista generic ::= lista interfata generica
clauze_porturi ::= port ( lista porturi ) ;
lista_porturi::= lista interfata porturi
declaratie_entitate ::= { elemente_declaratie_entitate}
In partea declarativa a unei entitati se declara elementele care vor fi folosite in implementarea acesteia. De obicei aceste declaratii sint incluse in partea de implementare. De obicei, declaratiile optionale din partea declarativa a unei entitati sint pentru a defini comportamente particulare ale entitatii.
Constantele generice pot fi folosite pentru a controla structura si comportamentul entitatii, iar porturile pentru a specifica canalele de intrare si iesire ale entitatii. Ele sint de tip constant. Valoarea actuala a unei constante generice este transmisa in momentul in care entitatea este folosita.
Porturile unei entitati sint de tip signal. Sintaxa este:
interfata_signal ::=
[ signal ] lista-identificatori : [ mod ] subtip [ bus]
[ := expresie statica ]
Cuvintul bus poate fi folosit daca portul poate fi conectat la mai mult de o iesire . La fel ca la constantele generice, semnalele care trebuie conectate la porturi sint specificate cind entitatea este folosita.
Exemple:
entity processor is
generic (max_clock_freq: frequency := 30 MHz);
port (clock: in bit; address: out integer; data: inout word_32; control: out proc control; ready in bit );
end processor;
In acest caz constanta generica max clock freq este folosita pentru a specifica comportamentul in timp al entitatii. In codul care descrie comportamentul entitatii, se va folosi aceasta valoare pentru a determina intirzierile in schimbarea valorilor semnalelor. In exemlul urmator se arata cum pot fi folositi parametrii generici pentru a specifica o clasa de entitati de structura variabila:
entity ROM is
generic (width , depth: positive);
port (enable : in bit; address: in bit_vector(depth – 1 downto 0; data : out bit_vector(width – 1 downto 0);
end ROM;
Aici cele doua constante generice sint folosite pentru a specifica numarul de biti de date si respectiv de adresa pentru memoria "read-only". Trebuie notat ca nu este data nici o valoare implicita pentru aceste constante. Aceasta inseamna ca atunci cind entitatea este folosita ca o componenta trebuie actualizate valorile acestora.
Exemplu de declaratie de entitate fara constante sau porturi generice:
entity test bench is
end test_bench
Cu toate ca acest exemplu pare la prima vedere sa nu aiba sens, de fapt el ilustreaza o utilizare des intilnita a entitatilor:
O entitate "top-level" pentru un design in curs de testare (design under test – DUT) este folosita ca o componenta intr-un circuit de tip "banc de lucru" impreuna cu o alta entitate generator de test (test generator – TG). Nu este nevoie de conexiuni externe, deci nu exista porturi.
6.2. Arhitecturi
Odata specificata interfata entitatii, una sau mai multe implementari ale acesteia pot fi descrise in sectiunea architecture, care reprezinta corpul entitatii. Pot fi date mai multe arhitecturi care descriu aceeasi entitate. De exemplu, o arhitectura poate descrie pur si simplu comportamentul entitatii, in timp ce alta poate descrie aceeasi entitate ca o colectie ierarhica de componente. In aceasta sectiune vom prezenta numai descrierile structurale.
arhitectura ::= architecture identificator of nume entitate is parte_declarativa_arhitectura
begin
instructiuni arhitectura
end [ nume arhitectura ];
parte_declarativa_arhitectura ::= { bloc declaratii }
instructiuni-arhitectura ::={ instructiune-concurenta }
bloc_declaratii ::= declaratie_subprogram
corp_subprogram
declaratie_tip
declaratie_subtip
declaratie_constanta
declaratie_alias
declaratie_signal
declaratie_componente
declaratie_configuratie
clauza_use
instructiune_concurenta ::=
instructiune_bloc
instructiune-instantiere_componenta
Declaratiile dintr-o arhitectura definesc elemente care vor fi folosite pentru a construi descrierea designului. In particular, semnalele si componentele pot fi declarate aici si folosite pentru a construi o descriere structurala, dupa cum a fost exemplificat.
a)Declaratiile de semnal
Semnalele sint folosite pentru a conecta submodule. Ele sint declarate cu sintaxa urmatoare:
declaratie_signal ::=
signal lista-identificatori : subtip [ tip-signal ][ :=expresie ] ;
tip-signal ::= register | bus
Expresia din declaratie este folosita pentru a initializa semnalul in timpul fazei de initializare a simulari. Daca expresia este omisa, atunci va fi asignata o valoare implicita.
b)Blocuri
Submodulele dintr-o arhitectura pot fi descrise ca blocuri. Un bloc este o unitate ce contine propria interfata, conectat cu alte blocuri sau porturi prin semnale. Sintaxa este:
bloc ::= eticheta_bloc
block [ (expresie_garda) ]
antet_bloc
parte_declarativa_bloc
begin
instructiuni bloc
end block [ eticheta bloc ] ;
antet_bloc ::=
[ clauza generica [ map_generic ;] ]
[ clauza port ]
[ map port ; ] ]
map-generic ::=
generic map ( lista asociatie generic )
map_port ::=
port map ( lista asociatie port )
parte_declarativa_bloc ::= declaratie bloc }
instructiuni_bloc ::=
{ instructiune concurenta }
Antetul blocului defineste interfata cu blocul respectiv in mod similar cu modul in care antetul entitatii defineste interfata unei entitati. Lista de asociatie generica specifica valorile pentru constantele generic evaluate in blocul inconjurator sau in arhitectura. Lista de asociatie a porturilor specifica care dintre semnalele sau porturile blocului inconjurator sau arhitecturii sint conectate la porturile blocului. Trebuie notat ca instructiunile dintr-un bloc pot de asemenea contine instructiuni bloc, astfel ca un proiect (design) poate fi privit ca o ierarhie de blocuri ce contine descrierea comportamentului la baza ierarhiei.
Ca si exemplu, sa presupunem ca se doreste descrierea structurii unei arhitecturi a entitatii procesor descrise intr-o sectiune anterioara. Daca separam procesorul intr-o structura de control si o sectiune ce se ocupa cu calea datelor, putem scrie o descriere ca o pereche de blocuri inlantuite.
Unitatea de control are porturile clk, bus control si bus ready, care sint conectate la porturile entitatii procesor. Exista de asemenea un port de iesire pentru controlul caii de date, acesta fiind conectat la un semnal declarat in sectiunea de arhitectura. Acest semnal este conectat de asemenea la un port de control din blocul caii de date. Porturile de date si de adrese ale blocului caii de date sint conectate la porturile corespunzatoare ale entitath respective. Avantajele acestei abordari modulare se refera la faptul ca odata precizate blocurile, fiecare dintre acestea poate fi dezvoltat separat. Aici putem observa aplicarea cu succes a separarii partii de interfata in mod clar, lucru ce permite astfel dezvoltarea in paralel.
architecture block structure of processor is
type data-path-control is …. ;
signal internal control : data-path-control;
begin
control unit: block
port (clk: in bit; bus _control : out proc_control;bus _ready : out data-path-control);
port map (clk => clock, bus_control => control, bus_ready => ready,
control => internal control);
declaratii pentru control_unit
begin
instructiuni pentru control-unit
end block control unit;
data_path: block
port (address: out integer;data : inout word 32; control : in data_path_control);
port map (address => address, data=> data, control => internal-control);
declaratii pentru data_path
begin
instructiuni pentru data_path;
end block_data_path;
end block_structure;
c)Declaratii de componente
O arhitectura poate folosi de asemenea si alte entitati descrise separat si plasate in biblioteci de proiectare. Pentru aceasta, intr-o arhitectura trebuie sa se declare o componenta la care ne putem gindi ca la un model ce defineste o entitate de proiectare virtuala. Aceasta componenta virtuala va fi instantiata mai tirziu in cadrul arhitecturii.
Sintaxa este:
componenta ::=
component identificator
[ clauza generic-locala ]
[ clauza port locala ]
end component;
Exemple:
component nand3
generic (Tpd : Time := 1 ns );
port (a,b,c :in logic level; y :out logic-level);
end component;
component read_only_memory
generic(data bits, addr_bits : positive);
port( en : in bit;
addr : in bit _vector(depth-1 downto 0);
data : out bit_vector(width-1 downto 0);
end component;
Acest exemplu declara o poarta cu 3 porturi si un parametru generic care specifica intrizierea de propagare. Instante diferite pot fi folosite apoi cu intirzieri de propagare diferite. Al doilea exemplu declara o componenta de memorie "read-only" ale carei dimensiuni sint dependente de constante generice.
2.2.7. Comportamente VHDL
Comportamentul unui sistem digital poate fi descris in termenii unui limbaj de programare. Aspectele legate de paradigma secventiala a VHDL ca limbajj de programare au detaliate in capitolul 2. In acest capitol vom incerca sa descriem modul in care aceste caracteristici ale VHDL sint extinse pentru a include instructiuni ce permit modificarea valorilor unor semnale si mijloace de a raspunde la modificarea valorilor unor semnale.
a)Atribuiri pentru semnale
Sintaxa este:
instructiune_atribuire_semnal ::=
destinatie <= [ transport ] forma unda ;
destinatie ::= nume | nume-compus
forma unda ::= element forma unda {, element forma unda }
element_forma_unda::= valoare_expresie [ after expresie_timp | null [ after expresie time ]
Destinatia trebuie sa reprezinte un semnal sau un grup de semnale. Daca expresie timp ce corespunde intirzierii este omisa, atunci se va considera valoarea implicita de 0 fs. Aceasta inseamna ca se va considera ca tranzactia a avut loc simultan cu executia operatiei de atribuire.
Asociem fiecarui semnal valoarea prevazuta a formei sale de unda. Atribuirile introduc tranzitii in aceasta forma de unda. De exemplu:
s <= ‘0' after 10 ns;
Aceasta atribuire va avea ca efect trecerea la '0' a valorii semnalului s la 10 ns dupa executia acesteia. Putem reprezenta valorile prevazute ale unui semnal dat, desenind tranzitiile care apar de-a lungul unei axe pe care consideram timpul. Spre exemplu, daca atribuirea de mai sus ar fi executata la momentul t=5 ns, atunci reprezentarea grafica ar fi cea de mai sus:
Cind simularea a atins momentul de timp t=15ns, tranzitia corespunzatoare va fi procesata si valoarea semnalului va fi modificata. Sa presupunem in continuare ca la momentul de timp t=16 ns, executam atribuirea urmatoare:
s <= ‘1' after 4 ns, '0' after 20 ns;
Vor apare astfel doua not tranzitii. Trebuie notat faptul ca atunci cind intr-o atribuire sint prezente mai multe tranzitii, momentele de timp ce specifica intirzierea trebuiesc sa fie in ordine crescatoare.
Este interesant de notat ce se intimpla in cazul executiei unor atribuiri de valori pentru semnale pentru care existau si alte atribuiri anterioare. Aici distingem doua cazuri, dupa cum cuvintul rezervat *transport* este sau nu inclus in instructiunea de atribuire. A stfel
daca *transport* apare, atunci intirzierea se numeste intirziere de transport.In aceasta situatie, orice alte tranzitii anterioare tranzitiei cauzate de executia atribuirii curente vor fi sterse. Este ca si cum tranzitiile anterioare ar fi suprascrise de tranzitia cauzata de atribuirea curenta.
daca nu se foloseste transport, atunci vom avea de-a face cu al doilea tip de intirziere ce se numeste intirziere inertiala. Acest tip de atribuire a valorii unui semnal corespunde dispozitivelor fizice care nu prezinta un raspuns la excitarea cu un impuls de durata mai mica decit intirzierea pe care o necesita propagarea semnalului de excitatie pina la iesire. In aceasta situatie, vor fi sterse toate acele tranzitii care ar fi trebuit sa aiba loc inainte de tranzitia nou introdusa si vor ramine numai cele care vor avea loc in timp de la momentul de timp corespunzator tranzitiei nou introduse inainte.
Cind se executa o atribuire a valorii unui semnal in care se specifica mai multe tranzitii, prima tranzitie va fi considerata de tip inertial, iar restul de tip intirziere de transport.
b)Procese. Instructiunea wait
Unitatea de baza intr-o descriere comportamentala de tip VHDL este procesul. Un proces este un corp de cod cu executie secventiala ce poate fi activat ca raspuns la o schimbare de stare. Atunci cind se executa mai mult de un proces la un moment dat, executia va avea loc concurent. Procesele sint specificate conform sintaxei urmatoare:
instructiune_proces ::= [ eticheta proces : ]
process [ ( lista semnale ) ]
parte declarativa proces
begin
instructiuni_proces
end process [ eticheta proces ] ;
parte_declarativa_proces :.= { element declaratie proces }
element_declaratie_proces ::= declarare subprogram
corp_subprogram
declarare_tip
declarare_subtip
declarare_constante
declarare_variabile
declarare_alias
clauza_use
instructiuni proces ::= { instructiune_secventiala }
instructiune secventiala ::= {instructiune wait
instructiune_atribuire
instructiune_atribuire_semnal
instructiune_atribuire_variabile
instructiune_if
apel_procedura
instructiune_case
instructiune_loop
instructiune_next
instructiune_exit
instructiune_return
instructiune_nula
O instructiune de tip proces este o instructiune concurenta ce poate fi folosita in corpul unei arhitecturi sau al unui bloc. Partea declarativa defineste elementele ce pot fi folosite local in cadrul procesului. Trebuie notat ca variabilele definite intr-un proces pot fi folosite la pastrarea starii in cadrul unui model.
Un proces poate contine instructiuni de atribuire a valorilor unui semnal dat. Aceste instructiuni de atribuire formeaza impreuna ceea ce se numeste un driver pentru acel semnal. In mod normal ar trebui sa existe un singur driver pentru orice semnal considerat, astfel incit codul ce defineste valorile pe care le poate avea un semnal sa fie restrins in interiorul unui singur proces.
Un proces este activat initial in cadrul fazei de initializare a simularii. Procesul va executa toate instructiunile secventiale si apoi va relua executia de la inceput. Eventual, procesul va fi blocat in executia unei instructiuni de tip wait. Aceasta instructiune are urmatoarea sintaxa:
instructiune wait wait [ clauza-semnale ] [ clauza-conditii ] [clauza timeout];
clauza_semnale::=on lista_semnale
lista_semnale::= nume_semnal {, nume-semnal}
clauza_conditii::= until conditie
clauza_timeout::= for expresie_timp
Lista de semnale a unei instructiuni de tip wait specifica un set de semnale fata de care procesul respectiv este sensibil in timpul perioadei in care este blocat. Atunci cind se petrece un eveniment ce afecteaza oricare din aceste semnale – adica se schimba valoarea semnalului – procesul respectiv va iesi din blocare si va reevalua conditia. Daca conditia este adevarata, sau daca conditia este omisa, atunci executia va continua cu urmatoarea instructiune, altfel procesul se va reintoarce in starea de blocare in care se afla anterior.
Daca lista de semnale este omisa, atunci procesul respectiv este sensibil la valoarea tuturor semnalelor ce apar in expresia conditiei. Cu alte cuvinte, o modificare a valorii unui semnal dih expresia conditiei va duce la "trezirea" procesului respectiv si la reevaluarea conditiei. In cazul in care si lista de semnale si conditia sint omise dintr-un proces, este posibil ca acesta sa ramina blocat un timp nedefinit.
Daca lista de semnale apare explicit in antetul unui proces, atunci se presupune ca acel proces va contine ca ultima instructiune o instructiune de tip wait a carei lista de semnale este identica cu lista specificata in antet. In acest caz este posibil ca procesul sa nu mai contina nici o alta instructiune de tip wait explicita.
Iata in continuare un exemplu:
process (reset, clock)
variable state : bit := false;
begin
if reset then state := false;
elsif clock = true then state := not state;
end if q <= state after prop_delay; – wait implicit pentru modificarea valorii semnalelor reset si clock.
end process;
In faza de initializare a simularii procesul este activat si se executa atribuirea valorii initiale a semnalului q. Apoi procesul se va bloca la executia instructiunii wait implicite indicate in comentariu. La orice modificare a valorii unuia dintre semnalele reset sau clock, procesul va fi "trezit" si se va reevalua valoarea semnalului q.
Urmatorul exemplu descrie comportarea unui circuit de sincronizare numit "Muller _C". Acest dispozitiv se foloseste in construirea circuitelor logice asincrone. Iesirea dispozitivului este initial '0' si ramine la aceasta valoare pina cind ambele intrari ale circuitului sint ' 1'. In acest moment iesirea devine ' 1' si ramine la aceasta valoare pina cind ambele intrari ale circuitului devin '0', moment in care va lua valoarea '0' din nou. Descrierea VHDL este urmatoarea:
muller_c_2: process
begin
wait until a='1' and b='1';
q<='1';
wait until a='0' and b='0';
q<=’0’;
end process muller_c_2;
Din cauza ca acest proces nu contine o lista de semnale este necesara folosirea explicita a instructiunilor de tip wait. In ambele instructiuni wait, lista semnalelor este formata din a si b, derivate din conditia testata.
c)Instructiuni concurente de atribuire a valorilor unui semnal.
Adesea, un proces ce descrie un driver pentru un semnal contine o singura instructiune de atribuire a valorii acelui semnal. Limbajul VHDL ofera posibilitatea de a folosi o notatie scurta pentru acest lucru. Aceasta notatie poarta numele de instructiune concurenta de atribuire a valorilor unui semnal. Sintaxa este:
instructiune_concurenta_semnal ::=
[eticheta : ] atribuire_conditionala_semnal | [ eticheta : ] atribuire_selectiva_semnal
Pentru fiecare tip de atribuire concurenta a valorii unui semnal exista o varianta corespunzatoare cu lists de semnale in antetul unui proces.
a)Atribuire conditionala
Acest tip de atribuire a valorii unui semnal corespunde situatiei in care in cadrul unui proces se face atribuirea de valori unui semnal in interiorul unei instructiuni if. Sintaxa este:
atribuire_conditionals_semnal ::= destinatie <= optiuni forma_unda_conditionala ;
optiuni ::= [ guarded ] [ transport ]
forma_unda_conditionala :.= { forma_unda when conditie else } forma_unda
Daca se include cuvintul cheie *transport*, atunci atribuirile corespunzatoare din proces vor fi facute cu intirziere de tip transport.
b)Atribuire selectiva
O instructiune de atribuire selectiva a valorii unui semnal corespunde unei instructiuni de atribuire ce se executa in interiorul unei instructiuni de tip case. Sintaxa este:
atribuire_selectiva ::= with expresie select
destinatie <= optiuni forma_unda_selectie;
forma_unda_selectie ::= { forma_unda when posibilitati ,} forma_unda when posibilitati
posibilitati ::= posibilitate { I posibilitate }
Optiunile sint aceleasi ca si pentru atribuirea conditionala. Cu alte cuvinte, daca cuvintul rezervat *transport* apare, atunci atribuirile corespunzatoare din proces vor fi facute cu intirziere de tip transport.
Lista de semnale pentru instructiunea wait se determina in felul urmator: daca in instructiunea de atribuire selectiva nu apare nici o referinta explicita la vreun semnal, atunci lista de semnale pentru acea instructiune wait va fi vida. In caz contrar, lista de semnale a instructiunii wait va contine acele semnale care au fost referite in interiorul instructiunii de atribuire selectiva.
Iata in continuare un exemplu de instructiune de atribuire selectiva:
with alu_function select
alu-result <= op1 + opt2 when alu_add I alu-incr,
opl – op2 when alu_substract,
opl and opt when alu_and,
opl or opt when alu_or,
opt and not opt alu mask;
In acest exemplu, valoarea semnalului alu function este folosita pentru a selecta care dintre instructiunile de atribuire se va executa. Instructiunea contine in lista ei de semnale urmatoarele semnale alu function, op 1, opt, astfel incit oricare dintre aceste trei semnale isi va schimba valoarea, instructiunea de atribuire selectiva isi va relua executia.
2.2.8.Organizare model
In acest capitol se va arata cum se poate scrie o descriere completa VHDL a unui sistem digital.
a)Unitati si biblioteci de design
Descrierile VHDL se scriu intr-un "design file" , se analizeaza cu un compiler care le introduce intrun "design library". O biblioteca este formata din mai multe unitati ("library units"). Unitatile de biblioteca primare sint declarathle de unitati, de package-uri si de configuratie. Unitatile de biblioteca secundare sint corpurile de package-uri si arhitecturile. Aceste unitati depind de specificarea interfetei lor, care se face in unitati primare. Deci unitatile de biblioteca primare trebuie analizate inaintea oricarei unitati secundare corespunzatoare.
Un fisier de design ("design file") contine mai multe unitati de biblioteca. Sintaxa este:
fisier_design ::= unitate design {unitate design }
unitate_design ::= clauza_context unitate_biblioteca
clauza_context ::={context }
context ::= clauza biblioteca I clauza use
clauza biblioteca ::= library lista nume logic ;
lista_nume_logic ::= nume logic { , nume_logic }
unitate_biblioteca ::= unitate primara unitate secundara
unitate_primara ::=declaratie_entitate declaratie-configuratie declaratie_package
unitate_secundara ::= arhitectura I corp_package
Bibliotecile sint referite folosind identificatori numiti NUME LOGICE. Numele logic trebuie tradus de sistemul de operare gazda in alt nume, dependent de implementare.
De exemplu, bibliotecile pot fi niste fisiere de baze de date, numele logic reprezentind numele unui fisier de baze de date. Unitatile dintr-o biblioteca pot fi referite prin prefixarea numelui for cu numele logic al bibliotecii. "ttl lib.ttl_10" se poate referi h unitatea "ttl_10" din biblioteca "ttl lib".
Clauza care precede fiecare unitate specifica ce alte biblioteci sau package-uri sint folosite. Scopeul numelor specifcate in clauza de context se intinde pina la sfirsitul unitatii de design.
Exista 2 biblioteci speciale care sint disponibile implicit tuturor unitatilor, si deci nu a necesar sa fie numite in clauze de context. WORK este biblioteca de lucru, in care vor fi plasate de catre analizor unitatile de design curente. Intr-o unitate se pot referi unitatile analizate anterior folosind numele bibliotecii "work". STD este o biblioteca de design care contine package-urile "standard" si "textio". "Standard" contine toate tipurile si functiile predefinite. Toate elementele din acest package sint implicit vizibile, deci nu e necesara clauza use pentru a le accesa.
b)Configuratii.
Am aratat cum se poate declara o specificare de componente intr-o descriere structurala, si cum se pot crea instante ale componentelor. Legatura dintre o entitate si componente se face prin declararea de configuratie. Intr-o astfel de declaratie se pot specifics constantele generice actuale pentru componente si blocuri.
Sintaxa este:
declarare_configuratie ::=configuration identificator of nume_entitate is
parte_declarativa_configuratie
configuratie bloc
end [ nume_configuratie ];
parte_declarativa_configuratie ::={ declarare configuratie }
declarare_configuratie ::= clauza_use
configuratie_bloc ::=
for specificare bloc
{ clauza_use }
{ conf iguratie }
end for ;
specificare_bloc ::= nume-arhitectura | eticheta_instructiune_bloc
configuratie ::= configuratie_bloc | configuratie_componenta
configuratie_componenta ::= for specificare_componenta
[ use legatura ; ]
[ configuratie_bloc ]
end for ;
specificare_componenta ::= lista_instantieri nume_componenta
lista_instantieri ::= eticheta_instantiere { , eticheta_instantiere }
others all
legatura ::=
aspect_entitate
[ map_generic ]
[ map port ]
aspect_entitate :.= entity nume_entitate [(identificator arhitectura)]
configuration nume-configuratie
open
map-generic ::= generic map (lista asociatii generic)
map port ::= port map (lists asociatii port)
Configuratia unei astfel de arhitecturi este:
configuration test_config of processor is
use work.processor_types. all
for block_structure
… … … … … … … …
elemente configuratie
… … … … … … … …
end for
end test_config;
In acest exemplu, continutul package-ului "processor types" din biblioteca de lucru curenta este vizibil, iar configurarea de bloc se refera la arhitectura "block structure" a entitatii "processor".
Se pot configura submodulele dintr-o arhitectura. Acestea contin blocuri si instante de componente. Iata o configurare de blocuri pentru arhitectura prezentata mai sus:
configuration test_config of processor is
use work.processor_types. all
for block_structure
for control_unit
… … … … … … … …
elemente_configuratie
… … … … … … … …
end for;
for data_path
… … … … … … … …
elemente_configuratie
… … … … … … … …
end for;
end_for;
end test_config;
Daca submodulul este o instanta a unei componente, configuratia componentei este folosita pentru a lega o entitate de instanta componentei.
Fie blocul "data-path", care contine o instanta a componentei "alu", declarata astfel:
data_path: block port(lista porturi);
port map (lista asociatii);
component alu
port(function: in alu_function; opl,op2: in bit_vector_32; result: out bit-vector-32);
end component ;
… … … … … … …
alte declaratii pentru data_path
… … … … … … …
begin
data_alu:alu
port map(function=>alu fn,opl=>bl,op2=>b2, result=>alu r);
… … … … … … …
alte declaratii pentru data_path
… … … … … … …
end block data_path;
Fie biblioteca "project cells" cu entitatea "alu cell" definita astfel:
entity alu_cell is
generic(width : positive);
port( function code : in alu_function; operand1, operand2 : in bit_vector(width-1 downto 0);
result : out bit_vector(width-1 downto 0); flags : out alu_flags); end alu_cell;
Entitatea are o arhitectura numita "behaviour". Aceasta entitate poate fi legata cu instanta "alu" daca porturile operanzi si rezultat pot fi puse in corespondenta cu porturile componentei, porturile "flags" putind fi lasate neconectate.
for data_path
for data_alu:alu
use entity project cells.alu_cell (behaviour)
generic map (width=>32)
port map (function code=>function, operandi=>opl, operand2=>op2, result=>result, flags=> open );
end for ;
… … … … … … …
alte declaratii pentru data path
… … … … … … …
end for ;
Daca biblioteca include si o configuratie "alu-struct" pentru o arhitectura a entitatii "alu_cell" atunci configuratia de bloc arata astfel:
for data_path
for data alu:alu
use configuration project_cells.alu_struct
generic map (width => 32)
port map (function code=>function, operandi=>opl, operand2=>op2,
result=>result, flags=> open );
end for ;
… … … … … … …
alte declaratii pentru data path
… … … … … … …
end for ;
c)Exemplu complet
Prezentam in continuare un fisier de design pentru exemplul din capitolul 1. Fisierul contine mai multe unitati de design care sint analizate in ordine. Prima unitate reprezinta declararea entitatii "count2". Urmeaza 2 entitati secundare – arhitecturi ale entitatii "count2". Urmeaza o alta deeclarare de entitate, un test pentru numarator ("test bench"). Urmeaza o unitate secundara, care reprezinta descrierea structurala a bancii de test. Urmeaza o declarare de configuratie, care face referiri la unitatile de bibliteca definite anterior, din biblioteca de lucru si deci nu a nevoie de clauza de context. Se observa ca entitatea "cunt2" este referita in cadrul configuratiei cu numele "work. count2". Urmeaza o declrare de configuratie cu arhitectura "structure" a entitatii "count2". Ea utilizeaza 2 unitati("misc.t flipflop", "misc. inverter") din biblioteca separata "misc", deci e necesara o clauza de biblioteca.
Aceasta descriere cuprinde toate unitatile intr-un singur fisier. Dar a posibil ca unitatile de design sa fie impartite pe mai multe fisiere ( cazul extrem fiind o unitate intrun fisiewr). Daca exista mai multe fisiere, ele trebuie compilate in ORDINEA corecta, si trebuie recompilate fisierele dependente de o unitate in care s-au operat modificari.
–unitate primara: declaratia entitatii count2
entity count2 is
generic(prop_delay: Time := 10 ns);
port(clock: in bit; q1,q0 . out bit);
end count2;
–unitate secundara: architectura-comportament pentru count2
architecture behaviour of count2 is
begin count_up: process (clock)
variable count-value: natural := 0;
begin if clock='1' then
count value:=(count value+1) mod4;
q0<=bit'val(count_value mod 2) after prop_delay;
q1<=bit'val(count_value/2) after prop delay;
end if ; end process count_up;
end behaviour ;
–unitate secundara: architectura-structura pentru count2
architecture structure of count2 is
component t_flipflop port(ck: in bit; q: out bit);
end component;
component inverter port(a: in bit;y: out bit);
end component;
signal ffo,ffi,inv_ff0: bit;
begin
bit-0: t-flipflop port map (ck=>clock, q=>ff0);
inv: inverter port map (a=>ff0, y=>inv ff0);
bit 1:t flipflop port map (ck=>inv-ff0, q=>ffl);
q0<=ff0; q1<=ff1;
end structure ;
–unitate primara: declaratia entitatii de testare
entity test _count2 is
end test count2;
–unitate secundara: arhitectura-structura pentru test
architecture structure of test_count2 is
signal clock,q0,q1: bit;
component count2
port(clock: in bit; q1,q0: out bit);
end component;
begin counter : count2
port map (clock => clock, q0 => q0, q1 => q1);
clock driver : process
begin wait for 100 ns;
end process clock_driver;
end structure;
– unitate primara: configuratie cu architectura comportament
configuration test count2 behaviour of test_count2 is
for structure pentru test_count2
for counter : count2
use entity work.count2(behaviour);
end for;
end for ;
end test_count2_behaviour;
– unitate primara: configuratie cu architectura
structura library misc;
configuration test_count2_structure of test_count2 is
for structure –pentru test_count2
for counter : count2
use entity work.count2(structure);
for structure –pentru count_2
for all :t_flipflop
use entity misc.t_flipflop(behaviour);
end for;
for all :inverter
use entity misc.inverter(behaviour);
end for;
end for;
end for;
end for;
end test_count2_structure;
2.3 Analizor lexical/sintactic (parser VHDL)
Modulul pentru care am analizat metodele de testare este programul care parseaza fisierul sursa si verifica daca e scris corect (conform gramaticii limbajului VHDL). Daca se gaseste o eroare programul semnaleaza linia respectiva din sursa si isi intrerupe functionarea. Daca este corect descris, se trece la pasul 5.
Prima faza a constat in descrierea gramaticii limbajului VHDL, pentru care am folosit programele Lex & Yacc:
Lex-> citeste descrierile (tokenii / cuvintele cheie ale limbajului si le semnaleaza )
se realizeaza analizorul lexical (yylex),fisierul lex.yy.c
=>fisierul VHDL_last.lex
Yacc-> citeste gramatica descrisa
(yyparse)
fisierele y.tab.c si y.tab.h
=>fisierul VHDL_last.yacc
Exemplu – pentru blocul de instructiuni din sursa:
entity andg is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_ulogic;
out1 : out std_ulogic);
end andg;
Analizorul lexical recunoaste cuvintele cheie : entity, is, generic, port,time, end
care sunt declarate in VHDL_last.lex
…..
static el_mc tab_mc []=
{
{"generic" ,t_GENERIC},
{"is" ,t_IS},
{"port" ,t_PORT},
… etc
}
….
Fiecarui cuvant cheie din gramatica limbajului i se asociaza un token t_GENERIC, t_IS.
Fisierul VHDL_last.yacc primeste tokenii pasati de VHDL_last.lex si verifica gramatica –
exemplu ->fiecare declarare de entitate trebuie se faca prin sintaxa:
entity_decl : entity_start entity_decl_1 entity_decl_2 entity_decl_3 entity_decl_4
t_END entity_optional_end entity_decl_5 t_Semicolon
;
entity_start : t_ENTITY t_Identifier t_IS ;
entity_decl_5 :
| t_Identifier
| ' ' t_Identifier
;
entity_optional_end :
| t_ENTITY ;
entity_decl_4 :
| t_BEGIN concurrent_stats {DEBUG_HERE};
entity_decl_3 :
| entity_decl_3 entity_decl_6 {DEBUG_HERE};
entity_decl_6 : entity_decltve_item {DEBUG_HERE};
entity_decl_2 :
| t_PORT interf_list t_Semicolon ;
entity_decl_1 :
| t_GENERIC interf_list t_Semicolon ;
Daca descrierea entitatii in program nu este conform gramaticii descrise in VHDL_last.lex / VHDL_last.yacc
se semnaleaza eroare.
Fisierul main.c primeste ca argument in linia de comanda numele fisierului sursa, si apeleaza functiile
de parsare din VHDL_last.lex, si VHDLlast.yacc
Makefile compileaza toate sursele si creeaza fisierul parser_vhdl.
Programul se apeleaza astfel:
./parser_vhdl nume_fisier_intrare
sau
./parser_vhdl nume_fisier_intrare nume_fisier_iesire
nume_fisier_iesire – este parametru optional, daca se doreste da output-ul sa fie intr-un fisier.
In cazul in care se gaseste o eroare in sursa se tipareste
Eroare: syntax error
Metode de testare ale parserul VHDL
3.1 Notiuni teoretice despre testare, obiectul testarii si strategii de testare pentru parserul VHDL
3.1.1 Notiuni teoretice despre testare, obiectul testarii
Testarea analizorului lexical/sintactic presupune conceperea si realizarea de teste care sa identifice erori in functionare, sau module care nu sunt definite corespunzator.
Un test bun are o probabilitate mare de a detecta o eroare care nu a fost gasita pana in acel moment.
Obiectivul testarii programelor este proiectarea de teste care sa detecteze si sa semnaleze diferite clase de erori intr-un timp cit mai scurt si cu un effort cit mai mic.
Avantaje ale testarii analizorului lexical/ sintactic pot si considerate urmatoarele:
verificarea functiilor implementate de program in raport cu descrierea gramaticii VHDL
verificarea satisfacerii cerintelor de performanta impuse prin gramatica limbajului VHDL
Testarea analizorului poate pune in evidenta anumite defecte dar nu poate garanta absenta oricarei erori.
3.1.2 Strategia de testare pentru analizorul lexical/sintactic si schema de verificare
Testarea analizorului lexical/sintactic este planificata si respecta strategiile de testare a aplicatiilor software prezentate mai jos :
– testarea incepe la nivel de modul si se extinde pina la nivel de aplicatie
la diverse momente de timp se folosesc tehnici de testare diferite
testarea si debgging-ul sunt doua activitati diferite. Debugger-ul trebuie sa fie inclus in orice strategie de testare.
Testarea de obicei se face intr-o ordine inversa:
Testarea modulelor
aceasta se reallizeaza prin folosirea metodelor tip cutie alba
se exerseaza caile specifice ale structurilor de control ale modulelor, pentru acoperirea maxima a testelor si pentru minimizarea numarului de erori
Testarea in vederea integrarii
exista atat probleme de verificare cat si de constructie a programelor
se folosesc pe scara larga testele de tip black-box
se utilizeaza deasemenea si tehnologiile cutie alba pentru a se asigura acoperirea principaleor cai e control
Validarea testarii
testarea criteriilor de validare (stabilite in timpul analizei cenintelor)
se aplica tehnici de tip cutie neagra
Testarea la nivel de sistem
este parte din ingineria de sistem
este de verificat integrarea software-ului cu ate componente din sistem
Pentru testarea analizorului lexical/sintactic pentru gramatica VHDL schema generala a verificarii urmareste urmatorii pasi:
1. Sintaxa generala a limbajului VHDL (adaptarea metodei de testare tip cutie alba)
Cuvinte cheie, module sau arhitecturi care nu sunt definite si detaliate in analizorul lexical/sintactic (metoda testarii conditiilor, metoda testarii tip cutie neagra)
Conceperea de fisiere de test pentru testarea automata a programelor. (testarea prin comparatie).
3. 2 Metode de testare pentru analizorul lexical / sintactic
Avand in vedere ca Analizorul lexical/ sintactic este o faza esentiala proiectului, este necesar sa fie testat foarte bine, pentru a se evita erori grave in etapele urmatoare. E indicat sa se dezvolte programe de test foarte complexe, pentru a nu se lasa sa neobservate erori sau definitii sumare pentru gramatica limbajului VHDL.
3.2.1 Metoda de testare tip cutie alba ( pentru testarea analizorului lexical/ sintactic )
Metoda de testare tip cutie alba testeaza structurile de control ale programelor.
Am scris un fisiere de test care testeaza si verifica :
recunosterea tuturor cuvintelor cheie ale limbajului VHDL
deciziile logice exersate in anumite situatii
3. recunosterea diferitelor sintaxe pentru modul, arhitectura – in functie de parametri optionali
Functionare corecta a programului in conditiile de testare enumerate mai sus asigura detectarea erorilor de sintaxa, sau tratarea superficiala a acesteia, in cazul declaratiilor complexe.
Ca exemplificare, revin asupra exemplului ales in sectiunea de prezentare a proiectului :
entity_decl : entity_start entity_decl_1 entity_decl_2 entity_decl_3 entity_decl_4
t_END entity_optional_end entity_decl_5 t_Semicolon
;
entity_start : t_ENTITY t_Identifier t_IS ;
entity_decl_5 :
| t_Identifier
| ' ' t_Identifier
;
entity_optional_end :
| t_ENTITY ;
entity_decl_4 :
| t_BEGIN concurrent_stats {DEBUG_HERE};
entity_decl_3 :
| entity_decl_3 entity_decl_6 {DEBUG_HERE};
entity_decl_6 : entity_decltve_item {DEBUG_HERE};
entity_decl_2 :
| t_PORT interf_list t_Semicolon ;
entity_decl_1 :
| t_GENERIC interf_list t_Semicolon ;
Sunt analizate toate cazurile in care poate fi folosita sectiunea modul, cu lista de parametri completa. In cazul in care ar fi lipsit o entity_decl, daca intr-o sursa analizata ar fi fost folosit cu o lista de parametri extinsa, s-ar fi semnalat eroare.
3.2.2 Metoda testarii conditiilor
Testele concepute pentru metoda testarii conditiilor sunt dezvoltate din testele anterioare. Isi propun sa exerseze conditiile logice ale unui modul.
Teoretic, pentru aceasta metoda se definesc urmatoarele tipuri de conditii logice:
expresii relationale Acestea sunt de tipul (E1 op E2), unde E1 si E2 sunt expresii aritmetice;
conditii simple. Acestea sunt variabile booleene sau expresii relationale, eventual precedate de operatorul NOT
conditii compuse. Acestea sunt compuse din doua sau mai multe conditii simple, operatori booleeni si paranteze
expresii booleene. Conditii fara expresii relationale
De exemplu, testul gates.vhd, isi propune sa testeze recunoasterea modului, entitatilor, arhitecturilor corect definit : implementare de porti logice : SI, SAU, SAU EXCLUSIV. In caz de eroare se semnaleaza. Se analizeaza expresiile relationale, conditiile simple si compuse, expresiile booleene in gramatica VHDL, si situatiile mai putin folosite, de exemplu, descrierea portii sau exxclusiv :
library IEEE;
use IEEE.std_logic_1164.all;
entity xorg is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end xorg;
architecture only of xorg is
begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 xor in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;
3.2.3 Metoda metoda testarii tip cutie neagra
In mod teoretic, testarea tip cutie neagra urmareste sa descopere urmatoarele tipuri de erori:
1 functii incomplete sau lipsa
2. erori de interfata
3. erori in structurile de date sau de acces la bazele de date externe
4. erori de initializare sau de terminare a programelor
Aceste tipuri de erori sunt cautate si semnalate si in testele pentru analizorul lexical / sintactic pentru limbajul VHDL. De exemplu,
Se urmareste descoperirea acestor erori – parametri lipsa pentru functii sau functii nedefinite, erorile de definire, de structuri de date sau de terminare a programului.
Testul elaborat este bvadd.vhd – exemplu de supraincarcare operator + pentru vectori de biti, care urmareste detectarea erorilor semnalate mai sus, de exemplu, testarea arhitecturii :
– test architecture
ENTITY test IS END;
USE work.bit_vector_ops.ALL;
ARCHITECTURE test_add OF test IS
BEGIN
PROCESS
BEGIN
ASSERT "000" + "000" = "000";
ASSERT "000" + "001" = "001";
ASSERT "001" + "001" = "010";
ASSERT "110" + "111" = "101";
ASSERT "011" + "1" = "100";
ASSERT "011" + "1000001" = "100";
ASSERT false REPORT "Test complet";
WAIT;
END PROCESS;
END;
3.2.4 Metoda testarii prin comparatie
Teoretic, pentru testarea unui sistem prin metoda testarii prin comparatie, pentru anumite aplicatii critice se pot dezvolta versiuni independente de software care sa fie testate apoi prin comparatie.
Atunci cind exista diferente intre iesirile celor doua versiuni de implementare se analizeaza fiecare in parte pentru a determina daca exista defecte.
Pentru analizorul lexical / sintactic, propun ca fisierele de test sa fie testate cu programe specializate si comparate astfel cu parserul la care ne referim.
Sunt folosite aceste doua fisiere, la care ne referim : bvadd.vhd si gates.vhd.
3.3. Fisiere test
Am listat fisierele de test, la care am facut referire in capitolul 3, ca implementare a tehnicilor de testare.
3.3.1 Fisierul gates.vhd
– implementare de porti logice : SI, SAU, SAU EXCLUSIV
–––––––––––––––––––––––-
––––––––––––––––––––––––
– declararea de componente
––––––––––––––––––––––––
library IEEE;
use IEEE.std_logic_1164.all;
package gates is
component andg
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_ulogic;
out1 : out std_ulogic);
end component;
component org
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end component;
component xorg
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end component;
end gates;
––––––––––––––––––––––––
– poarta AND
––––––––––––––––––––––––
library IEEE;
use IEEE.std_logic_1164.all;
entity andg is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_ulogic;
out1 : out std_ulogic);
end andg;
architecture only of andg is
begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 and in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;
––––––––––––––––––––––––
– poarta SAU
––––––––––––––––––––––––
library IEEE;
use IEEE.std_logic_1164.all;
entity org is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end org;
architecture only of org is
begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 or in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;
––––––––––––––––––––––––
– poarta SAU EXCLUSIV
––––––––––––––––––––––––
library IEEE;
use IEEE.std_logic_1164.all;
entity xorg is
generic (tpd_hl : time := 1 ns;
tpd_lh : time := 1 ns);
port (in1, in2 : std_logic;
out1 : out std_logic);
end xorg;
architecture only of xorg is
begin
p1: process(in1, in2)
variable val : std_logic;
begin
val := in1 xor in2;
case val is
when '0' =>
out1 <= '0' after tpd_hl;
when '1' =>
out1 <= '1' after tpd_lh;
when others =>
out1 <= val;
end case;
end process;
end only;
3.3.2 Fisierul bvadd.vhd
– Examplu de supraincarcare operator + pentru vectori de biti
–––––––––––––––––––––
PACKAGE bit_vector_ops IS
– supraincarcarea operatorul + pentru vectori de biti
– rezultatul va fi de acelasi tip ca si operandul din stanga
FUNCTION "+"(left, right : bit_vector) RETURN bit_vector;
END;
PACKAGE BODY bit_vector_ops IS
FUNCTION "+"(left, right : bit_vector) RETURN bit_vector
IS
ALIAS left_val : bit_vector(left'length DOWNTO 1) IS left;
ALIAS right_val : bit_vector(right'length DOWNTO 1) IS right;
VARIABLE result : bit_vector(left_val'RANGE);
VARIABLE carry : bit := '0';
VARIABLE right_bit : bit;
VARIABLE left_bit : bit;
BEGIN
FOR i IN result'reverse_range LOOP
left_bit := left_val(i);
IF (i <= right_val'high) THEN
right_bit := right_val(i);
ELSE
right_bit := '0';
END IF;
result(i) := (left_bit XOR right_bit) XOR carry;
carry := (left_bit AND right_bit)
OR (left_bit AND carry)
OR (right_bit AND carry);
END LOOP;
RETURN result;
END "+";
END bit_vector_ops;
– test architecture
ENTITY test IS END;
USE work.bit_vector_ops.ALL;
ARCHITECTURE test_add OF test IS
BEGIN
PROCESS
BEGIN
ASSERT "000" + "000" = "000";
ASSERT "000" + "001" = "001";
ASSERT "001" + "001" = "010";
ASSERT "110" + "111" = "101";
ASSERT "011" + "1" = "100";
ASSERT "011" + "1000001" = "100";
ASSERT false REPORT "Test complet";
WAIT;
END PROCESS;
END;
4.Descrierea program
4.1 Introducere
Proiectul consta in realizarea unui program in C# care analizează corectitudinea sintaxei pentru fișiere VHDL, care descriu circuite și simularea circuitelor descrise.
VHDL (prescurtare de la Vhsic (Very High Speed Integrated Circuits) HDL (Hardware Description Language)) este un limbaj de programare care descrie structuri hardware. (de exemplu un circuit integrat care trebuie construit este descris in acest limbaj de programare, sursa este prelucrata, optimizata prin programe CAD, circuitul transformat in operatii elementare si apoi construit fizic)
Etapele principale ale functionarii proiectului :
1. se scrie sau se importă descrierea structurii
( fisierul sursa al descrierii VHDL este scris intr-un richtextBox, sau se poate deschide un fisier existent)
2. se realizează verificarea corectitudinii sursei
( cu ajutorul programelor Lex&Yacc se parseaza fisierul si se verifica daca e scris conform gramaticii limbajului vhdl)
3. dacă se gasește o eroare se semnalează și se revine la pasul 1
( in cazul detectarii unei erori, se afiseaza mesajul Syntax error si linia la care e gasita eroarea)
4. daca sursa este corect descrisa se trece la pasul 5
5. se seteaza valorile semnalelor de intrare
( intr-un form se afiseaza semnalele de intrare, citite din fisierul sursa si utilizatorul care testeaza sursa, alege valorile semnalelor de intrare)
6. se simulează funcționarea entității
( intr-un form separat, apar valorile intiale si dependenta semnalelor de iesire in functie de cele de intrare. )
4.2 Programul Principal
Programul principal este FormPRINCIPAL.cs.
Este fereastra care se deschide la execuția programului:
Conține meniul – care se observă în partea de sus a imaginii și richtextBox-ul în care se încarcă fișierul.
La lansarea programului, acesta este inactiv.
private void Form1_Load(object sender, System.EventArgs e)
{
rtbPrincipal.Enabled=false;
}
Meniul programului este detaliat în subcapitolele următoare.
Meniul File
Meniul File permite operațiile de deschidere – închidere fișier, salvare, crearea unui nou fișier și inchiderea programului.
4.3.1.1 New
Selectarea meniului New activează richtextBoxului rtbPrincipal.
private void menuItemNew_Click(object sender, System.EventArgs e)
{
/*devine activ richtextBoxul */
rtbPrincipal.Enabled=true;
rtbPrincipal.Text="";
this.Text="New File";
file="";
actiune=1;
}
Se poate scrie fișierul sursă, numele ferestrei principale devine New File.
4.3.1.2 Open
Selectarea submeniului Open deschide fereastra Open VHDL FIle Dialog care permite alegerea fișierului ce urmează a fi analizat. (test5.vhd de exemplu ).
Prin apăsarea butonului Open textul din fișierul respectiv este încărcat in rtbPrincipal, care devine activ.
s.Insert(s.Length,"\n");
StringBuilder sb = new StringBuilder();
while (s != null)
{
sb.Append(s);
sb.Insert(sb.Length,"\r\n");
s = sr.ReadLine();
}
sr.Close();
rtbPrincipal.Enabled=true;
rtbPrincipal.Text = sb.ToString();
4.3.1.3 Save
Submeniul Save permite salvarea textului scris în richtextBoxul rtbPrincipal. Prin selectarea acestui submeniu, se deschide o ferestră, unde se introduce numele fișierului sub care se dorește salvarea textului.
Se face controlul asupra richtextBoxul, dacă este vid, se afișează mesaj de eroare:
După salvarea fișierului se afișează numele sub care a fost salvat în titlul ferestrei principale.
Dacă fișierul există se salvează modificările cu același nume. Ferestra Save File VHDL apare doar dacă este fișier nou creat( cu meniul New ).
4.3.1.3 Close
Submeniul Close dezactivează richtextBoxul. Se formatează toate datele, astfel se poate relua execuția unui program.
private void menuItem2_Click(object sender, System.EventArgs e)
{
rtbPrincipal.Text="";
rtbPrincipal.Enabled=false;
}
4.3.1.4 Quit
Submeniul Quit închide aplicația :
private void menuItemQuit_Click(object sender, System.EventArgs e)
{
this.Close();
}
Meniul Edit
Meniul conține operații asupra textului din rtbPrincipal: Cut, Copy, Paste, Select All.
4.3.2.1Cut
Permite selectarea unei porțiuni de text și salvarea în clipboard, și stergerea din rtbPrincipal.
private void menuItem1_Click(object sender, System.EventArgs e)
{
rtbPrincipal.Cut();
}
4.3.2.2. Copy
Selectarea submeniului Copy permite salvarea unei porțiuni de text în clipboard.
4.3.2.3 Paste
Selectarea submeniului Paste copiază în rtbPrincipal textul salvat în clipboard.
4.3.2.4Select All
Select All selectează întreg conținutul richtextboxului.
4.3.3. Meniul Compile
4.3.3.1 Compile
Meniul Compile este o partea fundamentală a programului. Prin selectarea acestui meniu se realizează verificarea corectitudinii fișierului sursă VHDL.
Mai intâi se verifică dacă fișierul este salvat în prealabil.
if(salvat==0)
{
MessageBox.Show("Fisierul trebuie salvat","Eroare");
}
"salvat " este un flag inițializat în funcția de salvare a fișierului, discutată anterior.
Se verifică de asemenea dacă există un fișier deschis – din greșeală se poate selecta direct meniul compile, fără deschidea unui fișier, sau crearea altuia nou.
Se semnalează și această eroare :
Dacă există fișierul și este salvat, atunci se pasează numele formului FormCompile care compilează fișierul:
FormCompile x=new FormCompile();
/*
initializarea variabilei FILENAME: numele fisierului care trebuie compilat
*/
x.FileName=FileX;
x.fisier_salvat="SALVAT";
/*
se deschide formul FormCompile
*/
x.ShowDialog();
În fereastra FormCompile se verifică sintaxa pentru fișierul salvat.
Formul conține mai multe elemente :
un textbox cu numele fișierului
butonul "Verifica sintaxa"
un textbox în care se afișează eroarea -sau se semnalează că nu s-a găsit nici o eroare
butoanele "Ok " și "Cancel"
La deschiderea formului au loc următoarele evenimente :
se verifica existența fișierului executabil parser_VHDL (acest fișier este creat din VHDL_last.lex și VHDL_last.yacc comentate în capitolul anterior).
if(!File.Exists("parser_VHDL.exe"))
{
label1.Text="Nu exista fis parser_VHDL";
}
se crează un fișier temporar cu numele TEST si data + ora la care se compilează fișierul
fisier_iesire="TEST"+"_"+DateTime.Now.Day.ToString()+"_"+DateTime.Now.Month.ToString()+"_"+DateTime.Now.Year.ToString()+"_"+DateTime.Now.Hour.ToString()+"_"+DateTime.Now.Minute.ToString()+".txt";
se apelează un proces cu următorii parametri :
FileName – numele fișierului care se compilează
fisier_iesire – fișierul temporar anterior în care se scrie eroarea, dacă este gasită la compilare.
/*
pentru compilare se apeleaza un proces parser_VHDL fisier de compilat fisier de iesire
*/
if(!File.Exists(FileName))
{
label1.Text="Nu exista fisierul de test "+FileName;
}
else
{
label1.Text="Fisier : "+FileName;
string string_compilare="";
string_compilare=" "+FileName+" "+fisier_iesire;
System.Diagnostics.Process.Start("parser_VHDL",string_compilare);
}
Prin apăsarea butonului "Verificare sintaxa" se caută eroarea în fișierul temporar.
Pt cazul ales, există linia : XXarchitecture Eroare: syntax error. Se caută în fișier eroarea și se reține tokenul eronat : XXarchitecture, care se tipărește în textbox:
if(eroare!=-1)
{
label2.Text=s.ToString();
eroareX=label2.Text;
}
else
{
eroareX="";
}
Se semnalează eroarea :
Prin apăsarea butonului "Ok" – se revine la formul anterior și se pasează valoarea tokenului eronat în variabila eroareX – eroareX=label2.Text;
4.4 Secțiunea de simulare pentru circuitele descrise
4.4.1. Meniul Simulate
Simularea este procesul de interpretare a comportamentului circuitelor logice : pentru aplicarea unor semnale de intrare se calculează semnalele de ieșire.
4.4.1.1 Simulare Porți Logice
Fundamentul circuitelor sunt porțile logice – seturi de componente elecronice elementare care combinate sunt capabile să execute operații complexe logice și aritmetice. Poarta logică este realizarea fizică a unei expresii boolene simple.
Acestă secțiune din proiect permite simularea porților logice :
Poarta logică OR are reprezentarea :
și tabelul de adevăr :
Poarta logică AND are reprezentarea :
și tabelul de adevăr :
Poarta logică NAND are reprezentarea :
și tabelul de adevăr :
Poarta logică NOR are reprezentarea :
și tabelul de adevăr :
Poarta logică XOR are reprezentarea :
și tabelul de adevăr :
Programul permite selectarea tipului de poarta logică și semnalele de intrare(Intrare 1, Intrare 2), și calculează semnalul de ieșire. Ca exemplu, poarta logică AND:
4.4.1.2 Simulare HalfAdder
Half Adder este circuitul care realizează adunarea în Unitatea logico- aritmetică (ALU).
O unitate care adună două valori binare se numește half adder, și cea care adună trei biți – full adder.
Un circuit half adder însumează două valori binare rezultând SUM si CARRY, ambele valori booleene.
Pentru fișierul fisier_test_HalfAdder, schema circuitului este aceasta: două semnale de intrare(Intrare 1 , Intrare 2 ), 3 porți logice AND, si una OR. La ieșire sunt semnalele SUM, CARRY
Prin modificarea semnalele de intrare se urmărește evoluția semnalelor până la ieșire.
Acest exemplu va fi analizat mai consistent în capitolul următor, unde se prezintă un exemplu de funcționare.
4.4.1.3 Simulare FullAdder
Full Adder este circuitul care realizează adunarea a trei biți: este realizat din două blocuri HalfAdder și o poartă OR.
Schema logică pentru acest circuit este următoarea:
Trei intrari (A,B,CARRY_IN), blocurile HalfAdder, și poarta OR. Ieșirile Carry și SUM.
Funcționarea programului
( analiza utilizării programului pentru fișierul care descrie entitatea FULL ADDER. )
5.1 Noțiuni teoretice
Pentru a înțelege funcționarea programului, revin la exemplul prezentat în secțiunea introductivă Ș
entitatea FULL ADDER, cu trei intrari :
A
B
CARRY_IN
și iesirile
SUM
CARRY.
Descrierea VHDL pentru aceasta entitate este urmatoarea (fisierul – fisier_test_FullAdder.vhd)
entity FULLADDER is
port (A,B, CARRY_IN: in bit; – SEMNALELE DE INTRARE
SUM, CARRY: out bit); – SEMNALELE DE IESIRE
end FULLADDER;
architecture STRUCT of FULLADDER is
– ARHITECTURA REPREZINTA DESCRIEREA COMPORTAMENTULUI ENTITATII + SI A COMPONENTELOR
component HALFADDER
port (A, B : in bit;
SUM, CARRY : out bit);
end component;
component ORGATE
port (A, B : in bit;
RES : out bit);
end component;
signal W_SUM, W_CARRY1, W_CARRY2: bit;
begin
–SE INITIALIZEAZA SEMNALELE SI LEAGA SUBCOMPONENTELE ––ENTITATII INTRE ELE, CONFORM SCHEMEI.
MODULE1: HALFADDER
port map( A, B, W_SUM, W_CARRY1 );
MODULE2: HALFADDER
port map ( W_SUM, CARRY_IN,
SUM, W_CARRY2 );
MODULE3: ORGATE
port map ( W_CARRY2, W_CARRY1, CARRY );
end STRUCT;
entity HALFADDER is
port(
A, B: in bit;
SUM, CARRY: out bit);
end HALFADDER
architecture RTL of HALFADDER is
begin
SUM <= A xor B;
CARRY <= A and B;
end RTL;
5.2 Deschiderea fișierului
Din meniul File – Open se alegerea descrierea structurii FullAdder :
Se încarcă fișierul fisier_test_FullAdder.vhd. Dacă se fac modificări, se salvează (File – Save). Se poate redacta direct fișierul, prin deschidea unui nou fișier (File – New).
IMPORTANT :Fișierul trebuie salvat, inainte de compilare, altfel se semnalează eroare.
5.3 Compilarea fișierului
Se selectează meniul Compile și se verifică sintaxa fișierului fisier_test_fulladder.vhd (se apasă butonul Verificare sintaxa)
5.4 Simularea circuitului FullAdder
Din meniul Simulate se alege vizualizarea simulării pentru FullAdder.
Se poate testa funcționarea circuitului pentru toate valorile de intrare pentru A,B,CARRY_IN.
6. Concluzii
Programul în C# care analizează corectitudinea sintaxei pentru fișiere VHDL și simularea circuitelor elementare descrise a fost realizat.
Este structurat în două părți, care pot fi utilizate și independent:
Verificarea sintaxei pentru orice tip de fișier VHDL (orice descriere pentru circuite scrisă în VHDL v ANEXA A)
Simularea circuitelor logice elementare ( porțile logice elementare, sumatoare pe 2 biti si 3 biti)
(Detaliile despre dezvoltarea programului și utilizarea lui sunt precizate în capitolele anterioare.)
Programul poate fi folosit în primul rând în verificarea sintaxei fișierelor vhdl. Semnalează erorile
Acest program poate fi utilizat și în scop didactic, pentru exemplificările de simulări ale circuitelor elementare. Se poate urmări valoarea unui semnal, de la intrare (se setează valoarea dorită) și se vizualizează semnalele se ieșire și valorile intermediare.
Programul se poate dezvolta pe mai multe direcții :
se poate extinde simularea la circuite mai complexe, pornind de la structura deja existentă pe care se face simularea. Orice circuit se poate reduce la porțile logice analizate și simulate în program.
Fișierele analizate (parsate și verificate) pot fi folosite ca module în alte circuite – dezvoltarea programului în această direcție.
ANEXA A
VHDL este un limbaj de programare care descrie structuri hardware.
Este necesar să se facă o precizare esențială – elucidarea diferenței între soft și hard.
Spre deosebire de limbajele de programare soft, în VHDL sunt descrise circuite care mai întai sunt gandite și apoi descrise prin cod ( apoi sursa prelucrată, optimizată prin programe CAD etc și contruit circuitul fizic).
Acest limbaj permite descrierea în cod printr-un limbaj de programare a unor circuite care există ca reprezentare și apoi simularea sursei. Nu se urmărește implementarea teoretică a unei structuri, ci descrierea unui circuit.
ANEXA B
Fișierul VHDL_last.lex
%{
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <search.h>
#include "y.tab.h"
extern char *yysptr, *yysbuf;
int yylineno;
extern char yytchar;
#define YYLMAX 1024
/*
#ifdef BASE_REPRESENTATION
#ifdef ECHO
#undef ECHO
#endif
#define ECHO echo(yytext)
static void echo(char *s)
{
char loc[YYLMAX];
int l;
l=strlen(s);
loc[l]='\0';
while(l–) loc[l]=toupper(*(s+l));
fprintf(yyout, "%s\n", loc);
}
#endif
*/
int yycolumno=0;
/*
#undef input
int input()
{
if (yysptr>yysbuf){
yytchar=U(*–yysptr);
}else{
yytchar=getc(yyin);
}
if (yytchar=='\t')
yycolumno+=8;
else
++yycolumno;
if (yytchar=='\n'){
yylineno++;
}
if (yytchar==EOF)
return 0;
return yytchar;
}
#undef unput
void unput(char c)
{
yytchar=c;
*yysptr++=yytchar;
if(yytchar=='\n')
yylineno–;
if (c=='\t')
yycolumno-=8;
else
–yycolumno;
}
*/
#define MVL_LG_MC 15
#define MVL_NB_MC 81 +6
int MVL_LINNUM=1;
typedef struct {
char nom[MVL_LG_MC];
int kval;
} el_mc;
/*
definirea atomilor lexicali ai limbajului VHDL
(cuvinte cheie)
*/
static el_mc tab_mc []=
{
{"abs" ,t_ABS},
{"access" ,t_ACCESS},
{"after" ,t_AFTER},
{"alias" ,t_ALIAS},
{"all" ,t_ALL},
{"and" ,t_AND},
{"architecture" ,t_ARCHITECTURE},
{"array" ,t_ARRAY},
{"assert" ,t_ASSERT},
{"attribute" ,t_ATTRIBUTE},
{"begin" ,t_BEGIN},
{"block" ,t_BLOCK},
{"body" ,t_BODY},
{"buffer" ,t_BUFFER},
{"bus" ,t_BUS},
{"case" ,t_CASE},
{"component" ,t_COMPONENT},
{"configuration" ,t_CONFIGURATION},
{"constant" ,t_CONSTANT},
{"disconnect" ,t_DISCONNECT},
{"downto" ,t_DOWNTO},
{"else" ,t_ELSE},
{"elsif" ,t_ELSIF},
{"end" ,t_END},
{"entity" ,t_ENTITY},
{"exit" ,t_EXIT},
{"file" ,t_FILE},
{"for" ,t_FOR},
{"function" ,t_FUNCTION},
{"generate" ,t_GENERATE},
{"generic" ,t_GENERIC},
{"guarded" ,t_GUARDED},
{"if" ,t_IF},
{"impure" ,t_IMPURE},
{"in" ,t_IN},
{"inertial" ,t_INERTIAL},
{"inout" ,t_INOUT},
{"is" ,t_IS},
{"label" ,t_LABEL},
{"library" ,t_LIBRARY},
{"linkage" ,t_LINKAGE},
{"loop" ,t_LOOP},
{"map" ,t_MAP},
{"mod" ,t_MOD},
{"nand" ,t_NAND},
{"new" ,t_NEW},
{"next" ,t_NEXT},
{"nor" ,t_NOR},
{"not" ,t_NOT},
{"null" ,t_NULL},
{"of" ,t_OF},
{"on" ,t_ON},
{"open" ,t_OPEN},
{"or" ,t_OR},
{"others" ,t_OTHERS},
{"out" ,t_OUT},
{"package" ,t_PACKAGE},
{"port" ,t_PORT},
{"postponed" ,t_POSTPONED},
{"procedure" ,t_PROCEDURE},
{"process" ,t_PROCESS},
{"pure" ,t_PURE},
{"range" ,t_RANGE},
{"record" ,t_RECORD},
{"register" ,t_REGISTER},
{"reject" ,t_REJECT},
{"rem" ,t_REM},
{"report" ,t_REPORT},
{"return" ,t_RETURN},
{"select" ,t_SELECT},
{"severity" ,t_SEVERITY},
{"signal" ,t_SIGNAL},
{"subtype" ,t_SUBTYPE},
{"then" ,t_THEN},
{"to" ,t_TO},
{"transport" ,t_TRANSPORT},
{"type" ,t_TYPE},
{"unaffected" ,t_UNAFFECTED},
{"units" ,t_UNITS},
{"until" ,t_UNTIL},
{"use" ,t_USE},
{"variable" ,t_VARIABLE},
{"wait" ,t_WAIT},
{"when" ,t_WHEN},
{"while" ,t_WHILE},
{"with" ,t_WITH},
{"xor" ,t_XOR}
};
void *bsearch(const void *key, const void *base, size_t nmemb,
size_t size, int (*compar)(const void *, const void *));
static int find_mc(char * s)
{
char loc[YYLMAX];
int l;
el_mc *pt;
l=strlen(s);
strcpy(loc,s);
while(l–) loc[l]=tolower(loc[l]); /* conversia in litere mici */
pt= (el_mc *) bsearch(loc, (void *)tab_mc, MVL_NB_MC, sizeof(el_mc), strcmp);
if (pt==NULL) return(-1);
else return(pt->kval);
}
%}
upper_case_letter [A-Z]
digit [0-9]
special_character [\#\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\_\|]
space_character [ \t]
format_effector [\t\v\r\l\f]
end_of_line \n
lower_case_letter [a-z]
other_special_character [\!\$\@\?\[\\\]\^\`\{\}\~]
graphic_character ({basic_graphic_character}|{lower_case_letter}|{other_special_character})
basic_graphic_character ({upper_case_letter}|{digit}|{special_character}|{space_character})
letter ({upper_case_letter}|{lower_case_letter})
letter_or_digit ({letter}|{digit})
decimal_literal {integer}(\.{integer})?({exponent})?
integer {digit}(_?{digit})*
exponent ([eE][-+]?{integer})
base {integer}
based_integer {extended_digit}(_?{extended_digit})*
extended_digit ({digit}|[a-fA-F])
base_specifier (B|b|O|o|X|x)
%%
{space_character} {
/* nothing */
#ifndef BASE_REPRESENTATION
ECHO; /*return(t_Space);*/
#endif
}
\& { ECHO; return(t_Ampersand); }
\' { ECHO; return(t_Apostrophe); }
\( { ECHO; return(t_LeftParen); }
\) { ECHO; return(t_RightParen); }
"**" { ECHO; return(t_DoubleStar); }
\* { ECHO; return(t_Star); }
\+ { ECHO; return(t_Plus); }
\, { ECHO; return(t_Comma); }
\- { ECHO; return(t_Minus); }
":=" { ECHO; return(t_VarAsgn); }
\: { ECHO; return(t_Colon); }
\; { ECHO; return(t_Semicolon); }
"<=" { ECHO; return(t_LESym); }
">=" { ECHO; return(t_GESym); }
\< { ECHO; return(t_LTSym); }
\> { ECHO; return(t_GTSym); }
= { ECHO; return(t_EQSym); }
\/= { ECHO; return(t_NESym); }
"=>" { ECHO; return(t_Arrow); }
"<>" { ECHO; return(t_Box); }
\| { ECHO; return(t_Bar); }
! { ECHO; return(t_Bar); }
\. { ECHO; return(t_Dot); }
\/ { ECHO; return(t_Slash); }
{letter}(_?{letter_or_digit})* {
int itoken;
itoken=find_mc(yytext);
if (itoken== -1)
{
ECHO;
yylval.ds_Id.pos=yycolumno;
yylval.ds_Id.len=strlen(yytext);
yylval.ds_Id.line=yylineno;
/* yylval.ds_Id.name= insertName(yytext); */
return ( t_Identifier );
} else {
ECHO; return ( itoken );
}
}
({decimal_literal})|({base}#{based_integer}(\.{based_integer})?#({exponent})?)|({base}:{based_integer}(\.{based_integer})?:({exponent})?) {
ECHO; return ( t_AbstractLit ); }
'({graphic_character}|\"|\%)' {
ECHO; return ( t_CharacterLit );
}
(\"({graphic_character}|(\"\")|\%)*\")|(\%({graphic_character}|(\%\%)|\")*\%) {
ECHO; return ( t_StringLit );
}
{base_specifier}(\"{extended_digit}(_?{extended_digit})*\"|\%{extended_digit}(_?{extended_digit})*\%) {
ECHO; return ( t_BitStringLit );
}
\n {
/* end of line */
MVL_LINNUM++;
/* tobuf( "\n%4d\t", MVL_LINNUM);*/
#ifndef BASE_REPRESENTATION
ECHO;
#endif
yycolumno=0;
/*return(t_NEWLINE);*/
}
\-\-.*$ { /* comment */
#ifndef BASE_REPRESENTATION
ECHO; /*return(t_COMMENT);*/
#endif
}. {
ECHO; /*return (t_UNKNOWN);*/
}
%%
Fișierul VHDL_last.yacc
%{
#include <stdio.h>
#ifdef DEBUG_ENABLED
#define DEBUG_HERE printf ("[DBG %i]",__LINE__);
#else
#define DEBUG_HERE ;
#endif
#define YYERROR_VERBOSE 1
%}
%union{
struct{
int pos;
int len;
int line;
char *name;
} ds_Id;
}
/*definire tokeni*/
%token
t_ACCESS
t_AFTER
t_ALIAS
t_ALL
t_AND
t_ARCHITECTURE
t_ARRAY
t_ASSERT
t_ATTRIBUTE
t_BEGIN
t_BLOCK
t_BODY
t_BUFFER
t_BUS
t_CASE
t_COMPONENT
t_CONFIGURATION
t_CONSTANT
t_DISCONNECT
t_DOWNTO
t_ELSE
t_ELSIF
t_END
t_ENTITY
t_EXIT
t_FILE
t_FOR
t_FUNCTION
t_GENERATE
t_GENERIC
t_GUARDED
t_IF
t_IMPURE
t_IN
t_INERTIAL
t_INOUT
t_IS
t_LABEL
t_LIBRARY
t_LINKAGE
t_LOOP
t_MAP
t_NAND
t_NEW
t_NEXT
t_NOR
t_NULL
t_OF
t_ON
t_OPEN
t_OR
t_OTHERS
t_OUT
t_PACKAGE
t_PORT
t_POSTPONED
t_PROCEDURE
t_PROCESS
t_PURE
t_RANGE
t_RECORD
t_REGISTER
t_REJECT
t_REPORT
t_RETURN
t_SELECT
t_SEVERITY
t_SIGNAL
t_SUBTYPE
t_THEN
t_TO
t_TRANSPORT
t_TYPE
t_UNAFFECTED
t_UNITS
t_UNTIL
t_USE
t_VARIABLE
t_WAIT
t_WHEN
t_WHILE
t_WITH
t_XOR
/* VHDL operatori binari
*/
%nonassoc t_EQSym t_NESym t_LTSym t_LESym t_GTSym t_GESym
%left t_Plus t_Minus t_Ampersand
%left MED_PRECEDENCE
%left t_Star t_Slash t_MOD t_REM
%nonassoc t_DoubleStar t_ABS t_NOT MAX_PRECEDENCE
%token t_Apostrophe
t_LeftParen
t_RightParen
t_Comma
t_VarAsgn
t_Colon
t_Semicolon
t_Arrow
t_Box
t_Bar
t_Dot
%token < ds_Id > t_Identifier
t_AbstractLit
t_CharacterLit
t_StringLit
t_BitStringLit
%%
start : design_file;
design_file : design_unit_list;
design_unit_list : design_unit
| design_unit_list design_unit
;
designator : t_Identifier
| t_StringLit
;
literal : t_AbstractLit
| t_CharacterLit
| t_BitStringLit
| physical_literal_no_default
| t_NULL
;
enumeration_literal : t_CharacterLit
| t_Identifier
;
physical_literal : physical_literal_1 t_Identifier ;
physical_literal_1 :
| t_AbstractLit
;
physical_literal_no_default : t_AbstractLit t_Identifier
;
idf_list : t_Identifier
| idf_list t_Comma t_Identifier
;
/*– Unitate de design
*/
design_unit : context_list lib_unit
;
context_list :
| context_list context_item
;
lib_unit : entity_decl
| config_decl
| package_decl
| arch_body
| package_body
;
context_item : lib_clause
| use_clause
;
lib_clause : t_LIBRARY idf_list t_Semicolon
;
use_clause : t_USE sel_list t_Semicolon
;
sel_list : sel_name
| sel_list t_Comma sel_name
;
/*– Librarie
*/
entity_decl : entity_start entity_decl_1 entity_decl_2 entity_decl_3 entity_decl_4
t_END entity_optional_end entity_decl_5 t_Semicolon
;
entity_start : t_ENTITY t_Identifier t_IS ;
entity_decl_5 :
| t_Identifier
| ' ' t_Identifier
;
entity_optional_end :
| t_ENTITY ;
entity_decl_4 :
| t_BEGIN concurrent_stats {DEBUG_HERE};
entity_decl_3 :
| entity_decl_3 entity_decl_6 {DEBUG_HERE};
entity_decl_6 : entity_decltve_item {DEBUG_HERE};
entity_decl_2 :
| t_PORT interf_list t_Semicolon ;
entity_decl_1 :
| t_GENERIC interf_list t_Semicolon ;
arch_body : arch_start arch_body_1 t_BEGIN concurrent_stats t_END arch_optional_arch arch_body_2 t_Semicolon {DEBUG_HERE};
arch_start : t_ARCHITECTURE t_Identifier t_OF t_Identifier t_IS ;
arch_body_2 :
| t_Identifier ;
arch_body_1 :
| arch_body_1 arch_body_3 ;
arch_body_3 : block_decltve_item ;
arch_optional_arch :
| t_ARCHITECTURE ;
config_decl : config_start config_decl_1 block_config t_END config_optional_config config_decl_2 t_Semicolon ;
config_start : t_CONFIGURATION t_Identifier t_OF t_Identifier t_IS ;
config_decl_2 :
| t_Identifier ;
config_decl_1 :
| config_decl_1 config_decl_3 ;
config_decl_3 : config_decltve_item ;
config_optional_config :
| t_CONFIGURATION ;
package_decl : package_start package_decl_1 t_END package_optional_pkg package_decl_2 t_Semicolon ;
package_start : t_PACKAGE t_Identifier t_IS
package_decl_2 :
| t_Identifier ;
package_decl_1 :
| package_decl_1 package_decl_3 ;
package_decl_3 : package_decltve_item
package_optional_pkg :
| t_PACKAGE ;
package_body : pack_body_start package_body_1 t_END pkgbody_optional_pkgbody package_body_2 t_Semicolon ;
pack_body_start : t_PACKAGE t_BODY t_Identifier t_IS
package_body_2 :
| t_Identifier ;
package_body_1 :
| package_body_1 package_body_3 ;
package_body_3 : package_body_decltve_item ;
pkgbody_optional_pkgbody :
| t_PACKAGE t_BODY ;
/*– Sectiune declarativa
*/
;common_decltve_item : type_decl
;common_decltve_item : subtype_decl
;common_decltve_item : constant_decl
;common_decltve_item : file_decl
;common_decltve_item : alias_decl
;common_decltve_item : subprog_decl
;common_decltve_item : use_clause
;entity_decltve_item : common_decltve_item
;entity_decltve_item : subprog_body
;entity_decltve_item : attribute_decl
;entity_decltve_item : attribute_spec
;entity_decltve_item : disconnection_spec
;entity_decltve_item : signal_decl
;block_decltve_item : common_decltve_item
;block_decltve_item : subprog_body
;block_decltve_item : comp_decl
;block_decltve_item : attribute_decl
;block_decltve_item : attribute_spec
;block_decltve_item : config_spec
;block_decltve_item : disconnection_spec
;block_decltve_item : signal_decl
;package_decltve_item : common_decltve_item
;package_decltve_item : comp_decl
;package_decltve_item : attribute_decl
;package_decltve_item : attribute_spec
;package_decltve_item : disconnection_spec
;package_decltve_item : signal_decl
;package_body_decltve_item : common_decltve_item
;package_body_decltve_item : subprog_body
;subprog_decltve_item : common_decltve_item
;subprog_decltve_item : subprog_body
;subprog_decltve_item : attribute_decl
;subprog_decltve_item : attribute_spec
;subprog_decltve_item : variable_decl
;procs_decltve_item : common_decltve_item
;procs_decltve_item : subprog_body
;procs_decltve_item : attribute_decl
;procs_decltve_item : attribute_spec
;procs_decltve_item : variable_decl
;config_decltve_item : attribute_spec
;config_decltve_item : use_clause;
/*– Subprogram
*/
subprog_decl : subprog_spec t_Semicolon ;
subprog_spec : t_PROCEDURE t_Identifier subprog_spec_1
| subprog_fn_type t_FUNCTION designator subprog_spec_2 t_RETURN mark ;
subprog_fn_type :
| t_PURE
| t_IMPURE
;
subprog_spec_2 :
| interf_list ;
subprog_spec_1 :
| interf_list ;
subprog_body : subprog_spec t_IS subprog_body_1 t_BEGIN seq_stats t_END subprog_body_2 t_Semicolon
;
subprog_body_2 :
| designator
| t_PROCEDURE designator
| t_FUNCTION designator
;
subprog_body_1 :
| subprog_body_1 subprog_body_3
;
subprog_body_3 : subprog_decltve_item
;
/*– Interfata / Asociatii
*/
interf_list : t_LeftParen interf_element interf_list_1 t_RightParen ;
interf_list_1 :
| interf_list_1 interf_list_2 ;
interf_list_2 : t_Semicolon interf_element
;interf_element : interf_element_1 idf_list t_Colon interf_element_2 subtype_indic interf_element_3 interf_element_4
;interf_element_4 :
;interf_element_4 : t_VarAsgn expr
;interf_element_3 :
;interf_element_3 : t_BUFFER
;interf_element_2 :
;interf_element_2 : mode
;interf_element_1 :
;interf_element_1 : object_class ;
mode : t_IN
| t_OUT
| t_INOUT
| t_BUFFER
| t_LINKAGE
;
;association_list : t_LeftParen association_element association_list_1 t_RightParen
;association_list_1 :
;association_list_1 : association_list_1 association_list_2
;association_list_2 : t_Comma association_element ;
gen_association_list : t_LeftParen gen_association_element gen_association_list_1 t_RightParen ;
gen_association_list_1 :
| gen_association_list_1 gen_association_list_2 ;
gen_association_list_2 : t_Comma gen_association_element ;
association_element : formal_part t_Arrow actual_part
| actual_part ;
gen_association_element : expr
| name t_Arrow expr
| discrete_range1;
formal_part : name ;
actual_part : expr
| t_OPEN ;
/*– Nume si Expresii
*/
mark : t_Identifier
| sel_name
;
expr : and_relation {DEBUG_HERE}
| or_relation {DEBUG_HERE}
| xor_relation {DEBUG_HERE}
| relation {DEBUG_HERE}
| relation t_NAND relation {DEBUG_HERE}
| relation t_NOR relation {DEBUG_HERE}
;
and_relation : relation t_AND relation {DEBUG_HERE}
| and_relation t_AND relation {DEBUG_HERE}
;
or_relation : relation t_OR relation {DEBUG_HERE}
| or_relation t_OR relation {DEBUG_HERE}
;
xor_relation : relation t_XOR relation {DEBUG_HERE}
| xor_relation t_XOR relation {DEBUG_HERE}
;
relation : primary {DEBUG_HERE}
| t_Plus primary %prec MED_PRECEDENCE {DEBUG_HERE}
| t_Minus primary %prec MED_PRECEDENCE {DEBUG_HERE}
| t_ABS primary {DEBUG_HERE}
| t_NOT primary {DEBUG_HERE}
| primary t_DoubleStar primary {DEBUG_HERE}
;
relation : relation t_MOD relation {DEBUG_HERE}
| relation t_REM relation {DEBUG_HERE}
| relation t_Ampersand relation {DEBUG_HERE}
| relation t_Star relation {DEBUG_HERE}
| relation t_Plus relation {DEBUG_HERE}
| relation t_Minus relation {DEBUG_HERE}
| relation t_LESym relation {DEBUG_HERE}
| relation t_GESym relation {DEBUG_HERE}
| relation t_LTSym relation {DEBUG_HERE}
| relation t_GTSym relation {DEBUG_HERE}
| relation t_EQSym relation {DEBUG_HERE}
| relation t_NESym relation {DEBUG_HERE}
| relation t_Slash relation {DEBUG_HERE}
;
primary : name {DEBUG_HERE}
| literal {DEBUG_HERE}
| aggregate {DEBUG_HERE}
| qualified_expr {DEBUG_HERE}
| allocator {DEBUG_HERE}
| t_LeftParen expr t_RightParen {DEBUG_HERE}
;
;name : mark
;name : name2
;name2 : t_StringLit
;name2 : attribute_name
;name2 : ifts_name
;sel_name : name t_Dot suffix
;suffix : designator
;suffix : t_CharacterLit
;suffix : t_ALL
;ifts_name : mark gen_association_list
;ifts_name : name2 gen_association_list
;attribute_name : mark t_Apostrophe t_Identifier
;attribute_name : name2 t_Apostrophe t_Identifier
;attribute_name : mark t_Apostrophe t_RANGE
;attribute_name : name2 t_Apostrophe t_RANGE
;aggregate : element_association_list2 t_RightParen
;aggregate : t_LeftParen choices t_Arrow expr t_RightParen
;element_association_list2 : t_LeftParen element_association t_Comma element_association
;element_association_list2 : element_association_list2 t_Comma element_association
;qualified_expr : mark t_Apostrophe t_LeftParen expr t_RightParen
;qualified_expr : mark t_Apostrophe aggregate
;allocator : t_NEW mark mark allocator_1
;allocator : t_NEW mark allocator_2
;allocator : t_NEW qualified_expr
;allocator_2 :
;allocator_2 : gen_association_list
;allocator_1 :
;allocator_1 : gen_association_list;
/*– Asociatii / Choice
*/
element_association : choices t_Arrow expr
| expr
;
choices : choice choices_1
;
choices_1 :
| choices_1 choices_2
;
choices_2 : t_Bar choice ;
choice : expr
| discrete_range1
| t_OTHERS
;
/*– Declararea tipurilor
*/
type_decl : t_TYPE t_Identifier type_decl_1 t_Semicolon;
type_decl_1 :
| t_IS type_definition ;
type_definition : enumeration_type_definition
| range_constraint
| physical_type_definition
| unconstrained_array_definition
| constrained_array_definition
| record_type_definition
| access_type_definition
| file_type_definition
;
enumeration_type_definition : t_LeftParen enumeration_literal enumeration_type_definition_1 t_RightParen ;
enumeration_type_definition_1 :
| enumeration_type_definition_1 enumeration_type_definition_2 ;
enumeration_type_definition_2 : t_Comma enumeration_literal ;
physical_type_definition : range_constraint t_UNITS base_unit_decl physical_type_definition_1 t_END t_UNITS ;
physical_type_definition_1 :
| physical_type_definition_1 physical_type_definition_2 ;
physical_type_definition_2 : secondary_unit_decl ;
base_unit_decl : t_Identifier t_Semicolon ;
secondary_unit_decl : t_Identifier t_EQSym physical_literal t_Semicolon ;
unconstrained_array_definition : t_ARRAY t_LeftParen index_subtype_definition unconstrained_array_definition_1 t_RightParen
t_OF subtype_indic ;
unconstrained_array_definition_1:
| unconstrained_array_definition_1 unconstrained_array_definition_2 ;
unconstrained_array_definition_2: t_Comma index_subtype_definition;
index_subtype_definition : mark t_RANGE t_Box;
constrained_array_definition : t_ARRAY index_constraint t_OF subtype_indic;
record_type_definition : t_RECORD element_decl record_type_definition_1 t_END t_RECORD ;
record_type_definition_1 :
| record_type_definition_1 record_type_definition_2 ;
record_type_definition_2 : element_decl ;
element_decl : idf_list t_Colon subtype_indic t_Semicolon ;
access_type_definition : t_ACCESS subtype_indic ;
file_type_definition : t_FILE t_OF mark ;
/*– Subtipuri
*/
subtype_decl : t_SUBTYPE t_Identifier t_IS subtype_indic t_Semicolon ;
subtype_indic : mark subtype_indic_1
| subtype_indic1 ;
subtype_indic_1 :
| gen_association_list ;
subtype_indic1 : mark mark range_constraint
| mark range_constraint
| mark mark subtype_indic1_1
;
subtype_indic1_1 :
| gen_association_list ;
range_constraint : t_RANGE range_spec ;
index_constraint : t_LeftParen discrete_range index_constraint_1 t_RightParen ;
index_constraint_1 :
| index_constraint_1 index_constraint_2
index_constraint_2 : t_Comma discrete_range ;
discrete_range : subtype_indic
| range_spec
;
discrete_range1 : subtype_indic1
| expr direction expr
;
range_spec : attribute_name
| expr direction expr
;
direction : t_TO
| t_DOWNTO
;
/*– Obiecte, Aliase, Fisiere
*/
constant_decl : t_CONSTANT idf_list t_Colon subtype_indic constant_decl_1 t_Semicolon
constant_decl_1 :
| t_VarAsgn expr
;
signal_decl : t_SIGNAL idf_list t_Colon subtype_indic signal_decl_1 signal_decl_2 t_Semicolon ;
signal_decl_2 :
| t_VarAsgn expr
;
signal_decl_1 :
| signal_kind
;
variable_decl : t_VARIABLE idf_list t_Colon subtype_indic variable_decl_1 t_Semicolon ;
variable_decl_1 :
| t_VarAsgn expr
;
object_class : t_CONSTANT
| t_SIGNAL
| t_VARIABLE
;
signal_kind : t_BUS
| t_REGISTER
;
alias_decl : t_ALIAS t_Identifier t_Colon subtype_indic t_IS name t_Semicolon ;
file_decl : t_FILE t_Identifier t_Colon subtype_indic t_IS file_decl_1 expr t_Semicolon ;
file_decl_1 :
| mode
;
disconnection_spec : t_DISCONNECT signal_list t_Colon mark t_AFTER expr t_Semicolon ;
signal_list : name signal_list_1
| t_OTHERS
| t_ALL
;
signal_list_1 :
| signal_list_1 signal_list_2
;
signal_list_2 : t_Comma name ;
/*– Declaratii de atribute si specificatii
*/
attribute_decl : t_ATTRIBUTE t_Identifier t_Colon mark t_Semicolon ;
attribute_spec : t_ATTRIBUTE t_Identifier t_OF entity_spec t_IS expr t_Semicolon ;
entity_spec : entity_name_list t_Colon entity_class ;
;entity_name_list : designator entity_name_list_1
;entity_name_list : t_OTHERS
;entity_name_list : t_ALL
;entity_name_list_1 :
;entity_name_list_1 : entity_name_list_1 entity_name_list_2
;entity_name_list_2 : t_Comma designator
entity_class : t_ENTITY
| t_ARCHITECTURE
| t_PACKAGE
| t_CONFIGURATION
| t_COMPONENT
| t_LABEL
| t_TYPE
| t_SUBTYPE
| t_PROCEDURE
| t_FUNCTION
| t_SIGNAL
| t_VARIABLE
| t_CONSTANT
;
/*– Scheme
*/
generation_scheme : if_scheme
| for_scheme
;
iteration_scheme : for_scheme
| while_scheme
;
if_scheme : t_IF expr ;
for_scheme : t_FOR t_Identifier t_IN discrete_range ;
while_scheme : t_WHILE expr ;
/*– Concurent
*/
concurrent_stats :
| concurrent_stats concurrent_stats_2 ;
concurrent_stats_2 : concurrent_stat {DEBUG_HERE};
concurrent_stat : block_stat {DEBUG_HERE}
| concurrent_assertion_stat {DEBUG_HERE}
| concurrent_procedure_call {DEBUG_HERE}
| concurrent_signal_assign_stat {DEBUG_HERE}
| comp_inst_stat {DEBUG_HERE}
| generate_stat {DEBUG_HERE}
| procs_stat {DEBUG_HERE}
;
block_stat : t_Identifier t_Colon t_BLOCK block_stat_1 block_stat_2 block_stat_3 block_stat_4
t_BEGIN concurrent_stats t_END t_BLOCK block_stat_5 t_Semicolon
;
block_stat_5 :
| t_Identifier ;
block_stat_4 :
| block_stat_4 block_stat_6 ;
block_stat_6 : block_decltve_item ;
block_stat_3 :
| t_PORT interf_list t_Semicolon block_stat_7 ;
block_stat_7 :
| t_PORT t_MAP association_list t_Semicolon ;
block_stat_2 :
| t_GENERIC interf_list t_Semicolon block_stat_8 ;
block_stat_8 :
| t_GENERIC t_MAP association_list t_Semicolon ;
block_stat_1 :
| t_LeftParen expr t_RightParen ;
comp_inst_stat : t_Identifier t_Colon instantiated_unit generic_map_aspect port_map_aspect t_Semicolon;
instantiated_unit : t_COMPONENT mark
| name
| t_ENTITY mark optional_arch_id
| t_CONFIGURATION mark
;
optional_arch_id :
| t_LeftParen t_Identifier t_RightParen ;
generic_map_aspect :
| t_GENERIC t_MAP association_list ;
port_map_aspect :
| t_PORT t_MAP association_list
;
concurrent_assertion_stat : t_Identifier t_Colon assertion_stat {DEBUG_HERE}
| assertion_stat {DEBUG_HERE}
;
concurrent_procedure_call : t_Identifier t_Colon procedure_call_stat {DEBUG_HERE}
| procedure_call_stat {DEBUG_HERE}
;
concurrent_signal_assign_stat : t_Identifier t_Colon conditional_signal_assign {DEBUG_HERE}
| conditional_signal_assign {DEBUG_HERE}
| t_Identifier t_Colon sel_signal_assign {DEBUG_HERE}
| sel_signal_assign {DEBUG_HERE}
;
conditional_signal_assign : target t_LESym options
conditional_waveforms t_Semicolon {DEBUG_HERE} ;
conditional_waveforms : waveform t_WHEN expr t_ELSE conditional_waveforms {DEBUG_HERE}
| waveform {DEBUG_HERE}
| waveform t_WHEN expr {DEBUG_HERE}
;
waveform : waveform t_Comma waveform_element {DEBUG_HERE}
| waveform_element {DEBUG_HERE}
| t_UNAFFECTED
;
waveform_element : expr waveform_element_1 ;
waveform_element_1 :
| t_AFTER expr ;
target : name
| aggregate
;
options : optional_guarded delay_mechanism {DEBUG_HERE};
optional_guarded : {DEBUG_HERE}
| t_GUARDED {DEBUG_HERE};
delay_mechanism : {DEBUG_HERE}
| t_TRANSPORT {DEBUG_HERE}
| delay_optional_reject t_INERTIAL {DEBUG_HERE};
delay_optional_reject :
| t_REJECT expr;
sel_signal_assign : t_WITH expr t_SELECT target t_LESym options sel_waveforms t_Semicolon {DEBUG_HERE};
;sel_waveforms : sel_waveforms_1 waveform t_WHEN choices
;sel_waveforms_1 :
;sel_waveforms_1 : sel_waveforms_1 sel_waveforms_2
;sel_waveforms_2 : waveform t_WHEN choices t_Comma
;generate_stat : t_Identifier t_Colon generation_scheme t_GENERATE
concurrent_stats t_END t_GENERATE generate_stat_1 t_Semicolon
;generate_stat_1 :
;generate_stat_1 : t_Identifier
;procs_stat : t_Identifier t_Colon procs_stat1
;procs_stat : procs_stat1
;
procs_stat1 : t_PROCESS procs_stat1_1 proc_optional_is procs_stat1_2 t_BEGIN seq_stats t_END
t_PROCESS procs_stat1_3 t_Semicolon;
procs_stat1_3 :
| t_Identifier ;
procs_stat1_2 :
| procs_stat1_2 procs_stat1_4 ;
procs_stat1_4 : procs_decltve_item ;
procs_stat1_1 :
| t_LeftParen sensitivity_list t_RightParen ;
proc_optional_is :
| t_IS ;
;sensitivity_list : name sensitivity_list_1
;sensitivity_list_1 :
;sensitivity_list_1 : sensitivity_list_1 sensitivity_list_2
;sensitivity_list_2 : t_Comma name
/*– Secvential
*/
;seq_stats : seq_stats_1
;seq_stats_1 :
;seq_stats_1 : seq_stats_1 seq_stats_2
;seq_stats_2 : seq_stat;
seq_stat : assertion_stat
| case_stat
| exit_stat
| if_stat
| loop_stat
| next_stat
| null_stat
| procedure_call_stat
| return_stat
| signal_assign_stat
| variable_assign_stat
| wait_stat
| report_stat
;
report_stat : t_REPORT expr rep_stat_1 ;
rep_stat_1 : t_Semicolon;
| t_SEVERITY expr t_Semicolon
;
assertion_stat : t_ASSERT expr assertion_stat_1 assertion_stat_2 t_Semicolon ;
assertion_stat_2 :
| t_SEVERITY expr ;
assertion_stat_1 :
| t_REPORT expr;
case_stat : t_CASE expr t_IS case_stat_alternative case_stat_1 t_END t_CASE t_Semicolon
case_stat_1 :
| case_stat_1 case_stat_2
;
case_stat_2 : case_stat_alternative
;
case_stat_alternative : t_WHEN choices t_Arrow seq_stats ;
;exit_stat : t_EXIT exit_stat_1 exit_stat_2 t_Semicolon
;exit_stat_2 :
;exit_stat_2 : t_WHEN expr
;exit_stat_1 :
;exit_stat_1 : t_Identifier
;if_stat : t_IF expr t_THEN seq_stats if_stat_1 if_stat_2 t_END t_IF t_Semicolon
;if_stat_2 :
;if_stat_2 : t_ELSE seq_stats
;if_stat_1 :
;if_stat_1 : if_stat_1 if_stat_3
;if_stat_3 : t_ELSIF expr t_THEN seq_stats
;loop_stat : loop_stat_1 loop_stat_2 t_LOOP seq_stats t_END t_LOOP loop_stat_3 t_Semicolon
;loop_stat_3 :
;loop_stat_3 : t_Identifier
;loop_stat_2 :
;loop_stat_2 : iteration_scheme
;loop_stat_1 :
;loop_stat_1 : t_Identifier t_Colon
;next_stat : t_NEXT next_stat_1 next_stat_2 t_Semicolon
;next_stat_2 :
;next_stat_2 : t_WHEN expr
;next_stat_1 :
;next_stat_1 : t_Identifier
;null_stat : t_NULL t_Semicolon
;procedure_call_stat : name t_Semicolon
;return_stat : t_RETURN return_stat_1 t_Semicolon
;return_stat_1 :
;return_stat_1 : expr;
signal_assign_stat : target t_LESym signal_assign_stat_1 waveform t_Semicolon {DEBUG_HERE};
signal_assign_stat_1 :
signal_assign_stat_1 : t_TRANSPORT ;
variable_assign_stat : target t_VarAsgn expr t_Semicolon ;
wait_stat : t_WAIT wait_stat_1 wait_stat_2 wait_stat_3 t_Semicolon ;
wait_stat_3 :
| t_FOR expr ;
wait_stat_2 :
| t_UNTIL expr ;
wait_stat_1 :
| t_ON sensitivity_list ;
/*– Componente si Configuratii
*/
comp_decl : t_COMPONENT t_Identifier comp_optional_is comp_decl_1 comp_decl_2 t_END t_COMPONENT comp_optional_id t_Semicolon ;
comp_decl_2 :
| t_PORT interf_list t_Semicolon ;
comp_decl_1 :
| t_GENERIC interf_list t_Semicolon ;
comp_optional_is :
| t_IS ;
comp_optional_id :
| t_Identifier;
;block_config : t_FOR block_spec block_config_1 block_config_2 t_END t_FOR t_Semicolon
;block_config_2 :
;block_config_2 : block_config_2 block_config_3
;block_config_3 : config_item
;block_config_1 :
;block_config_1 : block_config_1 block_config_4
;block_config_4 : use_clause
block_spec : name ;
config_item : block_config ;
config_item : comp_config ;
comp_config : t_FOR comp_spec comp_config_1 comp_config_2 t_END t_FOR t_Semicolon ;
comp_config_2 :
| block_config ;
comp_config_1 :
| t_USE binding_indic t_Semicolon ;
config_spec : t_FOR comp_spec t_USE binding_indic t_Semicolon ;
comp_spec : inst_list t_Colon mark ;
inst_list : idf_list
| t_ALL
| t_OTHERS ;
;binding_indic : entity_aspect binding_indic_1 binding_indic_2
;binding_indic_2 :
;binding_indic_2 : t_PORT t_MAP association_list;
binding_indic_1 :
| t_GENERIC t_MAP association_list ;
entity_aspect : t_ENTITY name
| t_CONFIGURATION mark
| t_OPEN
;
%%
extern FILE* yyout;
void yyerror(char *str)
{
fprintf( yyout, " Eroare: %s\n", str );
}
ANEXA C
eventual sursele programului in C# .. care ar ajunge cam la 150- 180 pagini
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Realizarea Unui Program de Simulare Circuite Descrise In Vhdl (ID: 149170)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
