Sunteți pe pagina 1din 219

ANEXA

PACHETE STANDARDIZATE
-- Pachetele conforme cu standardul IEEE 1164

-- LEGENDĂ
-- () = „Grupare”
-- [ ] = „Opţional”
-- {} = „În mod repetat”
-- | = „Alternative”
-- MAJUSCULE = „Identificator utilizator”

-- Prescurtări
-- c ::= „comutativ”
-- b ::= BIT
-- bv ::= BIT_VECTOR
-- u/l ::= STD_ULOGIC/STD_LOGIC
-- uv ::= STD_ULOGIC_VECTOR
-- lv ::= STD_LOGIC_VECTOR
-- un ::= UNSIGNED
-- sg ::= SIGNED
-- in ::= INTEGER
-- na ::= NATURAL
-- sm ::= SMALL_INT (subtype INTEGER range 0 to 1)

-- 1. Pachetul IEEE STD_LOGIC_1164


-- 1.1. VALORI LOGICE
-- ‘U’ Neiniţializat
-- ‘X’/’W’ Necunoscut tare / slab
-- ‘0’/’L’ 0 tare / slab
-- ‘1’/’H’ 1 tare / slab
-- ‘Z’ Înaltă impedanţă
-- ‘-’ Fără importanţă

-- 1.2. TIPURI PREDEFINITE


-- STD_ULOGIC tip de bază
-- Subtipuri:
-- STD_LOGIC = STD_ULOGIC Rezolvat
-- X01 = tip rezolvat care conţine valorile X, 0 şi 1
-- X01Z = tip rezolvat care conţine valorile X, 0, 1 şi Z
-- UX01 = tip rezolvat care conţine valorile U, X, 0 şi 1
-- UX01Z = tip rezolvat care conţine valorile U, X, 0, 1 şi Z
222 LIMBAJUL VHDL

-- STD_ULOGIC_VECTOR(na to|downto na) = tablou de elemente


-- STD_ULOGIC
-- STD_LOGIC_VECTOR(na to|downto na) = tablou de elemente
-- STD_LOGIC

-- 1.3. OPERATORI SUPRAÎNCĂRCAŢI


-- Descriere Stânga Operator Dreapta
-- ŞI pe biţi u/l,uv,lv and, nand u/l,uv,lv
-- SAU pe biţi u/l,uv,lv or, nor u/l,uv,lv
-- XOR pe biţi u/l,uv,lv xor, xnor u/l,uv,lv
-- NU pe biţi not u/l,uv,lv

-- 1.4. FUNCŢII DE CONVERSIE


-- De_la La Funcţia
-- u/l b TO_BIT(de_la[, xmap])
-- uv,lv bv TO_BITVECTOR(de_la[, xmap])
-- b u/l TO_STDULOGIC(de_la)
-- bv,uv lv TO_STDLOGICVECTOR(de_la)
-- bv,lv uv TO_STDULOGICVECTOR(de_la)

-- 2. Pachetul IEEE NUMERIC_STD


-- 2.1. TIPURI PREDEFINITE
-- UNSIGNED(na to | downto na) = tablou de elemente STD_LOGIC
-- SIGNED(na to | downto na) = tablou de elemente STD_LOGIC

-- 2.2. OPERATORI SUPRAÎNCĂRCAŢI


-- Stânga Operaţie Dreapta Valoare de retur
-- abs sg sg
-- - sg sg
-- un +,-,*,/,rem,mod un un
-- sg +,-,*,/,rem,mod sg sg
-- un +,-,*,/,rem,modc na un
-- sg +,-,*,/,rem,modc in sg
-- un <,>,<=,>=,=,/= un bool
-- sg <,>,<=,>=,=,/= sg bool
-- un <,>,<=,>=,=,/=c na bool
-- sg <,>,<=,>=,=,/=c In bool

-- 2.3. FUNCŢII PREDEFINITE


-- SHIFT_LEFT(un, na) returnează un
-- SHIFT_RIGHT(un, na) returnează un
-- SHIFT_LEFT(sg, na) returnează sg
-- SHIFT_RIGHT(sg, na) returnează sg
ANEXA 223

-- ROTATE_LEFT(un, na) returnează un


-- ROTATE_RIGHT(un, na) returnează un
-- ROTATE_LEFT(sg, na) returnează sg
-- ROTATE_RIGHT(sg, na) returnează sg
-- RESIZE(sg, na) returnează sg
-- RESIZE(un, na) returnează un
-- STD_MATCH(u/l, u/l) returnează bool
-- STD_MATCH(ul, ul) returnează bool
-- STD_MATCH(lv, lv) returnează bool
-- STD_MATCH(un, un) returnează bool
-- STD_MATCH(sg, sg) returnează bool

-- 2.4. FUNCŢII DE CONVERSIE


-- De_la La Funcţia
-- un,lv sg SIGNED(de_la)
-- sg,lv un UNSIGNED(de_la)
-- un,sg lv STD_LOGIC_VECTOR(de_la)
-- un,sg in TO_INTEGER(de_la)
-- na un TO_UNSIGNED(de_la, dimensiune)
-- in sg TO_SIGNED(de_la, dimensiune)

-- 3. Pachetul IEEE NUMERIC_BIT


-- 3.1. TIPURI PREDEFINITE
-- UNSIGNED(na to | downto na) = tablou de elemente BIT
-- SIGNED(na to | downto na) = tablou de elemente BIT

-- 3.2. OPERATORI SUPRAÎNCĂRCAŢI


-- Stânga Operaţie Dreapta Valoare de retur
-- abs sg sg
-- - sg sg
-- un +,-,*,/,rem,mod un un
-- sg +,-,*,/,rem,mod sg sg
-- un +,-,*,/,rem,modc na un
-- sg +,-,*,/,rem,modc in sg
-- un <,>,<=,>=,=,/= un bool
-- sg <,>,<=,>=,=,/= sg bool
-- un <,>,<=,>=,=,/=c na bool
-- sg <,>,<=,>=,=,/=c in bool

-- 3.3. FUNCŢII PREDEFINITE


-- SHIFT_LEFT(un, na) returnează un
-- SHIFT_RIGHT(un, na) returnează un
-- SHIFT_LEFT(sg, na) returnează sg
-- SHIFT_RIGHT(sg, na) returnează sg
224 LIMBAJUL VHDL

-- ROTATE_LEFT(un, na) returnează un


-- ROTATE_RIGHT(un, na) returnează un
-- ROTATE_LEFT(sg, na) returnează sg
-- ROTATE_RIGHT(sg, na) returnează sg
-- RESIZE(sg, na) returnează sg
-- RESIZE(un, na) returnează un

-- 3.4. FUNCŢII DE CONVERSIE


-- De_la La Funcţie
-- un,bv sg SIGNED(de_la)
-- sg,bv un UNSIGNED(de_la)
-- un,sg bv BIT_VECTOR(de_la)
-- un,sg in TO_INTEGER(de_la)
-- na un TO_UNSIGNED(de_la)
-- in sg TO_SIGNED(de_la)

-- 4. Pachetul STD_LOGIC_ARITH al firmei SYNOPSYS


-- 4.1. TIPURI PREDEFINITE
-- UNSIGNED(na to | downto na) = tablou de elemente STD_LOGIC
-- SIGNED(na to | downto na) = tablou de elemente STD_LOGIC
-- SMALL_INT subtip al tipului Integer, 0 sau 1

-- 4.2. OPERATORI SUPRAÎNCĂRCAŢI


-- Stânga Operaţie dreapta Valoare de retur
-- abs sg sg,lv
-- - sg sg,lv
-- un +,-,*,/ un un,lv
-- sg +,-,*,/ sg sg,lv
-- sg +,-,*,/c un sg,lv
-- un +,-c in un,lv
-- sg +,-c in sg,lv
-- un +,-c u/l un,lv
-- sg +,-c u/l sg,lv
-- un <,>,<=,>=,=,/= un bool
-- sg <,>,<=,>=,=,/= sg bool
-- un <,>,<=,>=,=,/=c in bool
-- sg <,>,<=,>=,=,/=c in bool

-- 4.3. FUNCŢII PREDEFINITE


-- SHL(un, un) returnează un
-- SHL(sg, un) returnează sg
-- SHR(un, un) returnează un
-- SHR(sg, un) returnează sg
ANEXA 225

-- EXT(lv, in) returnează lv


-- SEXT(lv, in) returnează lv
-- extensie la zero
-- extensie de semn

-- 4.4. FUNCŢII DE CONVERSIE


-- De_la La Funcţie
-- un,lv sg SIGNED(de_la)
-- sg,lv un UNSIGNED(de_la)
-- sg,un lv STD_LOGIC_VECTOR(de_la)
-- un,sg in CONV_INTEGER(de_la)
-- in,un,sg,u un CONV_UNSIGNED(de_la, dimensiune)
-- in,un,sg,u sg CONV_SIGNED(de_la, dimensiune)
-- in,un,sg,u lv CONV_STD_LOGIC_VECTOR(de_la, dimensiune)

-- 5. Pachetul STD_LOGIC_UNSIGNED al firmei


-- SYNOPSYS
-- 5.1. OPERATORI SUPRAÎNCĂRCAŢI
-- Stânga Operaţie dreapta Valoare de retur
-- + lv lv
-- lv +,-,* lv lv
-- lv +,-c in lv
-- lv +,-c u/l lv
-- lv <,>,<=,>=,=,/= lv bool
-- lv <,>,<=,>=,=,/=c in bool

-- 5.2. FUNCŢII DE CONVERSIE


-- De_la La Funcţie
-- lv in CONV_INTEGER(de_la)

-- 6. Pachetul STD_LOGIC_SIGNED al firmei SYNOPSYS


-- 6.1. OPERATORI SUPRAÎNCĂRCAŢI
-- Stânga Operaţie dreapta Valoare de retur
-- abs lv lv
-- +,- lv lv
-- lv +,-,* lv lv
-- lv +,-c in lv
-- lv +,-c u/l lv
-- lv <,>,<=,>=,=,/= lv bool
-- lv <,>,<=,>=,=,/=c in bool
226 LIMBAJUL VHDL

-- 6.2. FUNCŢII DE CONVERSIE


-- De_la La Funcţie
-- lv in CONV_INTEGER(de_la)

-- 7. Pachetul STD_LOGIC_MISC al firmei SYNOPSYS


-- 7.1. FUNCŢII PREDEFINITE
-- AND_REDUCE(lv | uv) returnează u/l
-- OR_REDUCE(lv | uv) returnează u/l
-- XOR_REDUCE(lv | uv) returnează u/l

-- 8. Pachetul STD_LOGIC_ARITH al firmei CADENCE


-- 8.1. OPERATORI SUPRAÎNCĂRCAŢI
-- Stânga Operaţie dreapta Valoare de retur
-- u/l +,-,*,/ u/l u/l
-- lv +,-,*,/ lv lv
-- lv +,-,*,/c u/l lv
-- lv +,-c in lv
-- uv +,-,* uv uv
-- uv +,-,*c u/l uv
-- uv +,-c in uv
-- lv <,>,<=,>=,=,/=c in bool
-- uv <,>,<=,>=,=,/=c in bool

-- 8.2. FUNCŢII PREDEFINITE


-- SH_LEFT(lv, na) returnează lv
-- SH_LEFT(uv, na) returnează uv
-- SH_RIGHT(lv, na) returnează lv
-- SH_RIGHT(uv, na) returnează uv
-- ALIGN_SIZE(lv, na) returnează lv
-- ALIGN_SIZE(uv, na) returnează uv
-- ALIGN_SIZE(u/l, na) returnează lv,uv
-- Înlocuiri de genul înlocuirii „?:” din limbajul C:
-- COND_OP(bool, lv, lv) returnează lv
-- COND_OP(bool, uv, uv) returnează uv
-- COND(bool, u/l, u/l) returnează u/l

-- 8.3. FUNCŢII DE CONVERSIE


-- De_la La Funcţie
-- lv,uv,u/l in TO_INTEGER(de_la)
-- in lv TO_STDLOGICVECTOR(de_la, dimensiune)
-- in uv TO_STDULOGICVECTOR(de_la, dimensiune)
ANEXA 227

-- 9. Pachetul STD_LOGIC_ARITH al firmei MENTOR


-- 9.1. TIPURI PREDEFINITE
-- UNSIGNED(na to | downto na) = tablou de elemente STD_LOGIC
-- SIGNED(na to | downto na) = tablou de elemente STD_LOGIC

-- 9.2. OPERATORI SUPRAÎNCĂRCAŢI


-- Stânga Operaţie dreapta Valoare de retur
-- abs sg sg
-- - sg sg
-- u/l +,- u/l u/l
-- uv +,-,*,/,mod,rem,** uv uv
-- lv +,-,*,/,mod,rem,** lv lv
-- un +,-,*,/,mod,rem,** un un
-- sg +,-,*,/,mod,rem,** sg sg
-- un <,>,<=,>=,=,/= un bool
-- sg <,>,<=,>=,=,/= sg bool
-- not un un
-- not sg sg
-- un and,nand,or,nor,xor un un
-- sg and,nand,or,nor,xor,xnor sg sg
-- uv sla,sra,sll,srl,rol,ror uv uv
-- lv sla,sra,sll,srl,rol,ror lv lv
-- un sla,sra,sll,srl,rol,ror un un
-- sg sla,sra,sll,srl,rol,ror sg sg

-- 9.3. FUNCŢII PREDEFINITE


-- ZERO_EXTEND(uv | lv | un, na) returnează idem
-- ZERO_EXTEND(u/l, na) returnează lv
-- SIGN_EXTEND(sg, na) returnează sg
-- AND_REDUCE(uv | lv | un | sg) returnează u/l
-- OR_REDUCE(uv | lv | un | sg) returnează u/l
-- XOR_REDUCE(uv | lv | un | sg) returnează u/l

-- 9.4. FUNCŢII DE CONVERSIE


-- De_la La Funcţie
-- u/l,uv,lv,un,sg in TO_INTEGER(de_la)
-- u/l,uv,lv,un,sg in CONV_INTEGER(de_la)
-- bool u/l TO_STDLOGIC(de_la)
-- na un TO_UNSIGNED(de_la,dimensiune)
-- na un CONV_UNSIGNED(de_la,dimensiune)
-- in sg TO_SIGNED(de_la,dimensiune)
-- in sg CONV_SIGNED(de_la,dimensiune)
-- na lv TO_STDLOGICVECTOR(de_la,dim)
-- na uv TO_STDULOGICVECTOR(de_la,dim)
LUCRAREA NR. 1
INTRODUCERE ÎN VHDL

1. Scopul lucrării

În această lucrare se prezintă elementele fundamentale legate de


limbajul de descriere hardware VHDL: scurt istoric, definiţii, avantaje şi
dezavantaje, principalele sale caracteristici, însoţite de exemple elementare
de circuite, în principalele stiluri de descriere existente în VHDL.

2. Consideraţii teoretice

2.1 VHDL – scurt istoric

VHDL nu este un limbaj de programare, ci un limbaj de descriere a


sistemelor electronice hardware pornind de la structura lor modulară şi de la
interconexiunile dintre acestea. El a fost definit şi integrat în rândul
instrumentelor de CAD (Computer-Aided Design) din domeniul
electronicii, pentru a introduce o metodologie riguroasă de proiectare în
ciclul de dezvoltare al sistemelor hardware.
VHDL a devenit un limbaj industrial standardizat, utilizat pentru
descrierea hardware de la nivelul abstract până la nivelul concret. VHDL a
fost rapid asimilat ca un mediu universal de comunicaţie în proiectare. Toţi
producătorii de staţii de lucru şi de software CAE (Computer-Aided
Engineering) îşi standardizează produsele pentru a avea intrări şi ieşiri
standard VHDL. Aceste produse includ software pentru simulare, sinteză şi
trasare de cablaj imprimat.
Limbajul provine din programul VHSIC (Very High Speed
Integrated Circuit) iniţiat de Departamentul Apărării din Statele Unite ale
Americii în 1980. În faţa importantei creşteri a complexităţii sistemelor
electronice şi mai ales a costurilor de întreţinere rezultante, s-a făcut simţită
nevoia apariţiei unui limbaj modern şi standardizat.
Necesitatea unei descrieri lipsite de ambiguitate a sistemelor
hardware a apărut în mod pregnant la Departamentul Apărării (DOD) al
10 LIMBAJUL VHDL
Statelor Unite la începutul anilor 1980. Efortul de standardizare a fost
eşalonat între anii 1983 şi 1987 sub egida DOD. Efortul prestat de
numeroase societăţi americane, grupate în jurul lui INTERMETRICS, IBM
şi TEXAS INSTRUMENTS, a dus la apariţia normei IEEE 1076B, aprobată
în 10 decembrie 1987. Îmbunătăţirea standardului VHDL este supervizată de
IEEE. Ultima variantă a fost publicată în 2009.

2.2 Ce este un limbaj de descriere hardware?

VHDL este un limbaj de descriere hardware: el permite descrierea


funcţionării componentelor unui sistem hardware, precum şi a relaţiilor
dintre aceste componente şi a legăturilor lor cu exteriorul.
Există o strânsă similitudine între proiectarea hardware şi proiectarea
software. Tot ceea ce ţine de metoda de specificare, de organizarea software-
ului, de algoritmică, poate fi transpus direct la nivel hardware. Acest lucru
este posibil deoarece VHDL conţine toate elementele de descriere
algoritmică proprii limbajelor de programare. De aceea este adeseori
considerat drept un limbaj informatic sau chiar drept un limbaj de
informaticieni. Este deci preferabil ca programatorul începător în VHDL să
posede cunoştinţe minime într-un limbaj de programare structurată de nivel
înalt, pe lângă cunoştinţe de proiectare a sistemelor numerice.
Spre deosebire de un limbaj de programare, un limbaj de descriere
hardware nu vizează o „executare”, chiar dacă are un simulator asociat. Un
limbaj de descriere hardware poate servi la:
- demonstraţii formale („oare circuitul face într-adevăr
transformata Fourier?”);
- sinteză (în acest caz, limbajul are rolul de a furniza intrări unui
instrument „inteligent” pentru realizarea rapidă a prototipului
hardware);
- elaborarea de specificaţii;
- elaborarea de documentaţii.
Un limbaj de descriere hardware propune adeseori mai multe
niveluri de descriere. O descriere VHDL poate fi:
- structurală;
- comportamentală;
- de tip „flux de date”;
- o combinaţie a acestor trei tipuri de bază.
INTRODUCERE ÎN VHDL 11
Nivelul cel mai uşor de înţeles este cel structural. O descriere pur
structurală constă în a descrie modelul prin structura sa, adică printr-un
ansamblu de elemente interconectate. De aici se pot deduce imediat două
proprietăţi esenţiale:
- o asemenea descriere nu ia în calcul timpul;
- o asemenea descriere nu poate constitui o „frunză” (element
terminal în ierarhia de descriere) în momentul simulării. O frunză
va fi în mod necesar descrisă în stil comportamental sau „flux de
date”.
În cele ce urmează oferim un prim exemplu de descriere structurală:

Figura 1.1 Poarta ŞI

-- specificaţia unei porţi ŞI


entity AND_B is
port (A,B: in bit;
Y: out bit);
end AND_B;
architecture ARHITECTURA_1 of AND_B is
begin
Y <= A and B;
end ARHITECTURA_1;

Figura 1.2 Circuit cu două porţi ŞI - descriere structurală


12 LIMBAJUL VHDL
-- Specificaţie structurală: circuit conţinând două porţi ŞI
entity PORŢI_ŞI is
port (X1, X2, X3: in bit;
Y: out bit);
end PORŢI_ŞI;
architecture ARHITECTURA_STRUCTURALĂ of PORŢI_ŞI is
component AND_B
port (A, B: in bit;
Y: out bit);
end component AND_B;
signal Y_TEMP: bit;
begin
U1: AND_B port map (X1, X2, Y_TEMP);
U2: AND_B port map (Y_TEMP, X3, Y);
end ARHITECTURA_STRUCTURALĂ;

Descrierea de nivel comportamental urmăreşte să descrie


funcţionarea (comportamentul) unui model fără a se preocupa de o
eventuală împărţire în blocuri, mai apropiată de nivelul implementării fizice.
Descrierea capătă adeseori forma unui algoritm de genul celor utilizaţi în
limbajele de programare clasice. Acest nivel de descriere, deşi este prezent
în „frunzele” ierarhiei, se poate adresa şi proiectanţilor care doresc să
modeleze un sistem la un nivel înalt de abstractizare. Aici poate interveni şi
timpul. A modela sistemul în stil comportamental înseamnă a fi preocupat
de funcţionalitatea modelului descris, ceea ce face ca adeseori să se
folosească termenul de „descriere funcţională” în loc de „descriere
comportamentală”.

-- specificaţie comportamentală: circuit conţinând 2 porţi ŞI


entity PORŢI_ŞI is
port (X1, X2, X3: in bit;
Y: out bit);
end PORŢI_ŞI;

architecture ARHITECTURA_COMPORTAMENTALĂ of PORŢI_ŞI is


signal Y_TEMP: bit;
begin
PORŢI: process (X1, X2, X3, Y_TEMP)
begin
Y_TEMP <= X1 and X2;
Y <= Y_TEMP and X3;
end process PORŢI;
end ARHITECTURA_COMPORTAMENTALĂ;
INTRODUCERE ÎN VHDL 13
O descriere de tip „flux de date”, care nu este decât o formă
prescurtată a unei descrieri comportamentale, exprimă fluxurile datelor care
ies din model în funcţie de intrările primite, fără a se preocupa de structura
acestuia.

-- specificaţie „flux de date”: circuit conţinând 2 porţi ŞI


entity PORŢI_ŞI is
port (X1, X2, X3: in bit;
Y: out bit);
end PORŢI_ŞI;

architecture ARHITECTURA_FLUX_DE_DATE of PORŢI_ŞI is


signal Y_TEMP: bit;
begin
Y_TEMP <= X1 and X2;
Y <= Y_TEMP and X3;
end ARHITECTURA_FLUX_DE_DATE;

Obiectivul unui limbaj de programare îl constituie descrierea unei


execuţii a unui program, în vreme ce un limbaj precum VHDL are ca
obiectiv descrierea structurii hardware a unui sistem. Partea de hardware
are o structură fixă a cărei stare logică evoluează în decursul timpului.
Această evoluţie face parte din descrierea în VHDL a sistemului. Se poate
spune că un program VHDL are o structură fixă şi o execuţie evolutivă.
Deosebirea esenţială constă în faptul că programul este secvenţial,
în vreme ce descrierea hardware este concurentă. Aceasta este o problemă
de interpretare a codului: este posibil să se dea o descriere hardware cu
ajutorul unui limbaj de programare modificând semantica limbajului.
O deosebire majoră între un limbaj de programare şi un limbaj de
descriere pur structural este separarea elementelor lor constructive de bază:
sub-programul - în cazul limbajului algoritmic, şi componenta - în cazul
limbajului de descriere.
Sub-programul este apelat la un anumit moment dat, îşi îndeplineşte
misiunea, apoi este oarecum „uitat”.
Componenta există în sine, căci o descriere hardware la nivel
structural este „în afara timpului”. Componentele unui sistem există în mod
static şi funcţionează în mod concurent. De fapt, timpul nu intervine decât în
partea „flux de date” (sau comportamentală) a unui asemenea limbaj, care
permite scrierea unei instrucţiuni de genul: „lui A i se atribuie valoarea ‘0’,
apoi, după 10 ns, valoarea ‘1’, apoi după încă 5 ns valoarea ‘0’ ” etc.
14 LIMBAJUL VHDL
O altă deosebire dintre cele două categorii de limbaje este dată de
abstracţiunile purtătoare de valori. Într-un limbaj de programare, o variabilă
are o durată de viaţă limitată de cea a sub-programului care o conţine, şi
poartă succesiv (sau poate purta) mai multe valori. Un semnal în VHDL
există de-a lungul întregii simulări (dacă se efectuează simularea) şi
păstrează într-o agendă sui-generis lista valorilor care au fost generate de
interacţiunea sa cu celelalte semnale, sau care i-au fost transmise de către
sub-descrierile funcţionale sau „flux de date”. De asemenea, el poartă în
mod implicit lista valorilor pe care le-a luat în trecutul său (aşa-numitul său
„istoric” - history).
La modul general, o atribuire de variabilă (notată cu „:=” în VHDL)
are un efect limitat în timp.

A := B; -- instrucţiune de asignare de variabilă

înseamnă că variabila A ia valoarea B atunci când se execută instrucţiunea,


în vreme ce atribuirea de semnal (notată cu „<=” în VHDL).

S <= E; -- instrucţiune de asignare de semnal

semnifică un scurt-circuit permanent între semnalele S şi E (dacă atribuirea


este concurentă).

2.3 Avantaje şi dezavantaje ale VHDL

Principalul inconvenient al VHDL este complexitatea sa. Ea nu


trebuie deloc neglijată îndeosebi datorită faptului că limbajul se adresează
proiectanţilor de sisteme electronice care nu au în mod necesar cunoştinţe
foarte temeinice de programare.
În schimb, VHDL este un standard IEEE recunoscut de către toţi
producătorii de instrumente CAD. Perenitatea garantată de normă le permite
producătorilor să investească într-un instrument care nu este doar o modă
efemeră. Aceste investiţii se reflectă în multitudinea de instrumente şi de
biblioteci de modele care apar pe piaţă. Disponibilitatea unor modele
comportamentale şi structurale ale circuitelor existente permite efectuarea
simulării componentelor în contextul lor operaţional. Invers, un proiectant ar
fi putut ezita să integreze în sistemul său o componentă al cărei model
INTRODUCERE ÎN VHDL 15
VHDL nu este disponibil, această componentă interzicându-i simularea
ansamblului.
Deocamdată, nu se pune problema apariţiei vreunui alt limbaj de
descriere hardware care să înlocuiască VHDL. În plus, DOD din S.U.A
susţine în continuare standardul VHDL şi solicită, de exemplu, descrierea în
VHDL a fiecărui circuit integrat comandat.
Din punct de vedere tehnic, VHDL este un limbaj modern, puternic
şi general:
- Lizibilitatea VHDL este excelentă, graţie unei structurări
riguroase a expresiilor utilizate;
- Modularitatea VHDL este remarcabilă, graţie organizării sub
formă de componente şi a parametrilor generici - care permit
parametrizarea dimensiunilor componentelor;
- Securitatea în exploatare este foarte mare, graţie declarării
legăturilor externe separat de descrierea internă a unei
componente şi datorită tipizării legăturilor externe şi a modurilor
asociate acestora (intrare, ieşire, intrare / ieşire);
- Fiabilitatea descrierilor VHDL este deosebită.
Aceste caracteristici decurg direct din conceptele moderne (specifice
limbajelor de programare clasice) aplicate în VHDL: unităţile de compilare
separate, tipizarea puternică sau existenţa parametrilor generici.
VHDL mai oferă (printre altele):
- o bună tratare a concurenţei;
- o definiţie foarte solidă a noţiunii de timp, care permite o
descriere precisă şi sintetică a evoluţiei temporale a sistemului
descris, ceea ce constituie însăşi baza descrierii unui sistem
hardware, graţie unei semantici specifice introduse în limbaj;
- posibilitatea combinării stilurilor de descriere: structurală,
comportamentală şi „flux de date”;
- posibilitatea definirii unor funcţii de rezoluţie (de gestiune a
conflictelor) evoluate.
Ciclul de proiectare al unui sistem hardware se împarte, la modul
tradiţional, în trei faze:
- faza de proiectare propriu-zisă (un fel de analiză funcţională a
problemei);
- faza de realizare (codificare);
- faza de depanare.
16 LIMBAJUL VHDL
Cu cât o eroare este depistată mai rapid în ciclul de proiectare, cu
atât mai ieftină este corectarea sa. Aşadar, toate limbajele moderne (printre
care şi VHDL) se străduiesc, prin intermediul verificărilor efectuate, să
detecteze cât mai multe erori încă de la compilare. Prin urmare, utilizarea
VHDL are o influenţă foarte puternică asupra ciclului de proiectare al
produsului.
Un alt aspect remarcabil al VHDL este dat de proiectarea modulară
şi ierarhizată pe care o induce acest limbaj. Ea le permite responsabililor să
schimbe specificaţiile sau realizarea unei componente a produsului foarte
târziu în cadrul ciclului de proiectare fără a genera o adevărată catastrofă
(cum era cazul până la apariţia acestui gen de limbaj).

În sumar, avantajele VHDL sunt:

1. Limbaj complet - acoperă diferitele etape ale proiectării


sistemelor numerice: specificare, modelare, simulare, sinteză. Nu
mai este necesară învăţarea câte unui limbaj diferit pentru fiecare
dintre aceste etape;
2. Limbaj independent - fiind un standard IEEE, VHDL nu aparţine
nimănui care să acapareze evoluţia şi utilizarea sa. Independenţa
sa faţă de arhitecturi, sisteme gazdă (PC-uri sau staţii de lucru) şi
tehnologie este totală;
3. Limbaj flexibil - VHDL îi permite utilizatorului să-şi efectueze
descrierile plasându-se la diferite nivele de abstractizare:
comportamental, „flux de date” sau structural;
4. Limbaj modern - VHDL are o sintaxă lizibilă, este puternic
tipizat şi controlat, minimizează riscul de erori şi propagarea lor
insidioasă, favorizează calitatea. VHDL permite proiectarea
modulară, arhivarea şi re-utilizarea codului;
5. Limbaj standard - este o garanţie a compatibilităţii, a
portabilităţii, a stabilităţii şi a perenităţii;
6. Limbaj deschis - un limbaj standardizat acum 10 ani n-ar avea
nici o şansă de a rezista dacă nu ar evolua. Dar evoluţiile sunt
controlate, devenind şi ele standard. De altfel, VHDL suportă
mecanisme de funcţii şi proceduri care le permit proiectanţilor
să extindă funcţionalităţile limbajului, respectând principiul
portabilităţii şi o serie de reguli foarte stricte.
INTRODUCERE ÎN VHDL 17
2.4 Caracteristici generale

2.4.1 Organizarea în biblioteci


VHDL este un limbaj modular. Stilul de lucru constă în scrierea unor
unităţi mici, ierarhizate. Anumite părţi ale acestor descrieri pot fi compilate
separat. Ele sunt suficiente pentru a fi înţelese: acestea sunt unităţile de
proiectare.
Întrucât complexitatea sistemelor de modelat este adeseori
considerabilă, munca de echipă se dovedeşte din ce în ce mai necesară.
Munca de echipă necesită însă o anumită disciplină, sau metodologie, care
este oferită - parţial - de către VHDL.
De fiecare dată când o unitate de proiectare VHDL este considerată
corectă de către compilatorul VHDL, ea este automat plasată într-o aşa-
numită bibliotecă de lucru generată de mediul VHDL. Proiectantul lucrează
în biblioteca sa proprie, făcând referinţă, dacă este nevoie, la biblioteca
comună a proiectului sau la alte biblioteci aparţinând colegilor săi, care
conţin utilitare sau modele generale. Acestea sunt, pentru el, biblioteci de
resurse.
Toate verificările de coerenţă relative la biblioteci sunt asumate de
către compilatorul VHDL: este rolul său de „bibliotecar”. Aceste biblioteci
nu conţin decât unităţi de proiectare. Un fişier conţinând cod sursă VHDL,
odată analizat şi compilat, nu mai există pentru proiectant; numai unităţile
de proiectare rezultate sunt plasate în biblioteci. În VHDL, compilăm fişiere
şi utilizăm (referim) unităţi de proiectare.

2.4.2 Încapsularea în unităţi de proiectare


O descriere hardware este constituită dintr-un ansamblu de modele şi
de algoritmi utilizaţi pentru acele modele.
Un model poate fi văzut ca având două părţi:
- partea externă, care arată conexiunile sale cu lumea exterioară;
- partea internă, care descrie realizarea sa sub formă de
interconexiuni ale altor modele mai simple sau pur şi simplu sub
formă de algoritmi.
Un model VHDL este o pereche entitate / arhitectură, partea sau
vederea sa externă fiind numită entitate, iar vederea sa internă - arhitectură.
Fiecare dintre aceste două părţi este o unitate de proiectare şi aceste
unităţi alcătuiesc bibliotecile VHDL. O bibliotecă poate conţine, de
exemplu, o sută de unităţi de proiectare. Separarea entităţii de arhitectură
18 LIMBAJUL VHDL
apare pentru a permite modificarea uşoară a funcţionării interne fără a mai fi
necesară redefinirea viziunii externe a modelului, lucru permis de
compilarea separată.
În plus, aceeaşi vedere externă poate avea mai multe vederi interne
asociate, acestea din urmă depinzând adeseori de gradul de fineţe dorit
pentru descrierea funcţionării modelului. Totuşi, pentru o anumită simulare
dată, va fi necesară alegerea unei vederi interne precise asociate modelului.
Algoritmii utilizaţi frecvent pot fi separaţi, obţinându-se astfel sub-
programe care sunt adeseori grupate în pachete. Aceste pachete (packages)
sunt supuse aceleiaşi împărţiri ca şi modelele: vederea lor externă, adică
perspectiva tuturor posibilităţilor pe care le oferă (pe care le „exportă”), este
decuplată de vederea internă care conţine algoritmica ce realizează toate
aceste funcţionalităţi. În terminologie VHDL, vederea externă se numeşte
specificaţia pachetului, pe când vederea internă se numeşte corpul
pachetului. Pachetele nu se limitează doar la a exporta sub-programe, ci pot
oferi şi alte obiecte, cum ar fi semnale globale, tipuri şi componente.
Separarea interfeţei sub-programului de corpul său permite
modificarea unui algoritm fără a se modifica şi „imaginea” (vederea) pe care
o are lumea exterioară (celelalte modele şi pachete) asupra pachetului
corespunzător.
Pentru a nu bloca relaţia instanţă / model, modul de asociere al
componentelor şi modelelor va forma o unitate de proiectare de sine
stătătoare, care va fi şi ea stocată într-o bibliotecă. Această unitate va efectua
corespondenţa dintre instanţă şi model (cuplul entitate / arhitectură al cărui
„reprezentant” este). Ea este o configuraţie VHDL, ultima categorie de
unitate de proiectare care poate fi găsită într-o bibliotecă VHDL.

2.4.3 Separarea în domeniul concurent şi domeniul secvenţial


Un sistem hardware este în mod natural concurent. Nu trebuie însă
trasă concluzia pripită că descrierile secvenţiale ar fi inutile: de exemplu, un
protocol de citire din memorie se exprimă cel mai firesc în mod secvenţial:
poziţionăm semnalul X, apoi semnalul Y, aşteptăm un interval de timp ∆t
etc.
În VHDL, domeniile concurent şi secvenţial vor coabita. Proiectantul
va avea posibilitatea de a exprima fiecare parte a descrierii sale în domeniul
care i se pare cel mai adecvat. Fiecare dintre aceste domenii are setul său de
instrucţiuni.
INTRODUCERE ÎN VHDL 19
2.4.4 Clasificarea după tipuri
Toate limbajele de programare moderne utilizează tipizarea
obiectelor manipulate şi oferă posibilitatea creării de noi tipuri. Astfel se
obţine o creştere a puterii verificărilor şi o limitare a propagării erorilor.
Fiecare obiect va fi clasificat într-un tip menit să definească valorile
pe care le poate lua şi cele care-i sunt interzise, operaţiile permise şi cele
interzise.
Orice obiect are în mod obligatoriu un tip pe care nu şi-l schimbă
niciodată. Există patru familii de tipuri:
- tipurile scalare (întregi, flotanţi, tipuri fizice şi tipuri enumerate);
- tipurile compuse (tablouri şi articole);
- tipurile acces sau poantorii (pointers);
- tipurile fişier.

2.4.5 Clasele de obiecte


În VHDL există trei clase de obiecte: constantele, variabilele şi
semnalele.
Constantele au o valoare fixă definită o dată pentru totdeauna, cel
mai târziu după o fază de iniţializare numită elaborare.
Variabilele au o valoare modificabilă prin atribuire. Constantele şi
variabilele sunt obiecte ce pot fi întâlnite şi în limbajele de programare.
Semnalele sunt specifice limbajelor de descriere hardware. Ele
modelează informaţia care tranzitează prin fire, magistrale sau - la modul
general - între componentele hardware.

2.4.6 Reguli de scriere în VHDL


Comentariile încep cu două liniuţe „--” şi se continuă până la
sfârşitul liniei. Dacă e necesară continuarea comentariului pe linia
următoare, este din nou necesar să apară cele două liniuţe:

A <= B and C ; -- în VHDL simbolul <= efectuează o


-- atribuire: A ia valoarea operaţiei (B
-- and C)

VHDL nu face deosebirea dintre literele mari şi mici: SALUT este


identic cu salut sau SAluT.
20 LIMBAJUL VHDL
VHDL manipulează o mare diversitate de obiecte: semnale, pachete,
procese etc. desemnate prin numele lor. Regulile de denumire sunt acelaşi
pentru toate aceste obiecte:
- Numele lor este alcătuit dintr-o serie de caractere alfanumerice
(cele 26 de litere ale alfabetului), numerice (cele 10 cifre
zecimale) sau din caracterul „_”. Sunt excluse caracterele ASCII
speciale;
- Primul caracter trebuie să fie o literă;
- Caracterul „_” nu are voie să se afle la sfârşitul unui nume, nici
să apară de două ori consecutiv;
- Numele nu are voie să fie un cuvânt VHDL rezervat;
- Lungimea oricărui nume nu poate depăşi o linie. Această
toleranţă permite atribuirea de nume explicite, preferate de obicei
în locul abrevierilor. Caracterul „_” este adeseori utilizat ca
separator pentru numele „compuse”.
Există o serie de cuvinte cheie care nu pot fi utilizate ca
identificatori:

abs access after alias


all and architecture array
assert attribute begin block
body buffer bus case
component configuration constant disconnect
downto else elsif end
entity exit file for
function generate generic group
guarded if impure in
inertial inout is label
library linkage literal loop
map mod nand new
next nor not null
of on open or
others out package port
postponed procedure process protected
pure range record register
reject rem report return
rol ror select severity
signal shared sla sll
sra srl subtype then
to transport type unaffected
units until use variable
wait when while with
xnor xor
INTRODUCERE ÎN VHDL 21
Literalii sunt valori explicite atribuite diferitelor obiecte: constante,
variabile, atribute etc. Notaţia lor diferă în funcţie de tipul obiectelor cărora
li se aplică:
- numere întregi zecimale: este cazul cel mai simplu, folosindu-se
notaţia zecimală obişnuită, fără simboluri suplimentare. De exemplu, 22 sau
1971. Există posibilitatea includerii caracterului „_” pentru ameliorarea
lizibilităţii, fără nici o altă consecinţă: 1_999_234 este identic cu 1999234.
- caractere: se scriu între apostrofuri simple: 'A', '%', 'f', ' ' (spaţiu).
Caracterele acceptate sunt cele din setul ASCII. Atenţie, literele mari şi cele
mici sunt diferite atunci când este vorba despre valori literale: 'D' nu este
identic cu 'd'.
- şiruri de caractere: se scriu între ghilimele. Iată câteva exemple:
"salut", "Salut" (aceste două valori literale sunt diferite), "1001", "E*". Un
şir de caractere se poate obţine prin concatenarea mai multor şiruri cu
ajutorul operatorului &: "Totul " & "este " & "minunat" este echivalent
cu "Totul este minunat". Această posibilitate permite îndeosebi utilizarea
mai multor linii pentru definirea unui şir, de exemplu:
"Totul" &
"este" &
"minunat";
Operaţia de concatenare în sine nu adaugă nici un spaţiu - va trebui
deci urmărită cu atenţie prezenţa spaţiilor în şirurile care urmează să fie
concatenate.
- biţi: se utilizează notaţia caracterelor. Biţii pot avea ca valori
numai '0' şi '1'. Tipul STD_LOGIC utilizează în plus valorile 'U', 'X', 'H', 'L',
'W', 'Z' şi '-'.
- vectori de biţi: utilizează notaţia şirurilor de caractere, constituite
din simbolurile 0 şi 1: de exemplu, "10011010". Sistemul de numeraţie
implicit este cel binar, dar este posibilă utilizarea notaţiei octale sau
hexazecimale prin prefixarea şirului: O"127", X"01AC".
Spaţiile nu sunt semnificative, cu excepţia apariţiei lor în valorile
literale şi în cazurile de separare a identificatorilor. Ele nu sunt necesare
între identificatori şi simboluri. Următoarele două linii sunt corecte şi
echivalente:

A <= B and C;
A<= B and C ;
22 LIMBAJUL VHDL
O instrucţiune se poate întinde pe mai multe linii, atâta timp cât
sfârşitul de linie nu este plasat în mijlocul unui identificator, al unui simbol
de operator sau al unui literal.

A <= B when C = '0' else D;

se poate scrie şi:

A <= B when C = '0'


else D;

Sfârşitul unei instrucţiuni este semnalat de prezenţa caracterului „;”.

2.4.7 Structura unei descrieri în VHDL

Orice descriere VHDL se descompune în mod ierarhic, la fel cum un


program se descompune în sub-programe. Se porneşte, la nivelul rădăcinii,
cu sistemul complet văzut ca o cutie neagră, cu intrările şi ieşirile sale.
Această cutie neagră este descompusă în componente interconectate, care
sunt la rândul lor văzute ca nişte cutii negre. Se ajunge la un arbore care
poate fi definit în stil ascendent (bottom-up) sau descendent (top-down),
asemănător descompunerii ierarhice a unui program în funcţii.
Frunzele acestei ierarhii corespund componentelor elementare
descrise în manieră algoritmică secvenţială. Modul de descriere a acestor
componente elementare depinde de nivelul de abstractizare al descrierii
(specificare, proiectare, realizare). Criteriile după care se decide oprirea
descompunerii ierarhice sunt legate de complexitatea componentelor
terminale şi de conectivitatea acestor componente între ele. Oprirea
descompunerii ierarhice corespunde obţinerii unui nivel de complexitate
suficient de redus, care să permită trecerea la o descriere algoritmică a
funcţionalităţii componentelor de la nivelul cel mai de jos, care poate fi ea
însăşi descompusă în mod ierarhic în manieră software, sub formă de apeluri
imbricate de sub-programe. Conţinutul unei componente descrie ieşirile în
funcţie de intrările şi de stările sale interne.
Limita descompunerii ierarhice poate depinde de numărul de intrări
şi de ieşiri ale componentelor terminale, precum şi de dimensiunea şi deci de
complexitatea codului fiecărei componente. Acest criteriu se reduce aşadar
INTRODUCERE ÎN VHDL 23
la izolarea corectă a funcţionalităţilor, astfel încât acestea să fie pe cât
posibil independente unele faţă de celelalte.
Uneori este dificil să „spargem” ierarhia fără a fi început descrierea
internă a unei componente. În cursul acestei descrieri se poate opera o nouă
etapă de descompunere, atunci când o parte a componentei a fost deja scrisă.
Un alt criteriu de descompunere locală îl poate constitui separarea
între partea de control şi cea de prelucrare a datelor, numită şi cale de date
(data path).
Felul în care se va face în cele din urmă descompunerea depinde la
modul esenţial de experienţa proiectantului şi de familiarizarea sa cu
problema de rezolvat.

3. Desfăşurarea lucrării

3.1 Se va lansa în execuţie mediul de dezvoltare Active-HDL. Se


consultă documentaţia on-line referitoare la utilizarea editoarelor,
a simulatorului şi a compilatorului de fişiere VHDL.
3.2 Se vor efectua operaţiile necesare pentru descrierea şi compilarea
porţilor logice fundamentale ŞI, SAU, NU, SAU-NU, ŞI-NU,
SAU-EXCLUSIV, COINCIDENŢĂ.
3.3 Se vor efectua operaţiile necesare pentru simularea porţilor
logice fundamentale de la punctul anterior.
LUCRAREA NR. 2
UNITĂŢI FUNDAMENTALE DE PROIECTARE

1. Scopul lucrării

Se prezintă unităţile fundamentale de proiectare în VHDL: perechea


entitate / arhitectură, configuraţiile, specificaţiile de pachete şi corpurile
pachetelor. Se studiază sintaxa declaraţiilor respective, precum şi cadrul lor
de utilizare. Se detaliază avantajele şi dezavantajele specifice operării cu
unităţile respective.

2. Consideraţii teoretice

2.1 Noţiuni introductive

VHDL este un limbaj bazat pe biblioteci, ceea ce înseamnă că după


compilarea unui fişier sursă corect, rezultatul este stocat într-o bibliotecă,
gestionată de către VHDL; cel mai adesea, ea este stocată într-un director
sau subdirector pe care de obicei proiectantul nu are de ce să-l acceseze.
Există anumite părţi ale programelor care pot fi compilate separat:
acestea sunt unităţile de proiectare. Fiecare compilare reuşită a unui fişier
(realizată de către sistemul de operare sau de mediul de dezvoltare VHDL)
va fi susceptibilă să producă în biblioteca curentă una sau mai multe unităţi
de proiectare. Mai multe unităţi de proiectare pot fi scrise în acelaşi fişier,
dar o unitate de proiectare nu poate fi divizată în mai multe fişiere.
Se recomandă delimitarea cât mai exactă a noţiunilor de unitate de
proiectare şi de fişier: o unitate de proiectare va avea propriul său fişier,
chiar dacă acest lucru nu este obligatoriu. Această abordare permite evitarea
recompilării inutile a unităţilor de proiectare.
În cazul prezenţei mai multor unităţi de proiectare în acelaşi fişier,
există posibilitatea decelării unei erori în cadrul uneia dintre ele; în
consecinţă, analiza efectuată de compilator nu va fi dusă până la capăt. În
acest caz, compilatorul va analiza şi va compila unităţile de proiectare din
fişier care se află înaintea unităţii incriminate. În anumite cazuri, el va
UNITĂŢI FUNDAMENTALE DE PROIECTARE 25
încerca şi chiar va reuşi să compileze unităţile situate după unitatea de
proiectare respectivă.
Deci noţiunea de fişier se pierde complet după compilare: nu se mai
vorbeşte despre fişiere, ci numai de unităţi de proiectare (se va vorbi despre
fişiere în VHDL la nivelul tipurilor de date - acest concept este cu totul
diferit de cel discutat aici).

2.2 Unităţi de proiectare primare şi secundare

În VHDL există în total cinci tipuri de unităţi de proiectare diferite,


dintre care trei sunt considerate „primare” şi două „secundare”. O unitate
primară descrie o vedere externă, pe când o unitate secundară descrie
vederea internă.
De exemplu, specificaţia unui pachet va descrie sub-programele pe
care le propune (pe care le exportă) pachetul, tipurile sau obiectele
manipulate de acesta, însă fără a intra în detaliile (algoritmul) implementării.
Specificaţia unui pachet este o unitate de proiectare primară.
Partea de implementare a pachetului (corpul pachetului) conţine
algoritmii utilizaţi. Această informaţie nefiind primordială pentru cel care
utilizează pachetul, ea îi este ascunsă. Corpul pachetului este o unitate de
proiectare secundară. Entitatea este o unitate de proiectare primară, în vreme
ce arhitectura este o unitate de proiectare secundară.
Există o analogie fundamentală, care merită să fie reţinută, între
specificaţia unui pachet şi entitate - ca vederi externe ale unor cutii negre
(hardware sau software). De altfel, se poate deduce imediat şi analogia
dintre corpul pachetului şi arhitectură, aceste două unităţi de proiectare
servind la descrierea realizării cutiei negre (hardware sau software).
Cele patru unităţi de proiectare sunt completate de o a cincia:
configuraţia. Această unitate face legătura între instanţe de componente
utilizate într-o arhitectură şi realizarea lor efectivă prin intermediul unor
perechi entitate / arhitectură. Ea este considerată a fi o unitate primară.
În concluzie, cele cinci tipuri de unităţi de proiectare sunt:
- Entitatea;
- Arhitectura;
- Configuraţia;
- Specificaţia pachetului;
- Corpul pachetului.
26 Limbajul VHDL
2.3 Biblioteci

În VHDL putem avea un număr mare de biblioteci, fiecare dintre ele


conţinând mai multe unităţi de proiectare.
La un moment dat, una singură dintre aceste biblioteci este numită
bibliotecă de lucru, celelalte fiind biblioteci de resurse. Alegerea bibliotecii
de lucru este făcută în afara limbajului, printr-o comandă specifică
platformei VHDL utilizate. Ea poate fi referită în VHDL prin numele logic
WORK. Concret, în această bibliotecă vor fi introduse unităţile de proiectare
compilate cu succes. Unităţile de proiectare primare aparţinând bibliotecilor
de resurse pot fi referite. Pentru aceasta este necesar, mai întâi de toate, ca
biblioteca în care se găsesc să fie referită de o clauză library, care trebuie să
preceadă unitatea de proiectare care o apelează. În continuare, referirea
unităţii se poate face printr-o clauză use într-o zonă de declaraţii oarecare
(fără a uita sufixul .all pentru pachete, dacă dorim să accesăm totalitatea
„exporturilor” unui pachet). Aşadar, se scrie:

library biblioteca_în_care_este_păstrată_unitatea_primară;
...
use numele_entităţii; -- dacă unitatea primară e o
--specificaţie de entitate

use numele_configuraţiei; -- dacă este o configuraţie


use numele_pachetului.all; -- dacă este un pachet

Bibliotecile WORK şi STD (care conţine pachetele aşa-zis standard)


fac obiectul unei clauze library implicite, ceea ce înseamnă că fiecare
unitate de proiectare este implicit precedată de clauzele library şi use:

library WORK, STD;


use STD.STANDARD.all;

2.4 Entitate, arhitectură şi configuraţie

Orice sistem fizic comunică cu exteriorul prin intermediul unor


semnale care sunt grupate în aşa-zisa sa interfaţă cu exteriorul. Semnalele
primite de sistemul respectiv sunt orientate dinspre exterior către sistem şi
sunt numite semnale de intrare sau - mai simplu - intrări sau stimuli.
UNITĂŢI FUNDAMENTALE DE PROIECTARE 27
Reacţionând la stimulii primiţi, sistemul va genera, la rândul său, semnale
de ieşire sau răspunsuri, transmise mediului său exterior.
Interfaţa este un element fundamental al oricărui sistem fizic (în
categoria de sistem putem include, în această abordare, şi sistemele vii -
fiinţele), în lipsa sa fiind de neconceput interacţiunea sistemului cu
exteriorul. În realitate nu există sistem care să fie complet izolat de mediul
său exterior.
În VHDL, interfaţa este descrisă cu ajutorul declaraţiei de entitate,
iar structura internă a sistemului - cu ajutorul declaraţiei de arhitectură.

2.4.1 Entitatea
Entitatea descrie interfaţa unică dintre arhitecturi şi mediul exterior,
iar arhitectura descrie funcţionalitatea entităţii. Ansamblul poate fi văzut şi
utilizat, la un nivel superior, ca o componentă.
În funcţie de gradul de fineţe sau, pur şi simplu, de nivelul de
descriere (structural, comportamental, flux de date sau hibrid) care a fost
ales pentru descrierea funcţionalităţii, vom putea avea mai multe arhitecturi
diferite pentru aceeaşi entitate.
Această grupare în entităţi şi arhitecturi conferă o mare modularitate
modelării în VHDL. Modificarea unei arhitecturi sau înlocuirea sa nu va
avea nici o repercusiune asupra unităţilor de proiectare care fac referire la
entitatea corespunzătoare. Entitatea rămâne identică şi constituie singurul
element cunoscut de către mediul exterior; mediul nu „vede” arhitectura.
Dimpotrivă, informaţiile cuprinse în entitate sunt cunoscute de către
diversele sale arhitecturi. Toate aceste informaţii sunt opţionale. Ele sunt de
trei feluri:
- declaraţii de parametri generici;
- declaraţii de porturi;
- instrucţiuni.
Sintaxa unei entităţi este următoarea:

entity numele_entităţii is
{generic (listă de parametri generici)}
{port (listă de porturi)}
{begin
listă de instrucţiuni concurente}
end {numele_entităţii};
28 Limbajul VHDL
Acoladele delimitează informaţiile opţionale.
Numele entităţii este un identificator care trebuie să fie unic în
biblioteca dată. Pentru a desemna o pereche entitate / arhitectură, se notează:

numele_entităţii(numele_arhitecturii)

Numele entităţii, numit în mod formal identificator, are în primul


rând un rol de documentare. Din acest motiv, se recomandă ca acest nume să
fie alcătuit din substantive care să descrie cât mai exact menirea entităţii
respective. Regulile pe care trebuie să le respecte orice identificator au fost
prezentate în lucrarea nr. 1.
Lista parametrilor generici permite crearea unor entităţi foarte
flexibile şi deci reutilizabile - aspect care va fi detaliat în continuare.
Lista porturilor oferă o mulţime de informaţii relative la semnalele
care interfaţează entitatea. Se indică:
- numărul semnalelor;
- tipul acestora;
- valoarea lor implicită;
- sensul lor (intrare, ieşire, bidirecţional etc.).
Lista de instrucţiuni concurente permite scrierea o singură dată a
prelucrărilor care sunt globale pentru toate arhitecturile aferente. În practică,
restricţiile impuse1 limitează această porţiune de cod la un rol de control al
intrărilor entităţii (sau al parametrilor, în cazul entităţilor generice).
Să luăm exemplul următorului sumator complet:

A B

COUT Sumator complet CIN


de numere pe un bit

Figura 2.1 Vederea externă a unui sumator complet de numere pe un bit

1 Sunt acceptate numai instrucţiunile concurente al căror proces echivalent este pasiv, adică:
instrucţiunile concurente assert, anumite apeluri de proceduri şi anumite procese (aceste
instrucţiuni sunt prezentate ulterior).
UNITĂŢI FUNDAMENTALE DE PROIECTARE 29
Acestui sumator îi corespunde următoarea entitate:

entity SUMATOR_COMPLET is
port(A, B, CIN: in BIT;
S, COUT: out BIT);
end SUMATOR_COMPLET;

Porturile (punctele de intrare / ieşire) A, B, CIN, S şi COUT se


numesc porturi formale. Asta înseamnă că ele sunt relative la modelul
SUMATOR_COMPLET. În momentul referirii acestui sumator ca şi
componentă (component) sau în momentul utilizării sale (instanţiere),
aceste nume vor fi asociate unor valori sau semnale numite porturi actuale.
În esenţă, porturile sunt semnale şi posedă un tip care este indicat în
această specificaţie. Oarecum similar parametrilor procedurilor, fiecărui port
îi va fi atribuit un mod. Acest mod indică un port de intrare (in), un port de
ieşire (out) sau un port bidirecţional (inout). Mai există încă două moduri
(buffer şi linkage) care sunt mai deosebite şi vor fi descrise ulterior.
Un port formal poate fi:
- conectat la un alt port;
- conectat la un semnal local;
- lăsat neconectat (se foloseşte cuvântul cheie open).
Conexiunile pot fi verificate în foarte multe feluri datorită modurilor.
Astfel, un port formal în mod in al unui model nu poate fi conectat la un
port de mod out al uneia dintre componentele sale: intrarea modelului ar fi
atunci o ieşire a componentei înglobate de el. Figura 2.2 indică toate
conexiunile autorizate.

Modul componentei Modul Modul Modul Modul Modul


Portul entităţii in out inout buffer linkage
Port de mod in DA NU NU NU DA
Port de mod out NU DA NU NU DA
Port de mod inout DA DA DA NU DA
Port de mod buffer DA NU NU DA DA
Port de mod linkage NU NU NU NU DA
Semnal local DA DA DA DA DA
Port open (neconectat) NU DA DA DA DA
Figura 2.2 Conectarea porturilor formale
30 Limbajul VHDL
2.4.2 Arhitectura
Entitatea defineşte interfaţa sau vederea externă a unei cutii negre;
arhitectura descrie ceea ce se petrece (comportamentul) sau ce se găseşte în
interiorul său (structura).
Această descriere poate fi structurală (cutia neagră este o
interconectare a altor cutii negre), funcţională, „flux de date” (se dă
algoritmul pe care-l implementează cutia neagră) sau combinată.
Descrierile se completează adeseori una pe cealaltă, iar la simulare se
poate alege descrierea cea mai adecvată proiectului în curs. De aceea,
aceeaşi entitate poate avea mai multe arhitecturi asociate.
În VHDL entitatea şi arhitectura trebuie să se găsească în aceeaşi
bibliotecă.
Sintaxa unei arhitecturi este următoarea:

architecture numele_arhitecturii of numele_entităţii is


...
... Zona de declaraţii
...
begin
...
... Instrucţiuni concurente
...
end {numele_arhitecturii};

Întrucât arhitectura face parte din domeniul concurent, la acest prim


nivel nu se pot face declaraţii de variabile (cu excepţia variabilelor
partajate), ci numai de semnale. Tot astfel, funcţionalitatea acestei
arhitecturi este descrisă de către un ansamblu de instrucţiuni concurente
executate în manieră asincronă.
Iată câteva arhitecturi posibile ale entităţii a cărei specificaţie
(SUMATOR_COMPLET) a fost prezentată mai sus.
UNITĂŢI FUNDAMENTALE DE PROIECTARE 31
a) Descrierea structurală

architecture DESCRIERE_STRUCTURALĂ of SUMATOR_COMPLET is


component SEMI_SUMATOR
port(A, B: in BIT;
COUT, S: out BIT);
end component;

component POARTA_SAU
port(A, B: in BIT;
S: out BIT);
end component;

signal N1, N2, N3: BIT;


begin
--Interconectarea celor trei componente
C1: SEMI_SUMATOR port map (A, B, N1, N2);
C2: SEMI_SUMATOR port map (N2, CIN, N3, S);
C3: POARTA_SAU port map (N1, N3, COUT);
end DESCRIERE_STRUCTURALĂ;

Observaţie
Noţiunea de timp nu intervine într-o descriere structurală, care nu
reprezintă decât interconectarea unor elemente.

b) Descrierea „flux de date”


Sumatorul complet este descris de către următoarele ecuaţii:

V = A XOR B
S = V XOR CIN
COUT = (A and B) OR (V and CIN)

architecture DESCRIERE_FLUX_DE_DATE of SUMATOR_COMPLET is


signal INTER: bit;
begin
INTER <= A xor B after 1 ns;
S <= INTER xor CIN after 1 ns;
COUT <= (A and B) or (INTER and CIN) after 2 ns;
end DESCRIERE_FLUX_DE_DATE;
32 Limbajul VHDL
Observaţie
Conceptul de timp (de temporizare) intervine în cadrul acestui
exemplu numai pentru a ilustra posibilitatea temporizării unei asemenea
descrieri. O descriere lipsită de temporizări este la fel de valabilă.

c) Descrierea comportamentală
Această descriere este bazată pe tabelul de adevăr al sumatorului:

A B CIN S COUT
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 0 1
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1

de unde se poate deduce următoarea descriere:

architecture DESCRIERE_COMPORTAMENTALĂ of SUMATOR_COMPLET is


begin
process
variable L: integer;
constant TABEL_S: bit_vector(0 to 3):= "0101";
constant TABEL_COUT: bit_vector(0 to 3):= "0011";
begin
L:= 0;
if A = '1' then L:= 1; end if;
if B = '1' then L:= L + 1; end if;
if CIN = '1' then L:= L + 1; end if;
--Atribuiri de semnale
S <= TABEL_S(L) after 10 ns;
COUT <= TABEL_COUT(L) after 15 ns;
-- Monitorizăm modificările (evenimentele) de pe A, B şi CIN
wait on A, B, CIN;
end process;
end DESCRIERE_COMPORTAMENTALĂ;

Şi în acest caz este posibilă apariţia explicită a timpului.


UNITĂŢI FUNDAMENTALE DE PROIECTARE 33
d) Descrierea combinată
Am ales o descriere structurală pentru semi-sumator şi o descriere
comportamentală a porţii SAU.

architecture DESCRIERE_COMBINATĂ of SUMATOR_COMPLET is


component SEMI_SUMATOR
port(A, B: in bit;
COUT, S: out bit);
end component;
signal N1, N2, N3: bit;
begin
C1: SEMI_SUMATOR port map (A, B, N1, N2);
C2: SEMI_SUMATOR port map (N2, CIN, N3, S);
C3: COUT <= N1 or N3; -- aici ar putea interveni o
-- temporizare
end DESCRIERE_COMBINATĂ;

2.4.3 Configuraţia
Pentru a simula o instanţă a unei componente este necesară
cunoaşterea perechii entitate / arhitectură pe care aceasta o „utilizează”.
Această informaţie poate fi dată direct sau poate proveni de la configuraţie.
Începând cu versiunea '93 a VHDL există posibilitatea asocierii
directe şi într-o singură linie a unei perechi entitate / arhitectură cu o instanţă
a unei componente. Aceasta se realizează cu ajutorul unei instrucţiuni
numite „instanţiere directă” (descrisă în lucrarea nr. 9). De fapt, însăşi
noţiunea de „componentă” nu mai este necesară. Instrucţiunea este uşor de
pus în aplicare, dar suferă de lipsă de forţă şi de flexibilitate: numele entităţii
şi al arhitecturii sunt fixate într-un loc precis al codului sursă. Configuraţia –
deşi este un concept mai greu de asimilat - oferă mult mai multă
flexibilitate.
Conceptele de „componentă” şi de „configuraţie” sunt primordiale în
VHDL. De fapt, există două tipuri de configuraţii posibile:
a) De fiecare dată când utilizăm o componentă într-o descriere
VHDL, există posibilitatea de a o configura imediat, ceea ce înseamnă să
specificăm modelul al cărei instanţă este, parametrii săi (dacă este generic)
şi corespondenţa dintre porturile sale (numite porturi formale) şi porturile
actuale.
34 Limbajul VHDL
b) Cu toate acestea, în unele cazuri poate părea mai judicios să
amânăm această configuraţie pentru a o plasa în unitatea de proiectare cu
acelaşi nume (cu ajutorul cuvântului cheie configuration).
Forţa acestei unităţi de proiectare creşte atunci când este separată de
restul descrierii. Astfel, fără a modifica (deci fără a mai recompila) alte
unităţi de proiectare, va fi posibil, de exemplu, să schimbăm nivelul
descrierii unei componente (configurând o nouă arhitectură, poate chiar o
altă entitate).
Aspectele prezentate anterior în cazul unei componente sunt la fel de
adevărate şi în cazul unui bloc, element ierarhic intern unei arhitecturi care
va fi prezentat ulterior (lucrarea nr. 9).
Sintaxa unei configuraţii este următoarea:

configuration numele_configuraţiei of numele_entităţii is


{Zona declarativă (numai clauza use şi specificarea
atributelor)}
{Zona rezervată configuraţiei}
end {numele_configuraţiei};

Zona declarativă este extrem de redusă: în ea nu sunt autorizate decât


clauza use şi specificaţiile de atribute.
Zona rezervată configuraţiei propriu-zise descrie modul în care vor fi
realizate fizic componentele din interiorul blocurilor2.
Aceste aspecte se traduc printr-o serie de clauze for ... end for;
imbricate care desemnează blocul în care se găseşte componenta sau
componentele de configurat. În interiorul imbricării de clauze for,
configuraţia fiecăreia dintre componente este indicată precis şi are o sintaxă
de tipul:

for eticheta_instanţei_componentei: nume_componentă use


entity
numele_entităţii(numele_arhitecturii) {parametri generici}
{corespondenţa porturi formale / porturi actuale};
end for;

2 Blocul constituie elementul fundamental al ierarhiei în VHDL. O arhitectură este un bloc


şi putem avea blocuri (eventual imbricate) în interiorul unei arhitecturi.
UNITĂŢI FUNDAMENTALE DE PROIECTARE 35
Configuraţia poate fi ea însăşi ierarhică. În acest caz, în loc să facă
referire la o pereche entitate / arhitectură ca mai sus, ea poate pur şi simplu
să facă referire la o altă configuraţie de nivel inferior:

for eticheta_instanţei_componentei: numele_componentei


use configuration numele_configuraţiei
end for;

Reluând exemplul precedent al sumatorului complet şi în special


arhitectura structurală (DESCRIERE_STRUCTURALĂ) este posibil să
configurăm componentele semi-sumator prin entitatea HALF_ADDER de
arhitectură CN78975S (corespunzătoare unui circuit integrat disponibil pe
piaţă) şi poarta logică SAU prin entitatea OR_GATE de arhitectură
SN74LS32, scriind:

use WORK.SUMATOR_COMPLET; -- Clauza permite „vederea” entităţii


-- SUMATOR_COMPLET din biblioteca de
-- lucru WORK
configuration TEST1 of SUMATOR_COMPLET is
for DESCRIERE_STRUCTURALĂ
for C1, C2: SEMI_SUMATOR
use entity WORK.HALF_ADDER(CN78975S);
end for;
for C3: POARTA_SAU
use entity WORK.OR_GATE(SN74LS32);
end for;
end for;
end TEST1;

În VHDL este posibilă configurarea incrementală a unei descrieri. De


exemplu, se pot configura mai întâi valorile parametrilor generici, apoi se
poate reveni, în altă parte a codului, pentru a se modifica aceste valori.
Porturile neconectate pot de asemenea să fie configurate la un nivel mai înalt
al ierarhiei.
Configuraţia este un concept VHDL puternic, dar care pune
probleme începătorilor în ceea ce priveşte aplicarea sa concretă.
Apelarea unei biblioteci se efectuează prin intermediul unei clauze
use..., eventual precedată de o clauză library..., dacă numele bibliotecii nu
este WORK sau STD. De exemplu:
36 Limbajul VHDL
--apelarea tuturor perechilor entitate / arhitectură
library MY_PROJECT_SIM;
use MY_PROJECT_SIM.all;

Pentru ca această apelare să fie luată în considerare, trebuie ca ea să


preceadă contextul pe care-l are în vedere.
Există două situaţii posibile:
a) Întreaga configuraţie are nevoie de biblioteci care trebuie referite.
În acest caz, cel mai simplu este să scriem clauzele library şi use la
început, adică înaintea cuvântului cheie configuration. De exemplu:

library MY_PROJECT_SIM;
use MY_PROJECT_SIM.all;
configuration cfg_test_PROIECT of test_PROIECT is...

b) Modelul are foarte multe nivele ierarhice şi fiecare dintre sub-


blocuri nu apelează în mod necesar aceleaşi biblioteci, cu riscurile de
ambiguitate pe care le implică această abordare. Sunt posibile două soluţii:
b1) Configuraţia este „spartă” în atâtea sub-configuraţii câte sunt
necesare. Fiecare dintre ele face atunci referire, fără ambiguităţi, la una sau
mai multe biblioteci şi astfel am revenit întrucâtva la cazul anterior.
b2) Bibliotecile sunt referite în momentul când este nevoie de ele în
cadrul modelului. Concret, aceste referiri la biblioteci sunt plasate chiar
înainte de stipularea numelui arhitecturii care conţine instanţele de
configurat. De exemplu:

library MY_PROJECT_SIM; -- Referinţe globale


use MY_PROJECT_SIM.all;

configuration CFG_TEST_PROIECT of TEST_PROIECT is


--numele configuraţiei şi al entităţii
for A_TEST -- numele arhitecturii folosite
for all: B1 use entity BIB1.OBIECT1(ARC_RTL);
end for;
for all: B2 use entity BIB2.OBIECT1(ARC_RTL);
use MY_PROIECT_SIM.all;
for ARC_RTL
end for;
end for;
end for;
end CFG_TEST_PROIECT;
UNITĂŢI FUNDAMENTALE DE PROIECTARE 37
Observaţie
Soluţia cea mai uşor aplicabilă o constituie trecerea prin sub-
configuraţii. Într-adevăr, după fiecare analiză VHDL a unei configuraţii a
unui sub-bloc, este suficient să se lanseze simularea pentru a verifica dacă
instrumentul reuşeşte să construiască reţeaua completă de componente şi de
cupluri entităţi / arhitecturi la care trebuie să le lege (aşa-zisa „netlist”).

Forma minimală a configuraţiei este următoarea:

{scrierea referinţelor, clauzele library şi use}


configuration CFG_D of D is
for A_D
end for;
end CFG_D;

Regulile necesare utilizării acestei forme simplificate se pot rezuma


astfel:
a) Trebuie definite profiluri identice pentru entităţile şi
componentele VHDL. Astfel se poate utiliza mecanismul configuraţiei
implicite.
Prin profil se înţelege:
- acelaşi nume (componentă şi entitate);
- aceeaşi interfaţă la porturi;
- aceeaşi interfaţă în ceea ce priveşte parametrii generici; aceştia
pot totuşi să fie omişi din vederea componentei dacă au o valoare
implicită definită în entitate.
b) Trebuie definită o listă de biblioteci sau de elemente de referinţă
astfel încât să se evite orice ambiguitate: o aceeaşi entitate nu trebuie să
existe în două biblioteci diferite.
c) Este necesar să definim o sub-configuraţie (având o formă
simplificată) atunci când:
- este posibilă apariţia unei ambiguităţi;
- o entitate posedă mai multe arhitecturi operaţionale;
- entitatea reprezintă un bloc compact (RAM, ROM etc.).
Această nouă configuraţie va fi bineînţeles utilizată într-o
configuraţie de nivel superior.
Dacă proiectantul respectă regulile enunţate mai sus, el nu va avea
nevoie să expliciteze, nici măcar parţial, structura ierarhică a blocului în
38 Limbajul VHDL
decursul scrierii configuraţiei. Va fi necesară doar enunţarea listei de
referinţe care vor fi utile pentru ca asocierea „instanţei de componentă” cu
„perechea entitate / arhitectură” să se efectueze corect.
Referinţele pot fi de mai multe tipuri:
a) Referirea unei biblioteci complete în sens VHDL.
Aceste biblioteci pot reprezenta obiecte de naturi foarte diferite:
- un catalog de celule standard;
- lista (totală sau parţială) a vederilor ierarhice ale circuitului.
b) Referirea unei configuraţii deja definite. Avantajul acestei
abordări constă în faptul că este ierarhică.
c) Referirea unei entităţi deja definite. Această abordare prezintă
interes în cazul în care proiectantul nu doreşte să facă referire la
totalitatea unei biblioteci (pentru a evita problemele de
ambiguitate), ci numai la una sau mai multe entităţi.

Observaţie
Dacă această entitate, referită în mod explicit, are mai multe
arhitecturi, nu este posibil s-o alegem direct (compilatorul va selecta
arhitectura cea mai recent compilată). O soluţie constă în definirea unei
configuraţii specifice pentru o anumită pereche entitate / arhitectură şi în
alegerea perechii corecte.

2.4.4 Specificarea pachetelor


Specificaţia unui pachet (numită şi partea declarativă sau vederea
externă a pachetului) prezintă tot ce exportă pachetul: în primul rând obiecte
(semnale, constante, fişiere sau variabile partajate), tipuri şi sub-tipuri,
precum şi sub-programe. Se pot exporta şi tipuri fişier, declaraţii de
componente şi de alias-uri, specificaţii sau declaraţii de atribute, specificaţii
de conectare şi clauze use. Toate aceste obiecte îi vor fi accesibile oricărui
proiectant care apelează acest pachet printr-o clauză use.
Specificaţia următorului pachet oferă o imagine asupra acestor
posibilităţi:
UNITĂŢI FUNDAMENTALE DE PROIECTARE 39

package P1_PKG is
--Definirea unui tip enumerat
type CIT_SCR is (SCRIERE, CITIRE);
--Definirea unei constante
constant DIM_MAX: INTEGER:=200;
--Definirea de sub-tipuri
subtype DIM is INTEGER range 0 to DIM_MAX;
subtype CIT_SCR_BIT is BIT;
--Definirea de constante cu iniţializare ulterioară
constant SCRIERE_BIT: CIT_SCR_BIT;
constant CITIRE_BIT: CIT_SCR_BIT;
--Declararea unui semnal
signal COMANDĂ_MEMORIE: CIT_SCR;
--Declararea unei funcţii
function CDĂ_SPRE_BIT (C: in CIT_SCR) return CIT_SCR_BIT;
--Declararea unei proceduri
procedure CDĂ_SPRE_BIT (C: in CIT_SCR; B: out CIT_SCR_BIT);
end P1_PKG;

Un pachet poate avea un corp asociat, lucru care este chiar


obligatoriu atunci când se declară sub-programe sau constante cu iniţializare
ulterioară. În acest caz, corpul este unic şi conţine algoritmica (strict
secvenţială) necesară realizării sub-programelor, precum şi eventuale
declaraţii de uz intern ale pachetului.

2.4.5 Corpul unui pachet


Corpul unui pachet conţine algoritmii sub-programelor exportate de
către pachet, a căror declarare a fost făcută în specificaţia de pachet
corespunzătoare.
În corpul pachetului sunt declarate şi tipurile, sub-tipurile,
constantele, fişierele, alias-urile sau sub-programele (declaraţie şi corp)
utilizate local. Aceste declaraţii nu sunt văzute din exteriorul pachetului, nici
măcar din specificaţia sa. În schimb, toate declaraţiile efectuate în
specificaţie sunt cunoscute în corpul pachetului.

Observaţie
Este interzisă declararea semnalelor: corpul unui pachet face parte
din domeniul secvenţial.
40 Limbajul VHDL
Vom considera un exemplu extrem de simplu: următorul pachet va
trebui să exporte două funcţii care returnează, respectiv, cel mai mic şi cel
mai mare dintre două numere întregi.

package SIMPLU is
function MIN(A,B: INTEGER) return INTEGER;
function MAX(A,B: INTEGER) return INTEGER;
end SIMPLU;

package body SIMPLU is


function MIN(A,B: INTEGER) return INTEGER is
begin
if A < B then return A;
else return B;
end if;
end MIN;
function MAX(A,B: INTEGER) return INTEGER is
begin
if A > B then return A;
else return B;
end if;
end MAX;
end SIMPLU;

În acelaşi mod, corpul corespunzător pachetului P1_PKG, a cărui


specificaţie a fost prezentată mai sus, dă valorile constantelor cu iniţializare
ulterioară CITIRE_BIT şi SCRIERE_BIT, apoi descrie algoritmii funcţiei şi
procedurii CDĂ_SPRE_BIT.

Package body P1_PKG is

--Definirea valorii constantelor cu iniţializare ulterioară


constant CITIRE_BIT: CIT_SCR_BIT:= ’0’;
constant SCRIERE_BIT: CIT_SCR_BIT:= ’0’;

--Definirea funcţiei
function CDĂ_SPRE_BIT (C: in CIT_SCR) return CIT_SCR_BIT is
begin
case C is
when CITIRE => return CITIRE_BIT;
when SCRIERE => return SCRIERE_BIT;
end case;
end;
UNITĂŢI FUNDAMENTALE DE PROIECTARE 41
--Definirea procedurii
procedure CDĂ_SPRE_BIT(C: in CIT_SCR;B: out CIT_SCR_BIT) is
begin
case C is
when CITIRE => B:= CITIRE_BIT;
when SCRIERE => B:= SCRIERE_BIT;
end case;
end;
end P1_PKG;

3. Desfăşurarea lucrării

3.1 Se vor crea pachetele P1_PKG şi SIMPLU prezentate în


cuprinsul lucrării şi se va testa corectitudinea lor.
3.2 Se va crea o entitate numită SUMATOR_1 de numere pe 1 bit şi
se vor specifica trei arhitecturi diferite (structurală,
comportamentală şi „flux de date”) ale acesteia. Se vor simula
toate arhitecturile.
3.3 Se va crea o entitate numită SUMATOR_2 de numere pe 2 biţi în
cadrul căreia se vor utiliza instanţe ale entităţii SUMATOR _1,
folosind mecanismul specific configuraţiilor.
3.4 Se va crea o entitate numită COMPARATOR_2 de numere pe 2
biţi şi se vor specifica trei arhitecturi diferite (structurală,
comportamentală şi „flux de date”) ale acesteia. Se vor simula
toate arhitecturile.
3.5 Se va crea o entitate numită COMPARATOR_4 de numere pe 4
biţi în cadrul căreia se vor utiliza instanţe ale entităţii
COMPARATOR_2, folosind mecanismul specific
configuraţiilor.
3.6 Se va implementa un circuit de comandă a unui decodificator
BCD-7 segmente.
LUCRAREA NR. 3
SEMNALE. PARAMETRI GENERICI. CONSTANTE

1. Scopul lucrării

Lucrarea prezintă unul dintre obiectele fundamentale de proiectare în


VHDL: semnalul. Este detaliat conceptul de semnal, precum şi noţiunile
fundamentale referitoare la acesta (vizibilitatea semnalelor, reguli de
asignare, modele de transmisie etc.). Se introduce şi se detaliază şi conceptul
de parametri generici (sunt prezentate exemple, domeniul de aplicabilitate,
avantaje şi dezavantaje) şi cel de constantă.

2. Consideraţii teoretice

2.1 Conceptul de semnal. Definiţie

Semnalul este un purtător de informaţie. În general vorbind, el este


un fenomen fizic care se poate modifica în timp şi / sau în spaţiu, iar
modificările sale pot fi specificate prin instrucţiuni formale.
În VHDL, clasificarea fundamentală a semnalelor este următoarea:
- Semnale externe, purtătoare de informaţie între diferitele
dispozitive electronice. Ele formează interfaţa fiecărui sistem. Semnalele
externe se declară numai în partea de entitate a sistemului descris în VHDL.
- Semnale interne, purtătoare de informaţie în interiorul
dispozitivelor electronice. Ele nu sunt vizibile, fiind complet încapsulate în
interiorul dispozitivului şi constituind o parte a arhitecturii sale interne.
Semnalele interne se declară numai în partea de arhitectură a sistemului
descris în VHDL.
Semnalele electrice joacă un rol crucial în dispozitivele electronice.
Ele sunt cele mai importante obiecte din orice dispozitiv electronic.
Deoarece limbajul VHDL este proiectat pentru a descrie funcţionarea unor
sisteme complete, semnalele joacă un rol important în descrierea
comunicării dintre circuitele sau blocurile lor.
SEMNALE. PARAMETRI GENERICI.CONSTANTE 43
Semnalele permit analizarea relaţiilor temporale în cadrul sistemului.
Spre deosebire de variabilele din limbajele de programare clasice, cum ar fi
Pascal sau C, semnalele VHDL conţin informaţii atât despre valorile curente
cât şi despre cele trecute şi viitoare. Aceste informaţii sunt numite istoria
(history) semnalului.
Liniile de semnal pot fi implementate ca linii singulare sau multiple.
O conexiune singulară este reprezentată de către o singură linie de
semnal, care are o singură valoare binară la un moment dat. Un exemplu de
asemenea semnal este tactul care sincronizează toate blocurile sistemului.
Conexiunile multiple se mai numesc magistrale sau vectori, acestea
transmiţând informaţiile ca o combinaţie de valori binare.
În VHDL, un semnal corespunde reprezentării hardware a
conceptului de purtător de informaţie. Reprezentarea poate fi o structură de
date simplă sau complexă, în funcţie de tipul datelor purtate de semnal.
Orice structură de date este în primul rând declarată ca un semnal, cu
excepţia anumitor condiţii particulare. Orice declaraţie de semnal se face în
domeniul concurent, deoarece un semnal permite conectarea mai multor
module hardware (ele însele fiind definite sub formă de componente sau de
blocuri din domeniul concurent). Un semnal reprezintă aşadar o definiţie
concurentă. Zona declaraţiilor concurente corespunde zonei de declarare a
arhitecturilor şi entităţilor. Asignarea de valori unui semnal permite
evoluţia în timp a valorilor asociate cu semnalul respectiv. Faptul că
valoarea unui semnal poate fi utilizată în cadrul unei expresii şi asignată de
către o altă instrucţiune în acelaşi timp, va impune conservarea valorii
curente a semnalului separat de valorile viitoare (generate de instrucţiunea
care modifică valoarea semnalului).
În plus, pentru a facilita modelarea anumitor componente, este util să
putem avea acces la valorile trecute ale oricărui semnal. Prin urmare, orice
semnal poate păstra o istorie a valorilor sale trecute, valoarea prezentă şi
chiar şi valorile prevăzute pentru viitor. Memorarea valorilor se face în aşa-
numitul pilot (driver) al semnalului. În realitate, valorile trecute nu sunt
conservate direct în semnalul respectiv, ci într-o versiune întârziată în timp a
acestuia. De fapt, se memorează acele evenimente care indică o schimbare
de valoare la un moment de timp bine definit. Numai valorile
(evenimentele) viitoare pot fi modificate de către operaţia de asignare a unei
valori unui semnal. Valoarea prezentă nu poate fi modificată. Operaţia de
asignare a unei valori unui semnal poate fi efectuată fie prin conectarea la un
port de ieşire al unei componente, fie prin operaţia de asignare în domeniul
44 LIMBAJUL VHDL
concurent, fie prin operaţia de asignare în domeniul secvenţial. Operaţia de
asignare a unei valori unui semnal în domeniul concurent corespunde
stilului de descriere „flux de date”.

2.2 Declararea semnalelor

Manualul de referinţă al limbajului VHDL defineşte portul drept „un


canal de comunicare dinamică între un bloc (sau o entitate) şi mediul său
înconjurător”. Prin urmare, semnalele care conectează un modul cu mediul
său înconjurător sunt numite porturi şi sunt specificate în secţiunea specială
ports a declaraţiei unei entităţi. Fiecare semnal care leagă modulul hardware
cu mediul său exterior e specificat ca un port în interiorul declaraţiei de entitate.
Fiecare semnal trebuie să posede:
- un nume unic (de exemplu, Clock sau Data[0 to 7]);
- un tip (de exemplu, BIT sau BIT_VECTOR).
Fiecare semnal declarat ca port poate avea specificată o valoare
iniţială. Pe lângă aceste atribute indispensabile fiecărui semnal, un port
trebuie să aibă, în plus, şi un atribut care să ofere informaţii despre sensul
fluxului de informaţie vehiculat prin acel port: acesta se numeşte modul
portului (mode).
Sensul fluxului de informaţie vehiculat prin port este crucial şi
trebuie specificat de fiecare dată dacă un semnal este:
- de intrare (input): in;
- de ieşire (output): out;
- bidirecţional (inout): inout.
Sensul fluxului de informaţie trebuie declarat în mod explicit. Dacă
nu este specificat explicit, modul implicit este in. Prin urmare, declararea
unui port se realizează respectând următoarea sintaxă:

nume_port: mod tip_port;

Deşi în VHDL există cinci tipuri de moduri posibile ale unui semnal
(in, inout, out, buffer şi linkage) este recomandat să se utilizeze numai
primele trei dintre ele, deoarece ultimele două nu sunt acceptate de toate
instrumentele de sinteză. Cele mai folosite trei moduri pot fi descrise astfel:
- modul in: datele pot fi citite în interiorul modulului, dar nu pot fi
scrise de către arhitectura internă a modulului;
SEMNALE. PARAMETRI GENERICI.CONSTANTE 45
- modul out: datele pot fi generate în interiorul arhitecturii, dar nu
pot fi citite;
- modul inout: datele pot fi citite şi scrise în interiorul arhitecturii.

Figura 3.1 Principalele moduri ale porturilor

Modurile buffer şi linkage pot fi descrise astfel:


- modul buffer: este asimilabil modului „ieşire” şi trebuie în mod
obligatoriu să nu aibă decât o singură sursă. Cu această restricţie,
va fi posibilă citirea valorii vehiculate de port în interiorul
arhitecturii (vezi figura 3.1);
- modul linkage: este mai puţin utilizat. Într-adevăr, dacă un port
formal de mod linkage poate fi conectat la oricare alt port,
porturile interne deja conectate nu mai pot fi conectate decât la
alte porturi de mod linkage. Nu este recomandabilă utilizarea
acestui mod.
În completarea acestei descrieri, recomandăm consultarea figurii 2.2
din lucrarea nr. 2.
46 LIMBAJUL VHDL
De exemplu, pentru un modul de memorie (ROM sau RAM):
A_RAM
A_ROM
Memorie D_RAM Memorie
D_ROM ROM RAM

WE
CS_ROM CS_RAM

Figura 3.2 Schema pentru memorie ROM şi pentru memorie RAM

entity MEMORIE_ROM is
port(A_ROM: in BIT_VECTOR (3 downto 0); -- Adresele
CS_ROM: in BIT; -- Semnal selecţie cip, „Chip Select”
D_ROM: out BIT_VECTOR(7 downto 0)); -- Ieşiri de date
end MEMORIE_ROM;

entity MEMORIE_RAM is
port(A_RAM: in BIT_VECTOR (3 downto 0); -- Adresele
CS_RAM: in BIT; -- Semnal selecţie cip, „Chip Select”
WE: in BIT; -- Semnal validare scriere, „Write Enable”
D_RAM: inout BIT_VECTOR(7 downto 0)); -- Bidirecţional
end MEMORIE_RAM;

Entitatea specifică interfaţa sistemului cu mediul exterior, în timp ce


arhitectura descrie totalitatea componentelor rezidente în interiorul
sistemului. Semnalele interne nu fac excepţie de la această regulă. Pentru a
putea fi deosebite de alte obiecte din codul VHDL, se utilizează cuvântul
cheie signal pentru fiecare declaraţie.

Observaţii
1. Cuvântul cheie signal nu este necesar în declaraţia porturilor, deoarece
fiecare port este prin definiţie un semnal.
2. Semnalele interne nu au nevoie de declaraţii de mod (in, inout, out etc.)
3. Există posibilitatea să se specifice o valoare iniţială pentru fiecare
semnal intern.

De exemplu, pentru dispozitivul din figura 3.3, declararea semnalelor


externe şi interne se face în felul următor:
SEMNALE. PARAMETRI GENERICI.CONSTANTE 47
Împărţitor Y Deîmpărţit X

Registrul C
7 0
7 0 7 0

Unitate
Aritmetică
Logică
7 0
Reacţie
Transport
Registrul A Registrul B
7 0 7 0

Câtul Q Restul R

Figura 3.3 Unitatea de execuţie a unui dispozitiv de împărţire


binară (schemă de principiu)

entity UNITATE_DE_EXECUŢIE is
port (X, Y: in BIT_VECTOR (7 downto 0);
Q,R: out BIT_VECTOR (7 downto 0));
-- X, Y, Q şi R sunt semnale externe
end entity UNITATE_DE_EXECUŢIE;
architecture UEA of UNITATE_DE_EXECUŢIE is
signal C: BIT_VECTOR (7 downto 0);
signal REACŢIE: BIT_VECTOR (7 downto 0);
signal TRANSPORT: BIT; --A,B: registre de deplasare pe 8 biţi
-- C, REACŢIE şi TRANSPORT sunt semnale interne
begin
-- Descrierea structurii interne a Unităţii de execuţie
...
end UEA;

Vizibilitatea fiecărui semnal este determinată de locul în care este


declarat. Pentru determinarea vizibilităţii semnalelor se aplică următoarele
reguli:
1. Un semnal declarat în cadrul unui pachet este vizibil în toate
unităţile de proiectare care utilizează acel pachet;
2. Un semnal declarat ca port al unei entităţi este vizibil în toate
arhitecturile asociate acelei entităţi;
48 LIMBAJUL VHDL
3. Un semnal declarat în partea declarativă a unei arhitecturi este
vizibil numai în interiorul acelei arhitecturi;
4. Un semnal declarat în cadrul unui bloc situat în interiorul unei
arhitecturi este vizibil numai în interiorul acelui bloc.
Aceste reguli derivă direct din principiile proiectării ierarhice: dacă
un obiect este declarat la un anumit nivel al ierarhiei, el va fi vizibil în
interiorul acelei construcţii specifice şi în toate nivelurile inferioare ale
ierarhiei. Figura 3.4 ilustrează aceste reguli într-un mod grafic:

package PackEx is Vizibilitatea lui A


signal A

use PackEx Vizibilitatea lui B


entity EntEx is
port (B)

architecture ArhEx of EntEx is Vizibilitatea lui C


signal C

BlockEx: block Vizibilitatea lui D


signal D

Figura 3.4 Reguli de vizibilitate a semnalelor

2.3 Asignarea de valori semnalelor

Instrucţiunile de asignare de valori semnalelor nu modifică valoarea


actuală, ci valorile viitoare pe care aceste semnale le-ar putea lua – deci,
modifică piloţii semnalelor (driver-ele).
Simbolul instrucţiunii de asignare este „<=” şi se citeşte „primeşte”.
De exemplu, asignarea valorii semnalului B, semnalului A, se scrie astfel:

A <= B after 10 ns, ‘1’ after 20 ns, ‘0’ after 30 ns;

Această instrucţiune are următoarea semnificaţie: semnalul A va lua


valoarea actuală a lui B peste 10 nanosecunde, apoi va lua valoarea ‘1’ peste
SEMNALE. PARAMETRI GENERICI.CONSTANTE 49
20 nanosecunde (deci cu 10 nanosecunde mai târziu) şi apoi valoarea ‘0’
peste încă 10 nanosecunde.
Fiecare pereche (valoare, întârziere), separată de cuvântul cheie
after, este numită element de formă de undă (waveform element). Un şir de
asemenea perechi constituie o formă de undă (waveform).
Valoarea trebuie să aibă un tip compatibil cu cel al semnalului căruia
îi este asignată (în membrul stâng al instrucţiunii de asignare). Valoarea este
fie o constantă, fie valoarea actuală a semnalului indicat sau, la modul
general, rezultatul unei expresii.
Cuvântul cheie after este în mod obligatoriu urmat de către o
expresie al cărei rezultat este de tipul TIME (tip definit în pachetul
STANDARD). Rezultatul indică intervalul de timp la capătul căruia
valoarea calculată (acum) va fi asignată semnalului. Sau, mai exact, va fi
probabil asignată! În acest mod a fost calculată o posibilă valoare viitoare a
semnalului, adică, cel mult, o valoare susceptibilă să devină valoare prezentă
a semnalului. Acest viitor poate fi modificat prin acţiunea anumitor
evenimente (datorate unor semnale interconectate sau unei noi situaţii
apărute datorită avansului în timp).
Întârzierile specificate trebuie în mod obligatoriu să se prezinte în
ordine crescătoare (în cazul de faţă 10, 20, 30). Această observaţie este
esenţială pentru scrierea unei expresii (nu a unei constante) pentru calculul
întârzierii. Nu există întârziere negativă în VHDL – definiţia tipului TIME
din pachetul STANDARD nici nu o permite. Totuşi, este permisă utilizarea
unei întârzieri nule.
Asignarea cu întârziere nulă se poate scrie astfel:

A <= B after 0 ns; -- Prima variantă


A <= B; -- A doua variantă; absenţa unei clauze after
-- semnifică o asignare a cărei întârziere este nulă

Atunci, care este semnificaţia unei întârzieri nule, de vreme ce nu se


pune problema modificării valorii prezente a semnalului?
Întrucât limbajul VHDL este folosit pentru modelarea unor
dispozitive hardware, noţiunea de asignare de valori cu întârziere nulă nu are
sens: nu există dispozitive fizice care să nu prezinte timpi de propagare a
semnalelor electrice prin ele, oricât de mici ar fi aceşti timpi. De fapt,
asignarea precedentă semnifică pur şi simplu o cauzalitate: valoarea
50 LIMBAJUL VHDL
semnalului A este (potenţial) modificată din cauză că valoarea semnalului B
se schimbă.
Iată în continuare modul în care simulatorul va traduce această
cauzalitate pentru a efectua o simulare coerentă. Prezentăm noţiunea de
întârziere „delta”.
O întârziere „delta” este o întârziere care este nulă pentru simulare
şi care nu reprezintă decât cauzalitatea.
Există aşadar două dimensiuni ale timpului în VHDL:
1. Timpul „real”, care se măsoară în paşi de simulare şi în unităţi de
timp TIME. Acest timp este cel “văzut” de către proiectant.
2. Timpul „delta”, care este gestionat de către simulator. El îi
permite acestuia să-şi aloce la fiecare pas al simulării un număr
variabil de “felii” infinitezimale de timp şi să gestioneze astfel
succesiunea asignărilor. Acesta reprezintă mijlocul de exprimare
a „cauzalităţii” generate de către diferitele asignări.
Iată în continuare un exemplu de utilizare a întârzierilor „delta”: fie
un bistabil RS descris funcţional de următorul cod VHDL:

Q <= S nand NQ;


NQ <= R nand Q;

Să presupunem că bistabilul se află de un anumit timp în starea


stabilă ‘1’ (Q este ‘1’, NQ este ‘0’), iar R şi S sunt ‘1’ fiecare.
La un moment dat, notat cu T0, intrarea R trece în starea ‘0’. În acest
moment, se evaluează cea de-a doua instrucţiune de asignare, ceea ce face ca
NQ să treacă în starea ‘1’ la momentul T0 + delta. Modificându-se NQ,
prima instrucţiune de asignare va fi reevaluată, deci Q va lua valoarea ‘0’ la
momentul de timp T0 + (2 × delta). Deoarece această schimbare a valorii lui
Q nu generează nici o modificare a lui NQ (care se află deja în starea ‘1’),
evaluările iau sfârşit.
Aşa cum se arată în figura 3.5 a), au fost necesare mai multe
intervale de timp „delta” pentru a descrie etapele succesive enumerate mai
sus. Aceste intervale nu au o realitate funcţională din punctul de vedere al
proiectantului (figura 3.5, b)).
SEMNALE. PARAMETRI GENERICI.CONSTANTE 51
R R

Q
Q

NQ

T0 + (2 × delta) NQ
T0
T0
a) b)
Figura 3.5 Întârzierile „delta”: a) punctul de vedere al simulatorului;
b) punctul de vedere al proiectantului

Dacă proiectantul ar fi intenţionat să creeze un model mai apropiat


de realitate, el ar fi trebuit să scrie (de exemplu):

Q <= S nand NQ after 5 ns;


NQ <= R nand Q after 5 ns;

Ceea ce ar fi determinat formele de undă din figura 3.6:


R

NQ

T0 T0 + 10 ns
T0 + 5 ns

Figura 3.6 Formele de undă aşteptate de către proiectant

2.4 Modele de transmisie

Un sistem hardware poate prezenta un comportament de răspuns cu


frecvenţă infinită – de exemplu, acesta este cazul unei linii obişnuite de
transmisie de date. Orice impuls, indiferent de durata sa, va fi transmis.
52 LIMBAJUL VHDL
Acest comportament este opus comportamentului sistemelor de
comutaţie, la care apare un fenomen de inerţie: un impuls nu va fi transmis
decât dacă durata sa va fi suficient de mare.
În VHDL, la asignarea fiecărui semnal, se poate specifica unul dintre
următoarele două moduri de transmisie:

• Modelul inerţial. Acesta este modul de lucru implicit al oricărei


instrucţiuni de atribuire de valori unui semnal, în care se filtrează
impulsurile de durată inferioară timpului de transmisie.

SEMNAL1 <= REF after 10 ns;

Orice impuls prezent pe linia REF, a cărui durată este mai mică
decât 10 nanosecunde, nu va fi transmis pe SEMNAL1;

• Modelul transport. Acesta reprezintă modelul cu răspuns de


frecvenţă infinită. Este indicat de cuvântul cheie transport care
urmează după simbolul operaţiunii de asignare. El permite
transmiterea oricărui impuls, indiferent de durata acestuia.

SEMNAL2 <= transport REF after 10 ns;

Figura 3.7 ilustrează diferenţa dintre modelele de transmisie.

Începând cu VHDL’93 se poate face deosebirea dintre valoarea


întârzierii (clauza after) şi lărgimea maximală a impulsurilor filtrate (clauza
reject). În plus, cuvântul cheie inertial poate fi indicat în mod explicit în
scopuri documentare, deşi rămâne modul implicit în VHDL.
Sintaxa generală pentru modelul de transmitere a semnalelor este
deci următoarea:

[etichetă:] numele_semnalului <= [transport | [reject


lărgime_impuls] inertial] formă de undă;
SEMNALE. PARAMETRI GENERICI.CONSTANTE 53
Semnal de referinţă: REF

SEMNAL1 <= REF after 10 ns; -- modelul inerţial

SEMNAL2 <= transport REF after 10 ns; -- modelul transport

10 ns 50 ns 100 ns
Figura 3.7 Modelele de propagare inerţial şi transport

Figura 3.8 ilustrează utilizarea clauzei reject. Dacă întârzierea


introdusă rămâne cea indicată de clauza after, se vor filtra numai
impulsurile de durată (sau „lărgime”) inferioară timpului indicat de clauza
reject. Se va genera o eroare dacă valoarea clauzei reject este mai mare
decât cea a clauzei after (sau decât prima valoare a clauzei after, în cazul
formelor de undă multiple).

Semnal de referinţă: REF

SEMNAL0 < = transport REF after 10 ns;

SEMNAL1 < = REF after 10 ns; sau SEMNAL1 <= inertial REF after 10 ns;

SEMNAL2 < = reject 3 ns inertial REF after 10 ns;

Filtrat! Păstrat!

10 ns 50 ns 100 ns

Figura 3.8 Modelele de propagare cu lăţime de rejectare explicită


54 LIMBAJUL VHDL
Aceasta este o facilitate introdusă odată cu norma VHDL’93 şi nu o
funcţionalitate suplimentară. Într-adevăr, instrucţiunea VHDL’93:

SEMNALX <= reject 6 ns inertial REF after 20 ns;

este echivalentă cu următoarele două linii (utilizând un semnal intermediar


SEMNAL_INTER):

--Filtrarea inerţială a impulsurilor < 6 ns


SEMNAL_INTER <= REF after 6 ns;
-- Propagare pură (20 ns – 6 ns)
SEMNALX <= transport SEMNAL_INTER after 14 ns;

Sintaxa generală a instrucţiunii de asignare de semnal este foarte


puternică: se poate utiliza un agregat atât în partea stângă a simbolului de
asignare cât şi într-un element de formă de undă.

Nume_sau_agregat < = {transport} element_de_formă_de_undă 1,


element_de_formă_de_undă 2, …;

Un element de formă de undă se exprimă prin:

expresie {after expresie_temporală} -- Prima variantă


agregat {after expresie_temporală} -- A doua variantă

Pentru a asigna valori unui agregat, adică pentru a-l putea folosi în
membrul stâng al asignării, trebuie ca tipul agregatului să fie declarat şi ca
identificarea acestui tip să se poată face fără ambiguitate din punctul de
vedere al părţii drepte a instrucţiunii de asignare. Nu este permisă utilizarea
unei expresii calificate (tip_ obiect ‘(expresie) sau tip_obiect ‘agregat) în
membrul stâng.
Presupunând că semnalele A şi B sunt de tipul BIT şi că se cunoaşte
tipul BIT_VECTOR, am putea fi tentaţi să scriem:

(A, B) <= transport “10” after 10 ns;

Această instrucţiune nu va fi recunoscută în VHDL, membrul drept


al instrucţiunii nefiind lipsit de ambiguitate. Într-adevăr, “10” poate la fel de
SEMNALE. PARAMETRI GENERICI.CONSTANTE 55
bine să fie de tipul BIT_VECTOR precum şi de tipul STRING, chiar dacă
tipul STRING pare incoerent cu partea stângă. Scrierea corectă va fi:

(A, B) <= transport BIT_VECTOR’(“10”) after 10 ns;


-- Apare un apostrof! Expresia calificată (în membrul drept)
-- înlătură ambiguitatea.

Observaţie
La utilizarea unui agregat într-un element de formă de undă, aceleaşi
restricţii interzic utilizarea cuvântului cheie others; tipul agregatului din
stânga trebuie să poată fi identificat fără ambiguitate.

2.5 Programe cu execuţie secvenţială

Rularea secvenţială a unui program este uşor de înţeles, deoarece se


ştie exact care este instrucţiunea executată de către program. Instrucţiunile
se succed într-o ordine precisă şi nu trebuie făcut nici un calcul pentru
determinarea instrucţiunii executate. Modul de rulare secvenţial este
caracteristic majorităţii limbajelor de programare clasice.
În VHDL însă, în general, instrucţiunile se execută concurent, cu
excepţia anumitor părţi care se scriu şi se execută în manieră secvenţială (de
exemplu, corpul unui proces). Semnalele se vor comporta în moduri diferite
în funcţie de tipul lor de propagare: inerţial sau transport.
Fie două tranzacţii, numite „prima” şi „a doua” – după ordinea lor de
apariţie într-un proces.

TRANSPORT INERŢIAL
A doua tranzacţie
Scrie peste prima
are loc înaintea Scrie peste prima tranzacţie
tranzacţie
primei tranzacţii.
A doua tranzacţie Adaugă a doua Scrie peste prima tranzacţie
are loc după prima tranzacţie la dacă valorile sunt diferite;
tranzacţie. semnal dacă nu, ambele sunt păstrate

Următoarele patru figuri vor ilustra efectele codificării VHDL.


56 LIMBAJUL VHDL
1a) Inerţial – tranzacţia 2 are loc înaintea tranzacţiei 1 (figura 3.9).

begin
process
begin
IEŞIRE <= ‘1’ after 10 ns; -- prima tranzacţie
IEŞIRE <= ‘0’ after 5 ns; -- a doua tranzacţie
wait;
end process;
end;

1b) Transport - tranzacţia 2 are loc înaintea tranzacţiei 1 (figura 3.9).

begin
process
begin
IEŞIRE <= transport ‘1’ after 10 ns; -- prima tranzacţie
IEŞIRE <= transport ‘0’ after 5 ns; -- a doua tranzacţie
wait;
end process;
end;

Ieşire X 0

0 ns 5 ns 10 ns 15 ns
Figura 3.9
2) Inerţial – tranzacţia 2 are loc după tranzacţia 1, valori similare (figura
3.10).

begin
process
begin
IEŞIRE <= ‘0’ after 10 ns; -- prima tranzacţie
IEŞIRE <= ‘0’ after 15 ns; -- a doua tranzacţie
wait;
end process;
end;
SEMNALE. PARAMETRI GENERICI.CONSTANTE 57

Ieşire X 0

0 ns 5 ns 10 ns 15 ns

Figura 3.10

3) Inerţial – tranzacţia 2 are loc după tranzacţia 1, valori diferite (figura


3.11).

begin
process
begin
IEŞIRE <= ‘1’ after 10 ns; -- prima tranzacţie
IEŞIRE <= ‘0’ after 15 ns; -- a doua tranzacţie
wait;
end process;
end;

Starea ‘1’ care ar fi trebuit să apară între t = 10 ns şi t = 15 ns a fost


filtrată (suprimată).

Ieşire X 0

0 ns 5 ns 10 ns 15 ns
Figura 3.11
4) Transport – tranzacţia 2 are loc după tranzacţia 1 (figura 3.12)
begin
process
begin
IEŞIRE <= transport ‘1’ after 10 ns; -- prima tranzacţie
IEŞIRE <= transport ‘0’ after 15 ns; -- a doua tranzacţie
wait;
end process;
end;
58 LIMBAJUL VHDL

Ieşire X 1 0

0 ns 5 ns 10 ns 15 ns
Figura 3.12

2.6 Parametri generici

Utilizarea parametrilor generici reprezintă mijlocul de a transmite o


informaţie unui bloc, această informaţie fiind statică pentru blocul respectiv.
Un bloc generic este văzut din exterior ca un bloc parametrizat, prin
intermediul parametrilor generici. Din interiorul blocului, aceşti parametri
sunt văzuţi drept constante şi pot fi manipulaţi ca atare.
De exemplu, se poate descrie un bloc cu un comportament de
registru pe N biţi, valoarea lui N fiind stabilită de un parametru generic.
Blocurile generice sunt foarte utilizate în VHDL. Bibliotecile de
modele sunt de foarte multe ori generice; în general se urmăreşte ca aceste
biblioteci să conţină componente generale.
Obiectele care pot fi generice în VHDL sunt următoarele:
1. O entitate (o pereche entitate / arhitectură). O asemenea unitate
de proiectare poate fi compilată (dar nu şi simulată ca atare!) şi
poate fi regăsită în bibliotecă. De reţinut că o entitate poate fi
generică, dar o arhitectură – nu!;
2. Orice bloc intern (definit cu ajutorul instrucţiunii block). Totuşi,
acest bloc nu poate fi instanţiat nici din exteriorul arhitecturii în
care se găseşte, nici din interior. Blocul intern generic nu poate fi
instanţiat decât în momentul declarării sale (fie cu parametri de
intrare, fie cu variabile). Această restricţie face ca utilizarea
blocurilor generice să fie dificilă în cadrul modelării în VHDL.
Raţiunea sa de a fi o constituie echivalenţa a două blocuri interne
imbricate cu o entitate, aceasta din urmă putând fi generică.
Parametrii generici pot fi folosiţi oriunde în cod, acolo unde este
necesară o valoare statică. De fapt, se recomandă insistent să se utilizeze
parametrii generici şi constantele în locul codificării fixe, deoarece uşurează
modificarea proiectului. În general, parametrii care se declară generici sunt:
SEMNALE. PARAMETRI GENERICI.CONSTANTE 59
• Dimensiunea obiectelor complexe, cum ar fi vectorii sau
magistralele. În cazul utilizării parametrilor generici pentru
definirea lăţimii de bandă a unei magistrale, se poate modifica
clauza generic de la începutul specificaţiei. Parametrii generici
se pot folosi ca limite de interval pentru iteratorii buclelor for şi
pentru alte aplicaţii similare;
• Parametrii de temporizare: întârzieri, timpi de prepoziţionare
(set-up), timpi de menţinere (hold), timpi de comutare şi orice
alte caracteristici temporale ale dispozitivelor electronice.
Declaraţia unui bloc generic constă, din punct de vedere sintactic, în
scrierea unei clauze generic după antetul blocului:

generic (parametru_1 {, alt_parametru} : tipul_ parametrului


{:= valoarea_implicită};
(parametru_2 {, alt_parametru} : tipul_ parametrului
{:= valoarea_implicită};
...
(parametru_n {, alt_parametru} : tipul_ parametrului
{:= valoarea_implicită};

Ca tip al parametrului se poate indica un tip de date efectiv, dar şi, la


modul general, indicaţii de sub-tipizare. Valoarea implicită este valoarea
care va fi utilizată în momentul instanţierii blocului, dacă parametrul
corespunzător este omis.
Această sintaxă se aplică atât entităţilor (numite blocuri externe) cât
şi instrucţiunilor bloc (numite blocuri interne). Ea este adeseori urmată de o
clauză (opţională) ce va descrie porturile acestui bloc (cuvântul cheie port).
Iată un exemplu în care parametrul generic este direct utilizat în
descrierea porturilor. Se urmăreşte descrierea unei vederi externe a unei
porţi logice ŞI cu N intrări:
entity POARTA_ŞI is
generic (NUMĂR_DE_INTRĂRI: NATURAL := 2);
port (INTRĂRI: in BIT_VECTOR (1 to NUMĂR_DE_INTRĂRI);
IEŞIRE: out BIT);
end POARTA_ŞI;

Aceeaşi funcţionalitate, aplicată unui bloc intern, se scrie astfel:


60 LIMBAJUL VHDL

POARTA_ŞI: block
generic (NUMĂR_INTRĂRI: NATURAL := 2);
port (INTRĂRI: in BIT_VECTOR (1 to NUMĂR_INTRĂRI);
IEŞIRE: out BIT);
generic map (NUMĂR_INTRĂRI => 8); -- Instanţierea unei porţi
-- ŞI cu 8 intrări
begin --Zona de instrucţiuni din cadrul blocului
process (INTRĂRI)
variable V: BIT:=’1’;
begin
for I in 1 to NUMĂR_INTRĂRI loop
V:= V and INTRĂRI(I);
end loop;
IEŞIRE <= V;
end process;
end POARTA_ŞI;

Să presupunem de exemplu că dorim să descriem o componentă


generală capabilă să recunoască un profil oarecare de N biţi transmişi în
serie. Figura 3.13 prezintă schema de principiu a dispozitivului.
Secvenţa de recunoscut se prezintă la intrarea INTRARE, fiind
sincronizată de semnalul de TACT. Detectarea secvenţei provoacă trecerea
ieşirii DETECTAT în starea ‘1’ logic, aceasta fiind ‘0’ în toate celelalte
cazuri.
TACT

INTRARE Recunoaşterea secvenţei ŞABLON de DETECTAT


N biţi

Figura 3.13 Dispozitiv de detectare a unei secvenţe binare

Datorită utilizării parametrilor generici, entitatea se poate scrie:

entity RECUNOAŞTERE is
generic (SECVENŢA: BIT_VECTOR);
port (INTRARE, TACT: in BIT;
DETECTAT: out BIT);
end RECUNOAŞTERE;

Una dintre arhitecturile posibile va fi:


SEMNALE. PARAMETRI GENERICI.CONSTANTE 61
architecture SIMPLĂ of RECUNOAŞTERE is
signal MEMORIE: BIT_VECTOR (SECVENŢA’RANGE);
begin
-- Conversia unui BOOLEAN în tipul BIT. Atributele VAL şi POS
-- sunt studiate în lucrarea 5.
DETECTAT <= BIT’VAL(BOOLEAN’POS(MEMORIE = SECVENŢA));
MEMORARE: process
begin
if TACT = ‘1’ then
MEMORIE<=MEMORIE(MEMORIE’LEFT+1 to MEMORIE’RIGHT) & INTRARE;
end if;
wait on TACT;
end process MEMORARE;
end SIMPLĂ;

Observaţii
1. Testul TACT = ’1’ e utilizabil deoarece TACT este de tipul BIT. Dacă, de
exemplu, TACT ar putea lua valorile ‘X’, ‘0’, ‘1’ sau ‘Z’, acest test ar fi
adevărat (în mod greşit) în decursul trecerilor de la ‘X’ la ‘1’ sau de la ‘Z’ la
‘1’. Ar trebui deci regândită expresia utilizând atribute adecvate.
2. Descrierea de mai sus se pretează foarte bine la o transformare în
arhitectură recursivă.

2.7 Constante

Constantele joacă acelaşi rol ca şi parametrii generici: ele constituie


un suport al informaţiei statice care poate fi utilizată în interiorul unui
model. Cu toate acestea, spre deosebire de parametrii generici (care sunt
declaraţi în entităţi), constantele sunt declarate în cadrul arhitecturilor.
Constanta este un obiect de bază în VHDL. O constantă este
iniţializată la o valoare care nu mai poate fi modificată ulterior. Valoarea de
iniţializare trebuie să fie de acelaşi tip ca şi cel indicat în momentul
declarării constantei. Ea va fi calculată în etapa de elaborare şi poate fi o
expresie complexă, incluzând eventual apeluri de funcţii (putând chiar să
preia o valoare de la tastatură, de exemplu). Sintaxa sa este următoarea:

constant nume_constantă: tip_sau_sub-tip {:= valoare_constantă};

Iată câteva exemple de declaraţii de constante:


62 LIMBAJUL VHDL
constant PI: REAL := 3.1416;
constant ÎNTÂRZIERE_MAXIMĂ: TIME := 25 ns;
constant MARJĂ_MIN: INTEGER := 40*N; -- N e definit anterior

În ultimul exemplu, N poate desemna un parametru de intrare al


procedurii în care se află această declaraţie şi poate deci conferi un caracter
„dinamic” valorii constantei MARJĂ_MIN.
O constantă nu poate avea nici tipul acces (nici un alt tip mai
complex care are un tip acces printre elementele sale), nici tipul fişier (file).
Ţinând cont de sintaxa precedentă, valoarea constantei poate să nu
fie precizată în momentul declaraţiei sale. Utilitatea acestei opţiuni poate să
nu pară evidentă. În practică, o declaraţie de constantă fără valoarea sa se
numeşte declaraţie de constantă cu iniţializare ulterioară.
Acest tip de declaraţie de constantă nu are sens decât dacă se găseşte
în partea de specificare a unui pachet. Scopul său este simplu: din exteriorul
pachetului se poate folosi această constantă, dar fără a avea cunoştinţă de
valoarea sa care este definită printr-o declaraţie completă de constantă în
interiorul corpului pachetului. Astfel, un proiectant care va utiliza această
constantă nu va putea fi tentat să o înlocuiască prin valoarea sa: el nu o
cunoaşte. Acesta este un exemplu de mascare a informaţiei în VHDL.
Exemplul de mai jos ilustrează utilizarea constantelor:
entity MUX_2_LA_1 is
port (I0, I1, SEL: in BIT;
Y: out BIT):
end entity MUX_2_LA_1;
architecture ARHITECTURA of MUX_2_LA_1 is
constant TP: TIME := 10 ns;
begin
Y <= (I0 and SEL) or (I1 and not (SEL)) after TP;
end ARHITECTURA;

Ne putem întreba de ce conţine limbajul VHDL două construcţii atât


de similare cum ar fi parametrii generici şi constantele. Este, oare, într-
adevăr utilă existenţa ambelor?
Principala diferenţă dintre parametri generici şi constante constă în
aceea că parametrii generici pot fi utilizaţi şi în mod dinamic, în vreme ce
constantele sunt pur statice. Asta înseamnă că valoarea unui parametru
generic poate fi modificată fără a se opera modificări în cod, deci fără a se
recompila entitatea. Dimpotrivă, constantele nu pot fi modificate fără a se
opera modificări în cadrul codului. Acest aspect este important mai ales în
SEMNALE. PARAMETRI GENERICI.CONSTANTE 63
cazul în care specificaţia va fi folosită ca o componentă pentru o altă
specificaţie, de nivel superior. De fiecare dată când această componentă este
utilizată, i se pot atribui noi valori, dacă ele sunt specificate prin parametri
generici.
Aceeaşi entitate poate fi partajată de mai multe arhitecturi şi toţi
parametrii generici se aplică tuturor arhitecturile entităţii. De vreme ce
acelaşi nume de parametru generic poate fi folosit în mai multe arhitecturi,
orice schimbare a valorii sale va afecta toate instanţele sale din toate
arhitecturile. În schimb, dacă se folosesc constante, acestea vor localiza
schimbările numai în arhitectura selectată.

3. Desfăşurarea lucrării

3.1 Se vor crea entităţile Memorie_ROM şi Memorie_RAM


(conform figurii 3.2).
3.2 Se vor descrie prin arhitecturi structurile interne pentru
memoriile ROM şi RAM, utilizând semnale. Harta memoriilor
va fi cea necesară implementării următoarelor funcţii: f1 = Σ (0,
2, 8, 10); f2 = Σ (0, 1, 2, 5, 6, 8, 10); f3 = Σ (1, 2, 4, 5, 7, 11);
f4 = Σ (0, 2, 8, 9, 13, 15);
3.3 Se vor testa modelele de transmitere ale semnalelor pentru
semnalele definite în arhitecturile de la punctul 3.2.
3.4 Se vor implementa şi testa exemplele POARTA_ŞI şi
MUX_2_LA_1 descrise în lucrare pentru înţelegerea utilizării
parametrilor generici.
LUCRAREA NR. 4
OPERATORI. TIPURI DE DATE

1. Scopul lucrării
În lucrare se prezintă toţi operatorii din VHDL, precum şi literalii
utilizaţi în cadrul limbajului. Sunt analizate pe larg tipurile de date utilizate
în cadrul unei descrieri VHDL, împărţite în cele patru mari categorii: tipul
scalar, tipul compus, tipul acces şi tipul fişier. Se subliniază diferenţele
dintre anumite tipuri de date care pot crea confuzii.

2. Consideraţii teoretice
2.1 Operatori

În VHDL există şapte clase de operatori, fiecărei clase fiindu-i


atribuit un nivel de prioritate unic. Această prioritate face ca, atunci când se
evaluează o expresie complexă, operanzii să fie afectaţi operatorului cu
prioritatea cea mai mare, apoi să se aplice acest operator, şi aşa mai departe.
Lista claselor de operatori, în ordinea crescătoare a priorităţilor
(clasa cu prioritatea cea mai mică este prima) este următoarea:
1. Operatorii logici: and, or, nand, nor, xor şi xnor;
2. Operatorii relaţionali: =, /=, <, <=, > şi >=;
3. Operatorii de deplasare: sll, srl, sla, sra, rol, ror;
4. Operatorii de adunare: +, - şi &;
5. Operatorii de semn: + şi –;
6. Operatorii de înmulţire: *, /, mod şi rem;
7. Operatorii diverşi: **, abs, not.
Într-o expresie, ordinea priorităţilor poate evita scrierea de paranteze
inutile. Cu toate acestea, putem folosi paranteze dacă nu suntem siguri de
priorităţi sau dacă dorim să ameliorăm lizibilitatea programului.
În VHDL există posibilitatea de a supraîncărca operatorii, adică de a
le conferi o nouă semnificaţie. Această operaţie nu va modifica însă
prioritatea lor, care este fixată o dată pentru totdeauna.
OPERATORI. TIPURI DE DATE 65
2.1.1 Operatorii logici: and, or, nand, nor, xor şi xnor
Aceşti operatori sunt predefiniţi în vederea realizării operaţiilor
logice „ŞI”, „SAU”, „ŞI-NU”, „SAU-NU”, „SAU-EXCLUSIV” şi
„COINCIDENŢĂ” pe operanzi de tip BOOLEAN (FALSE, TRUE) şi BIT
(‘0’, ‘1’);
Operatorii logici sunt funcţionali şi pe tablouri uni-dimensionale
(numite şi vectori) de elemente de tip BOOLEAN sau BIT, cu condiţia ca
operanzii să aibă aceeaşi lungime (acelaşi număr de elemente). O
particularitate a primilor patru operatori: operandul lor din dreapta nu este
obligatoriu evaluat. Într-adevăr, dacă după evaluarea operandului din stânga
se obţine un rezultat care determină rezultatul operaţiei (de exemplu, o
valoare FALSE pentru operatorul and), compilatorul nu va mai lua în
considerare operandul din dreapta şi va câştiga astfel, în anumite cazuri, un
timp preţios.

2.1.2 Operatorii relaţionali: =, /=, <, <=, > şi >=


Rezultatul acestor operatori este de tip BOOLEAN. Operatorii de
egalitate şi de inegalitate (respectiv „=” şi „/=”) sunt definiţi pe toate tipurile
de date din VHDL, cu excepţia tipului fişier (file).
La compararea a două tipuri compuse (tipuri a căror valoare este
compusă din mai multe elemente), sunt luate în considerare toate elementele
respective. În cazul comparării a două tipuri acces (poantori), compararea se
referă exclusiv la adresele obiectelor referite. Aceşti operatori sunt definiţi
pe toate tipurile enumerate. Ordinea prezentă la definirea tipului enumerat
este păstrată, primul element numit fiind considerat drept cel mai mic.
Astfel, la definirea tipului BOOLEAN, elementul FALSE, fiind declarat
primul, este mai mic decât TRUE.

2.1.3 Operatorii de deplasare: sll, srl, sla, sra, rol, ror


Această clasă conţine şase operatori binari (cu doi operanzi) care
operează exclusiv pe tablouri uni-dimensionale (vectori) de tip BIT sau
BOOLEAN.
a) sll (Shift Left Logical) – efectuează o deplasare logică la stânga:
elementul cel mai din stânga este pierdut (lost value), iar un alt
element numit valoare de umplere (fill value) soseşte din partea
dreaptă. Această valoare de umplere este ‘0’ pentru tablourile de
tip BIT şi FALSE pentru cele de tip BOOLEAN. Singura
66 LIMBAJUL VHDL
posibilitate de a schimba valoarea de umplere este
supraîncărcarea operatorului.
Presupunând că A este un vector de elemente de tip BIT, avem:

B := A sll 0; -- valoarea lui B este identică cu a lui A,


-- operaţie nulă
B := A sll 2; -- valoarea lui B este identică cu a lui A
-- deplasată la stânga cu două poziţii; în
-- partea dreaptă apar doi biţi de ‘0’
B := A sll –3; -- valoarea lui B este identică cu a lui A
-- deplasată la DREAPTA cu trei poziţii. O
-- valoare negativă inversează deci sensul
-- deplasării

b) srl (Shift Right Logical) – efectuează o deplasare logică la


dreapta: este operaţia complementară celei precedente;
c) sla (Shift Left Arithmetic) – efectuează o deplasare aritmetică la
stânga: elementul cel mai din stânga (semnul) este deplasat şi
duplicat;
sll (Shift Left Logical)
lost fill
value value

srl (Shift Right Logical)


fill lost
value value

sla (Shift Left Arithmetic)


lost
value

sra (Shift Right Arithmetic)


lost
value

rol (Rotate Left logical)

ror (Rotate Right logical)

Figura 4.1 Operatorii de deplasare şi de rotire pe biţi


OPERATORI. TIPURI DE DATE 67
d) sra (Shift Right Arithmetic) – efectuează o deplasare aritmetică
la dreapta. Este operatorul complementar celui precedent:
elementul cel mai din dreapta este deplasat şi duplicat;
e) rol (Rotate Left) – efectuează o rotaţie spre stânga: elementul cel
mai din stânga dispare şi reapare în partea dreaptă;
f) ror (Rotate Right) – efectuează o rotaţie spre dreapta: elementul
cel mai din dreapta dispare şi reapare în partea stângă.

2.1.4 Operatorii de adunare: +, -, &


Aceştia sunt operatori binari (cu doi operanzi) de adunare şi scădere
(„+” şi „-“), la care se adaugă operatorul de concatenare („&”).
Concatenarea este definită pe tablourile uni-dimensionale (vectori).
Un şir de caractere (tipul STRING) este un vector de caractere; este deci
evident modul de aplicare a operatorului de concatenare. De exemplu,
operaţia “ROM” & “NIA” are drept rezultat “ROMÂNIA”.

2.1.5 Operatorii de semn: +, -


Aceştia sunt operatori unari, adică se aplică pe un operand unic.
Dacă vom supraîncărca operatorii de adunare, operatorii de semn nu vor fi
supraîncărcaţi în mod automat, ci această supraîncărcare a lor va trebui
făcută în mod explicit.

Observaţie
Operatorii de semn au o prioritate mai mică decât operatorii de
înmulţire, ceea ce nu este normal.
Astfel, expresia 2* -3, care ar trebui să ia valoarea –6, va genera o
eroare (nu se poate evalua înmulţirea înaintea evaluării operatorului unar
„-“). Introducerea de paranteze va rezolva problema (se va scrie: 2*(-3)).

2.1.6 Operatorii de înmulţire: *, /, mod, rem


Operaţia de împărţire pe tipuri de date întregi efectuează o rotunjire
prin trunchierea părţii fracţionare.
Următoarele relaţii sunt verificate în VHDL:

(-A)/B = -(A/B) = A/(-B)


68 LIMBAJUL VHDL
Operatorul rem (de la remainder) reprezintă restul împărţirii întregi
definite mai sus. Prin urmare, presupunând că A şi B sunt numere întregi,
vom avea:

A = (A/B) * B + (A rem B)

A rem B are semnul lui A.


Operatorul mod (de la modulo) este definit de următoarea relaţie:

A = B * N + (A mod B) --N este un număr întreg oarecare

A mod B e mai mic ca B (în valoare absolută) şi are semnul lui B.

2.1.7 Operatorii diverşi: **, abs, not


Operatorul ** este numit exponenţial (ridicare la putere). Acest
operator se aplică unui operand aflat la stânga sa, care este de un tip întreg
sau flotant. Operandul din dreapta (puterea) trebuie obligatoriu să fie un
întreg.
Operatorul abs returnează valoarea absolută a oricărui tip numeric.
not este un operator logic unar. El operează deci asupra obiectelor de
tip BOOLEAN, BIT şi asupra vectorilor cu elemente de aceste tipuri.

2.2 Literali

În VHDL, noţiunea de literal este puţin diferită faţă de alte limbaje


de programare cunoscute. De exemplu, 123 este un literal. Literalii se
clasifică în:
- literali numerici (123, 3.14);
- literali enumeraţi;
- literali şiruri de caractere sau şiruri de biţi;
- literalul null – reprezintă orice obiect de tip acces neiniţializat.
În mod evident, literalul nu poartă cu el tipul său, ci numai familia sa
de tipuri.
În VHDL sunt autorizate numai 95 de caractere ASCII (din setul
ASCII complet au fost excluse caracterele de control). Aceste caractere sunt
notate între apostrofuri: ‘x’, ‘Y’, ‘7’, ‘*’, ‘ ‘ (spaţiu) etc.
OPERATORI. TIPURI DE DATE 69
Şirurile de caractere se notează între ghilimele. Ele nu trebuie să
depăşească o linie de cod sursă. În cadrul lor se face deosebirea dintre litere
mari şi mici. Câteva exemple: “Salut”, “T78%0-#”, “1010”.
Uneori se poate dovedi utilă scrierea unui şir de caractere pe două
linii – acest lucru se poate face folosind simbolul de concatenare. Iată un
exemplu:
“Se poate” &
“scrie pe două linii”.
Pentru numerele zecimale utilizate, un element interesant îl
constituie posibilitatea folosirii liniuţei de subliniere (underscore) pentru
delimitarea grupelor de cifre zecimale, atât pentru numere întregi cât şi
pentru numere reale. Iată câteva exemple: 22, 1971 sau 1_971, 84000000
sau 84_000_000 sau 84E6 sau 84E+6, 7.0, 1971.0 sau 1_971.0, 84000000.0
sau 84_000000.0 sau 84.0E6 sau 84.0E+6.
Numerele întregi şi cele reale pot fi exprimate în diferite baze, de la
baza 2 (sistemul numeric binar) până la baza 16 (sistemul numeric
hexazecimal). Notaţia este următoarea: întâi se exprimă baza (în zecimal),
după care urmează numărul propriu-zis, încadrat de caracterul # (diez).
Iată câteva exemple de reprezentare a numărului întreg 255:

2#1111_1111# sau 4#3333# sau 10#255# sau 16#FF#

Iată câteva exemple de reprezentare a numărului real 255.25:

2#1111_1111.01# sau 2#1.111111101#E7 sau 4#3333.1# sau


16#FF.4#

În cazul folosirii tipului BIT standard, în VHDL se obişnuieşte să se


folosească notaţia prin şiruri de biţi (aşa-numitul bit string). Acest tip de
date este exportat de pachetul STANDARD şi poate lua valorile ‘0’ sau ‘1’.
Notaţia prin şiruri de biţi permite iniţializarea vectorilor de biţi.
Valoarea se scrie între ghilimele (la fel ca în cazul şirurile de caractere) şi
este precedată de litera B pentru scrierea binară, O pentru scrierea octală şi
X pentru scrierea hexazecimală. Astfel, X”ABC” este echivalent cu
B”101010111100”, iar O”427” este echivalent cu B”100010111”. X”ABC”
este echivalent şi cu ”101010111100” şi poate deci să fie utilizat oriunde
”101010111100” este valid.
Fiecare expresie are un tip şi o valoare în momentul evaluării sale.
70 LIMBAJUL VHDL
2.3 Tipuri de date

VHDL este un limbaj puternic tipizat: fiecare constantă, variabilă


sau semnal trebuie să aibă un tip înainte de utilizarea sa. De asemenea,
parametrii unei proceduri sau ai unei funcţii, precum şi valoarea returnată de
funcţia respectivă, sunt obiecte tipizate.
Există patru mari tipuri de date în VHDL:
• tipurile scalare – valoarea lor este constituită dintr-un singur
element;
• tipurile compuse – valoarea lor este constituită din mai multe
elemente;
• tipurile acces sau poantorii (pointers);
• tipurile fişier.

Observaţie
Semnalele nu pot avea tipul acces; nici măcar un sub-element al unui
semnal nu poate avea acest tip.
Numai variabilele pot avea tipul acces; fişierele constituie o familie
cu totul specială de tipuri. Semnul de întrebare din figura 4.1 indică faptul
că fişierele pot fi văzute ca variabile a căror valoare nu poate fi modificată.

Constante Variabile Semnale Fişiere


Scalare DA DA DA NU
Compuse DA DA DA NU
Acces NU DA NU NU
Fişier NU NU (?) NU DA

Figura 4.1 Clase de obiecte şi familii de tipuri

2.3.1 Tipurile scalare


Din această familie fac parte următoarele tipuri:
a) tipurile enumerate;
b) tipurile întregi;
c) tipurile flotante;
d) tipurile fizice.
OPERATORI. TIPURI DE DATE 71
Toate aceste tipuri sunt ordonate (există o relaţie de ordine definită
asupra lor), aşadar valorile lor vor putea fi comparate cu ajutorul
operatorilor relaţionali: =, /=, <, <=, > şi >=.
Valorile posibile ale unui tip scalar pot fi restrânse prin indicarea
unui interval de validitate. Notaţia pentru acest interval este următoarea:

range expresie1 to expresie2

Dacă expresie1 este mai mare decât expresie2, intervalul va fi nul


(vid). În acest caz se pot inversa capetele intervalului de definiţie:

range expresie3 downto expresie4

a) Tipurile enumerate
Toate declaraţiile de tip încep cu cuvântul cheie type. Pentru
declararea acestui tip de date, este suficientă indicarea valorilor simbolice
(identificatori sau caractere) pe care le poate lua. De exemplu:

type CULOARE is (ROŞU, GALBEN, ALBASTRU);


type STARE is (INIŢIALIZARE, CITIRE, SCRIERE, AŞTEPTARE);
type ZI is (LUNI,MARŢI,MIERCURI,JOI,VINERI,SÂMBĂTĂ,DUMINICĂ);
type CULOARE_MAŞINĂ is (ROŞU, ALB, VERDE);

Un acelaşi simbol (în exemplul de mai sus, simbolul ROŞU) poate


să aparţină mai multor tipuri enumerate (se spune că apare o
supraîncărcare). Contextul acestei supraîncărcări trebuie să permită
eliminarea ambiguităţii.
În pachetul STANDARD sunt predefinite următoarele tipuri
enumerate: BOOLEAN, BIT, CHARACTER şi SEVERITY_LEVEL.

type BOOLEAN is (FALSE, TRUE);


type BIT is (‘0’, ‘1’);
type SEVERITY_LEVEL is (NOTE, WARNING, ERROR, FAILURE);

Tipul CHARACTER conţine enumerarea tuturor caracterelor admise


într-un program VHDL.
Simbolurile utilizate în cadrul unui tip enumerat vor fi numai
identificatori sau caractere. În cadrul tipurilor enumerate apare noţiunea de
72 LIMBAJUL VHDL
poziţie. Primul element din definiţia tipului primeşte poziţia 0, următorul
poziţia 1 etc. Atributul predefinit POS, care se apelează la fel ca o funcţie,
returnează poziţia unui element al unui tip enumerat. Astfel,
BOOLEAN’POS(TRUE) are valoarea 1. Această poziţie este cea care
induce relaţia de ordine asupra elementelor. Operatorii de comparare sunt
disponibili în cadrul tipurilor enumerate, ceea ce va face ca, de exemplu,
TRUE să fie mai mare decât FALSE.

b) Tipurile întregi
Tipurile numerice întregi pot fi văzute ca tipuri enumerate (ele
reprezintă un şir ce poate fi enumerat). Pe toate aceste tipuri sunt definiţi
operatorii aritmetici. Toate tipurile numerice întregi pot fi convertite implicit
într-un tip virtual numit UNIVERSAL_INTEGER, aspect care asigură
compatibilitatea tuturor tipurilor numerice.
Pentru a declara un tip numeric întreg este suficient să indicăm
intervalul pe care este definit cu ajutorul cuvântului cheie range. De
exemplu, porţiunea de cod VHDL de mai jos reprezintă declaraţia unui tip
enumerat întreg care variază între 1 şi 22, inclusiv capetele intervalului:

type DISTANŢA is range 1 to 22;

În pachetul STANDARD există un tip predefinit şi deosebit de util:


INTEGER.

type INTEGER is range –2_147_483_648 to 2_147_483_647;

De fapt, capetele acestui interval (care pot fi aflate cu ajutorul


atributelor INTEGER’LOW şi INTEGER’HIGH) variază cu lăţimea
cuvintelor calculatorului pe care rulează compilatorul VHDL.

c) Tipurile flotante
La fel ca în cazul tipurilor numerice întregi, declaraţia unui tip
flotant se face prin specificarea capetelor intervalului său de definiţie (aceste
capete ale intervalului de definiţie trebuie să fie şi ele flotante):

type FLOTANTUL_MEU is range 1.63 to 3.14;


OPERATORI. TIPURI DE DATE 73
Operatorii aritmetici se pot aplica pe toate aceste tipuri. Toate
tipurile numerice flotante pot fi convertite implicit într-un tip virtual numit
UNIVERSAL_REAL. Există un tip real predefinit în pachetul
STANDARD:

type REAL is range –16#0.7FFFFF8#E+32 to 16#0.7FFFFF8#E+32;

Capetele acestui interval depind de implementarea specifică a


analizorului VHDL, însă standardul VHDL precizează că intervalul trebuie
să fie de cel puţin [–1E38, 1E38] (în notaţie zecimală), cu o precizie de
minimum 6 cifre după virgulă (această condiţie este respectată în definiţia
de mai sus, deoarece notaţia utilizată este cea hexazecimală).

d) Tipurile fizice
Aceste tipuri sunt foarte apropiate de tipurile întregi. În limbajul
VHDL există noţiunea de unitate de cantitate. De exemplu, în pachetul
STANDARD este definit tipul TIME, care este un tip fizic. Acesta este
singurul tip fizic predefinit şi el este utilizat de către simulator.
Un tip fizic este caracterizat de:
• Unitatea de bază (femtosecunda, pentru tipul TIME);
• Intervalul valorilor autorizate;
• O eventuală colecţie de sub-unităţi împreună cu corespondenţele
lor.
În cele ce urmează este prezentată definiţia tipului TIME din
pachetul STANDARD. Intervalul valorilor sale autorizate depinde de
implementare. În varianta de mai jos este propusă o codificare pe 64 de biţi:

--Tipul TIME predefinit


type TIME is range –9_223_372_036_854_775_808 to
–9_223_372_036_854_775_807 -- Codificare pe 64 de biţi!
units fs; -- femtosecundă
ps = 1000 fs; -- picosecundă
ns = 1000 ps; -- nanosecundă
us = 1000 ns; -- microsecundă
ms = 1000 us; -- milisecundă
sec = 1000 ms; -- secundă
min = 60 sec; -- minut
hr = 60 min; -- oră
end units;
74 LIMBAJUL VHDL
Astfel, atunci când utilizăm o expresie de tip TIME, putem scrie la
fel de bine 20 fs, sau 30 us, sau 3 min. De asemenea, expresia 345 fs + (347
ps/ 20) + 2 ns are sens şi valoarea sa este 2_017_695 fs.

Observaţie
Între număr şi unitatea de măsură există întotdeauna un caracter
„spaţiu”. Acest aspect nu este specificat explicit în norma VHDL, aşa că
fiecare constructor de instrumente VHDL realizează propria sa
implementare, în care poate să ţină sau nu cont de aceasta.

Pe tipurile fizice sunt definite toate operaţiile aritmetice. Toate


valorile unui tip fizic şi toate valorile de conversie între unităţi trebuie să fie
întregi. Practic, aceasta implică să avem întotdeauna unitatea de bază – cea
mai mică dintre toate unităţile.
Putem construi orice tip fizic pe baza modelului oferit de tipul
TIME. De exemplu, în manualul de referinţă al VHDL este oferit următorul
exemplu:

type DISTANCE is range 0 to 1 E16


units A; -- angstrom
nm = 10 A; -- nanometru
um = 1000 nm; -- micron
mm = 1000 um; -- milimetru
cm = 10 mm; -- centimetru
m = 1000 mm; -- metru
km = 1000 m; -- kilometru
mil = 254000 A; -- mil
inch = 1000 mil; -- inch
ft = 12 inch; -- foot
yd = 3 ft; -- yard
mi = 5280 ft; -- milă
lg = 3 mi; -- leghe
end units;

Aşadar, se poate scrie:

variable Dis1, Dis2 : DISTANCE;


........
Dis1 := 18 mm;
Dis2 := 2 cm - 1 mm;
if Dis1 < Dis2 then ...
OPERATORI. TIPURI DE DATE 75
2.3.2 Tipurile compuse
Din această familie fac parte următoarele tipuri:
• Tablourile - care reprezintă o colecţie de obiecte de acelaşi tip;
• Articolele - care reprezintă o colecţie de obiecte de tipuri diferite.

a) Tablourile
Structurile omogene se descriu în VHDL prin tablouri. De exemplu,
o magistrală de semnale văzută ca o structură omogenă de biţi poate fi
reprezentată printr-un tablou. Acest lucru este făcut în pachetul
STANDARD prin declararea tipului BIT_VECTOR.
Elementele acestor structuri sunt accesibile cu ajutorul unuia sau mai
multor indecşi (numărul acestora poate fi oarecare). Un tabel cu un singur
index este numit vector.
Definirea unui tablou necesită specificarea tipului şi a numărului
indecşilor şi a tipului elementelor tabloului. Tipul indecşilor poate fi orice
tip discret (enumerat sau întreg). Tipul elementelor poate fi oricare, cu
excepţia tipului fişier.
Există două tipuri de tablouri:
- tablourile constrânse, în care intervalul de variaţie a indecşilor
este cunoscut cu anticipaţie. Sensul de variaţie al indecşilor este
specificat cu ajutorul cuvintelor cheie to şi downto. Iată câteva
definiţii de tablouri constrânse:

type CUVÂNT is array (0 to 21) of BIT;


type LOGIC4 is (‘X’, ’0’, ‘1’, ‘Z’);
type INTRARE is array (0 to 7) of LOGIC4;
--Utilizând tipul POSITIVE din pachetul STANDARD:
constant JOS: INTEGER := 7;
constant SUS: INTEGER := 22;
type INTERVAL is array (POSITIVE range JOS to SUS) of BIT;

- tablourile neconstrânse, în care intervalul de variaţie al


indecşilor nu va fi cunoscut decât în momentul execuţiei codului
(în timpul simulării). Pentru tablourile neconstrânse, simbolul
„<>”, care se citeşte box, permite amânarea definirii unui
interval de indexare şi a unei direcţii de variaţie până în
momentul execuţiei. În pachetul STANDARD sunt definite două
tablouri neconstrânse:
76 LIMBAJUL VHDL

type BIT_VECTOR is array (NATURAL range <>) of BIT;


type STRING is array (POSITIVE range <>) of CHARACTER;

b) Articolele
Spre deosebire de tablouri, articolele pot conţine elemente de tipuri
diferite. Aceste elemente se mai numesc şi câmpuri şi sunt desemnate prin
nume şi nu prin index, ca în cazul tablourilor. Pentru a descrie un articol,
este suficient să enumerăm câmpurile care fac parte din el între cuvintele
cheie record şi end record.
Iată un exemplu de definiţie a unui tip articol:

type CAPACITATE is range 0 to 1_000_000_000


units pF; -- picofarad
nF = 1000 pF; -- nanofarad
uF = 1000 nF; -- microfarad
end units;
type REZISTENŢĂ is range 0 to 1_000_000_000
units Ohm; -- ohm
kOhm = 1000 Ohm; -- kiloohm
MOhm = 1000 kOhm; -- megaohm
end units;
type CIRCUIT_RC is
record
PREŢ: INTEGER; -- preţul de vânzare
CAP_INTERNĂ: CAPACITATE; -- tipul fizic definit anterior
REZ_INTERNĂ: REZISTENŢĂ; -- tipul fizic definit anterior
end record;

Elementele sau câmpurile sunt selectate prin „notaţia cu punct”. Să


presupunem că avem următorul semnal şi următoarea variabilă:

signal A: CIRCUIT_RC; -- un semnal de tipul declarat anterior


variable B: CIRCUIT_RC; -- o variabilă de acelaşi tip

În cazul semnalului A: A. PREŢ este de tipul INTEGER, A.


CAP_INTERNĂ este de tipul CAPACITATE şi A.REZ_INTERNĂ este de
tipul REZISTENŢĂ. Aceleaşi notaţii sunt valabile şi în cazul variabilei B.
Asignările pot fi făcute individual:

A. CAP_INTERNĂ <= B. CAP_INTERNĂ after 100 ns;


OPERATORI. TIPURI DE DATE 77
În pilotul (driver-ul) semnalului A, elementului CAP_INTERNĂ i se
va atribui valoarea elementului CAP_INTERNĂ a variabilei B, la 100 ns de
la începerea simulării (ne referim la timpul simulatorului, nu este vorba
despre un timp real).
Este de asemenea posibilă asignarea simultană de valori tuturor
câmpurilor:

A <= B;

De asemenea, este posibil ca tuturor câmpurilor articolului să le fie


asignate valori dintr-o dată, cu ajutorul unui agregat. Iată două exemple:

B : = (‘10_000’, 35 pF, 7 kOhm); --Notaţia poziţională


B := (PREŢ => ‘20_000’, REZ_INTERNĂ => 700 kOhm, CAP_INTERNĂ
=> 70 pF); --Notaţia după nume

c) Notaţia prin agregare


Un agregat constituie o modalitate de a indica valoarea unui tip
compus. Agregatul se scrie între paranteze, elementele fiind separate prin
virgule. Verificarea corespondenţei dintre tipurile valorilor asignate şi
tipurile elementelor tipului compus vizat este efectuată de către compilator.
Vom exemplifica pe următoarele tipuri de date:

--Tablou de şapte elemente de tip întreg


type TAB is array (1 to 7) of INTEGER;
--Articol de trei elemente
type ART is record
C1: INTEGER;
C2: BIT;
C3: INTEGER;
end record;

În cazul unui agregat există trei posibilităţi distincte de notare:


• Asocierea poziţională constă pur şi simplu în a înşira elementele
ca într-o listă. Asocierea dintre câmpul articolului sau al
indicelui tabloului se face graţie poziţiei sale din listă. Această
ordine corespunde celei din declaraţia tipului compus. Pe
exemplele de mai sus, putem nota:
- pentru tipul TAB: (2,4,6,8,10,12,14)
78 LIMBAJUL VHDL
- pentru tipul ART: (6,’0’,8)
• Asocierea prin denumire permite precizarea numelui elementului
(numele câmpului sau al indexului) şi valoarea corespunzătoare.
Se utilizează simbolul „=>” pentru marcarea acestei
corespondenţe. Ordinea câmpurilor nu mai are importanţă.
Pentru a realiza aceleaşi asignări ca şi mai sus, notaţia este
următoarea:
- pentru tipul TAB: (1=>2, 2=>4, 3=>6, 4=>8, 5=>10, 6=>12,
7=>14)
- pentru tipul ART: (C1=>6, C2=>’0’, C3=>8)
sau:
- pentru tipul TAB: (1=>2, 7=>14, 3=>6, 5=>10, 2=>4,
6=>12, 4=>8)
- pentru tipul ART: (C2 => ‘0’, C3 =>8, C1 => 6)
• Asocierea mixtă este un amestec al primelor două notaţii. Ea
trebuie neapărat să înceapă cu partea sa poziţională, apoi să se
încheie cu asocierile după nume. Utilizarea cuvântului cheie
others, care desemnează toate câmpurile sau indecşii (în cazul
tabloului) neasignaţi încă, permite efectuarea „dintr-o dată” a
acestei asignări. De exemplu:
- pentru tipul TAB: (2, others =>0)
- pentru tipul ART: (C2 => ‘1’, others =>0)
ceea ce este echivalent cu:
- pentru tipul TAB: (2, 0, 0, 0, 0, 0, 0)
- pentru tipul ART: (0, ‘1’, 0)
Există câteva cazuri particulare:
• Un agregat poate să nu conţină decât cuvântul cheie others
urmat de valoarea pe care dorim să o asignăm tuturor
elementelor. Această notaţie se utilizează adeseori atunci
când se urmăreşte iniţializarea tuturor elementelor unui
tablou cu o aceeaşi valoare;
• Un agregat care conţine un singur element trebuie să fie
exprimat folosind asocierea prin denumire, pentru a evita
orice ambiguitate cu o expresie dată între paranteze;
• În cazul utilizării clauzei others pentru a asigna o valoare
câmpurilor unui articol, aceste câmpuri trebuie să fie toate de
acelaşi tip.
OPERATORI. TIPURI DE DATE 79
2.3.3 Tipurile acces
Un tip acces este adeseori numit poantor (pointer). Un tip acces
„indică” („poantează”) spre un obiect de un tip definit anterior. Rostul unui
asemenea tip este, în principal, cel de a permite crearea dinamică (adică, în
timpul rulării) a unor noi obiecte. Cuvântul cheie utilizat pentru desemnarea
acestui tip este access.
Acest tip de date permite alocarea dinamică a memoriei. Stilul de
manipulare a obiectelor de acest tip este identic cu cel al limbajelor de
programare clasice (C, Pascal etc.). Prin urmare, sunt valabile toate regulile
şi metodele de programare referitoare la: alocarea dinamică a obiectelor de
tip acces, dealocarea zonei de memorie corespunzătoare ocupate, crearea
structurilor de date dinamice specifice (liste simplu şi dublu înlănţuite,
arbori etc.) şi modul de operare cu ele (inserarea unui element, ştergerea
unui element etc.) Aceste aspecte fiind foarte cunoscute în limbajele de
programare clasice, care constituie totuşi o cerinţă preliminară pentru
însuşirea limbajului VHDL, ele nu vor fi detaliate aici.
Toate tipurile acces sunt de aceeaşi natură (fizic, ele reprezintă
adresa unui obiect) dar, pentru o mai mare siguranţă, ele sunt tipizate în
funcţie de tipul obiectului spre care poantează. Aşadar, un poantor (pointer)
spre un întreg va fi diferit de un poantor (pointer) spre un flotant. La
declarare, tipurile acces sunt iniţializate cu o valoare implicită care este null.
În continuare este necesar să alocăm o zonă de memorie pentru
stocarea obiectelor spre care se poantează. Aceasta se face cu ajutorul
instrucţiunii speciale new. De foarte multe ori – dar nu obligatoriu – putem
profita de această alocare pentru a iniţializa valoarea obiectului poantat.
După ce obiectul şi-a încheiat ciclul de viaţă, procedura
DEALLOCATE (declarată automat) va elibera zona de memorie ocupată.
Iată câteva declaraţii de tip acces:

type P_INT is access INTEGER; -- Poantor la întreg


variable A,B: P_INT;
type COMPLEX is
record
NUME: STRING (1 to 7);
DATA: TIME;
VALOAREA: BIT;
end record;
type P_ COMPLEX is access COMPLEX; -- Poantor la un articol
variable ELEM: P_ COMPLEX;
80 LIMBAJUL VHDL
La acest nivel al declaraţiilor, egalităţile A = null, B = null şi
ELEM = null sunt adevărate.
Câteva exemple de alocări:

A := new INTEGER’(22); -- A poantează la întregul 22;


B := new INTEGER; -- B poantează la un întreg. Iniţializarea
-- implicită este la INTEGER’LOW, cel mai
-- mic număr întreg care poate fi codificat
-- ELEM poantează la un articol: câmpul NUME va fi
-- iniţializat la “ADUNAT”, câmpul DATA va fi iniţializat la
-- 50 ns, iar câmpul VALOAREA va fi iniţializat la ‘1’
ELEM := new COMPLEX’(“ADUNAT”, 50 ns, ‘1’);

Dealocarea se face simplu:

DEALLOCATE(A);

Obiectul A va deveni astfel inaccesibil, iar locul ocupat de el în


memorie va putea fi utilizat în alte scopuri. Această operaţie trebuie
efectuată prin program, altminteri existând riscul ca toată zona de memorie
rezervată alocărilor dinamice să fie „consumată”.

2.3.4 Tipurile fişier


Fişierele sunt foarte importante în VHDL. Ele pot fi folosite, de
exemplu, pentru încărcarea conţinutului unui modul de memorie ROM sau
pentru a defini un ansamblu de stimuli. Acest stil de lucru va evita
necesitatea de a recompila proiectul, ceea ce va duce la o importantă
reducere a timpului de proiectare efectiv. De asemenea, fişierele sunt
folosite pentru interfaţarea cu alte instrumente de dezvoltare, ele aflându-se
la baza interacţiunii cu utilizatorul (pentru că acesta e „văzut” ca un fişier).
Toate fişierele sunt cu acces secvenţial: valorile scrise în / citite
dintr-un fişier sunt plasate în secvenţă, unele după celelalte, ca pe o bandă
magnetică.
La declararea unui fişier trebuie indicat şi tipul de date al
elementelor din care este alcătuit:

type FIŞIER_DE_PROGRAMARE is file of BIT;


type FIŞIER_TEXT is file of STRING;
type FIŞIER_DE_DATE is file of INTEGER;
OPERATORI. TIPURI DE DATE 81
Există câteva limitări în privinţa tipurilor de date care se pot afla
într-un fişier. Un fişier nu poate conţine:
- poantori (pointer-i) sau tipuri compuse;
- alte obiecte de tip fişier;
- tablouri (cu excepţia vectorilor).
Declararea unui tip fişier are ca efect crearea implicită a trei sub-
programe care vor permite citirea din şi scrierea în fişierul respectiv.
a) Procedura de citire (READ)
Această procedură are ca parametru de intrare un tip fişier şi
returnează valoarea următoare citită din acest fişier. Aici, parametrul
formal corespunzător fişierului de intrare se numeşte F. El este de
tipul fişier TF care se presupune că a fost declarat anterior. VALUE
este parametrul formal de ieşire; el este de tipul TM (fişierul TF a
fost definit ca un fişier conţinând date de tipul TM).

procedure READ (F: in TF; VALUE: out TM);

b) Procedura de scriere (WRITE)


Este complementara procedurii de citire (READ). Ea primeşte o
valoare pe care o va scrie la sfârşitul fişierului dat.

procedure WRITE(F: out TF; VALUE: in TM);

c) Funcţia ENDFILE
Această funcţie primeşte un parametru de intrare de tip fişier şi ne
permite să aflăm dacă am ajuns la sfârşitul fişierului. Ea returnează
valoarea FALSE dacă se mai poate citi cel puţin încă o valoare din
fişierul respectiv.
Dacă fişierul a fost utilizat în mod out (în scriere), rezultatul boolean
este întotdeauna adevărat. În cazul în care se va încerca efectuarea
unei operaţii de citire (READ) asupra unui fişier pentru care apelul
funcţiei ENDFILE ar returna valoarea „adevărat”, se va genera o
eroare la simulare.

function ENDFILE (F: in TF) return BOOLEAN;


82 LIMBAJUL VHDL

Observaţie
Pentru tablourile neconstrânse, procedura READ returnează, pe
lângă valoarea citită din fişier, şi lungimea tabloului. Atunci, profilul
funcţiei nu este cel de mai sus, ci următorul:

procedure READ(F: in TF; VALUE: out TM; LENGTH: out NATURAL);

Majoritatea intrărilor curente făcându-se sub formă de şiruri de


caractere, pachetul TEXTIO permite o gestiune mai fină a acestora. Acest
pachet standard este descris în lucrarea nr. 12.
Începând cu versiunea VHDL’93, au apărut două proceduri de
deschidere, respectiv de închidere de fişiere: FILE_OPEN şi FILE_CLOSE.
Foarte utilă în anumite cazuri, această facilitate trebuie totuşi utilizată cu
precauţie. Într-adevăr, a avea posibilitatea de a deschide şi de a închide un
fişier le permite unor procese concurente să comunice, „ascunzând” însă
acest fapt mecanismului de simulare al VHDL, care se bazează pe
conceptele de semnal şi de eveniment. Rezultatele pot fi neaşteptate…
Cu ocazia efectuării declaraţiei:

type tipul_meu_de_fişier is file of tipul_meu_de_obiect;

vor fi efectuate implicit următoarele declaraţii:

procedure FILE_OPEN (file F: tipul_meu_de_fişier;


EXTERNAL_NAME: in STRING;
OPEN_KIND: in FILE_OPEN_KIND := READ_MODE);

procedure FILE_OPEN (STATUS: out FILE_OPEN_STATUS;


file F: tipul_meu_de_fişier;
EXTERNAL_NAME: in STRING;
OPEN_KIND: in FILE_OPEN_KIND := READ_MODE);

Se observă că a doua supraîncărcare oferă un parametru de ieşire


(STATUS) care permite testarea unei erori de tipul „fişier inexistent”.
În acelaşi mod, sunt definite implicit şi disponibile următoarele
proceduri şi funcţii:
OPERATORI. TIPURI DE DATE 83

procedure FILE_CLOSE (file F: tipul_meu_de_fişier);


procedure WRITE (file F: tipul_meu_de_fişier,
VALUE: in tipul_meu_de_obiect);
function ENDFILE (file F: tipul_meu_de_fişier)return BOOLEAN;
procedure READ(file F: tipul_meu_de_fişier, --Pentru obiecte
VALUE: out tipul_meu_de_obiect); --constrânse
-- Pentru obiecte neconstrânse, cum ar fi STRING sau
-- BIT_VECTOR; astfel se returnează şi lungimea acestora
procedure READ(file F: tipul_meu_de_fişier,
VALUE: out tipul_meu_de_obiect, LENGTH: out NATURAL);

2.4 Expresii calificate

Rolul calificării expresiilor este de a înlătura o ambiguitate


referitoare la tipul unei expresii sau al unui agregat, indicând explicit
numele acestui tip. Nu este o conversie de tip, ci o informaţie transmisă
compilatorului pentru a-l ajuta să rezolve o ambiguitate.
Sintaxa calificării este următoarea:

tipul_obiectului‘(expresie) -- Prima variantă


tipul_obiectului‘agregat -– A doua variantă

Elementul important (care nu trebuie uitat!) este apostroful; fără el,


calificarea se transformă într-o conversie de tip.
În continuare prezentăm un exemplu de calificare a expresiilor în
contextul pachetului TEXTIO (pachetul standard de intrări şi ieşiri). În
cadrul acestui pachet sunt declarate mai multe proceduri WRITE, printre
care şi următoarele două:

procedure WRITE (L: inout LINE; VALUE: in BIT_VECTOR;


JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH:=0);
procedure WRITE (L: inout LINE; VALUE: in STRING;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH:=0);

Acesta este un exemplu de supraîncărcare, cele două proceduri fiind


diferite din punct de vedere al profilului. Diferenţa este datorată exclusiv
celui de-al doilea parametru, care în prima procedură are tipul
BIT_VECTOR, iar în cea de-a doua are tipul STRING.
84 LIMBAJUL VHDL
Următoarea instrucţiune, în care LINIE_CRT este o variabilă de tip
LINE, va crea o ambiguitate pe care compilatorul nu o poate rezolva de unul
singur:

WRITE (LINIE_CRT, “1010”);

Ambiguitatea constă în faptul că nu se ştie dacă se face referire la


vectorul de patru biţi 1,0,1,0 sau la şirul de caractere “1010”. Compilatorul
nu va putea alege pe care dintre cele două proceduri s-o apeleze.
Soluţia constă în utilizarea calificării expresiei. De exemplu, dacă ne
referim la un şir de caractere, vom scrie:

WRITE (LINIE_CRT, STRING’(“1010”)); -- Atenţie la apostrof!

Această calificare reprezintă o indicaţie, un ajutor oferit


compilatorului. Ea nu este o operaţie şi nu consumă nici spaţiu de memorie,
nici timp de execuţie suplimentar.

Observaţie
Notaţia aleasă, “1010” este deosebit de ambiguă. Acelaşi exemplu
rămâne valabil şi pentru notaţia “QWERTY”, chiar dacă ambiguitatea cu
tipul BIT_VECTOR pare exclusă (din punct de vedere uman): compilatorul
nu va face presupuneri în privinţa conţinutului, ci va reacţiona la notaţia “”!

2.5 Conversii de tipuri

Între două tipuri cu structuri apropiate se pot efectua conversii


explicite. Conversia este foarte restrictivă şi nu este autorizată decât în
următoarele trei cazuri:
- Conversii de tipuri cu reprezentare întreagă sau flotantă;
- Conversii de tablouri (cu anumite restricţii);
- Conversia unui tip în el însuşi.
Restricţiile menţionate în cazul tablourilor se referă la faptul că
acestea trebuie să aibă:
- aceleaşi dimensiuni;
- acelaşi tip de elemente;
- indecşii lor trebuie să fie cel puţin convertibili.
OPERATORI. TIPURI DE DATE 85
Sintaxa generală a conversiei de tip este următoarea:

Tipul_spre_care_se_face_conversia (valoarea_de_convertit)

Trebuie ca tipul valorii de convertit să fie cunoscut fără ambiguitate


şi ca respectiva conversie să fie autorizată.
Iată un exemplu de conversie:

variable A,B: REAL;


variable I: INTEGER;
...
A := B*REAL(I); -- În ambele cazuri este vorba despre
I : = INTEGER(A*B); -- o operaţie de înmulţire reală

Spre deosebire de expresiile calificate, în acest caz se realizează


efectiv o operaţie. De pildă, în cazul liniei a doua din exemplul de mai sus,
variabilei I îi este atribuit numărul întreg cel mai apropiat de rezultatul real
al înmulţirii lui A cu B.

3. Desfăşurarea lucrării
3.1 Se va exersa definirea tuturor tipurilor de date şi operatori. Se va
crea un tip fizic numit VOLUM şi un tip fizic VALUTĂ pentru
conversia între diferite monede.
3.2 Se va crea o listă simplu înlănţuită cu elemente de tip acces, cu
alocări şi dealocări.
3.3 Se va crea un modul REGISTRU_DE_DEPLASARE, folosind
operatorii cei mai adecvaţi pentru gestionarea datelor interne.
3.4 Se va crea o unitate aritmetică-logică, în care intrările şi ieşirile
se vor exprima sub formă de articole şi care poate efectua
următoarele operaţii, pe cei doi operanzi de intrare de tip întreg
INTEGER: AND, OR, Înmulţire cu o putere a lui 2, Împărţire la
un număr putere a lui 2, adunare şi scădere.
LUCRAREA NR. 5
ATRIBUTE

1. Scopul lucrării
Lucrarea prezintă pe larg toate atributele existente în VHDL, atât
cele predefinite cât şi cele ce pot fi definite de către proiectant. Se oferă
exemple în cazul fiecărui atribut şi se explică pe larg modul lor de utilizare.

2. Consideraţii teoretice
2.1 Definiţie

Un atribut este o caracteristică asociată unui tip sau unui obiect (de
exemplu, numărul de elemente ale unui tablou) care va putea fi cunoscută în
mod dinamic în cursul rulării programului.
Atributele se notează prin adăugarea unui apostrof după numele
tipului sau al obiectului. Astfel, de exemplu, cel mai mare număr întreg care
poate fi reprezentat se scrie INTEGER'HIGH.
Există două mari clase de atribute:
- atribute predefinite;
- atribute care urmează să fie definite de către proiectant.

2.2 Atribute predefinite

Atributele predefinite sunt foarte utilizate în VHDL, ele intrând


adeseori în componenţa anumitor funcţii utilitare şi contribuind astfel la
simplificarea scrierii anumitor teste. De exemplu, detectarea frontului
ascendent (cel mai adesea, al semnalului de tact) sau verificarea stabilităţii
nivelului HIGH - în ambele cazuri, pentru un semnal de tip BIT.
Atributele predefinite pot fi:
- valori (tipizate);
- funcţii;
- tipuri;
- intervale de variaţie.
ATRIBUTE 87
Ele se aplică unor prefixe care pot fi, la rândul lor:
- valori;
- tipuri;
- etichete de blocuri.
În continuare se prezintă lista atributelor predefinite pentru fiecare
gen de prefix.

2.2.1 Atribute definite pe tipuri


Vom utiliza în continuare, ca exemplu de lucru, următoarele definiţii
de tipuri:

-- Tipul NIVEL este definit ca un şir de valori enumerate


type NIVEL is ('U', '0', '1', 'Z');

-- Tipul ADRESĂ e definit ca un întreg cuprins între 7 şi 0


type ADRESĂ is range 7 downto 0;

-- Tipul COD este definit prin următorul tip enumerat


type COD is (NOP, ADD, PLUS1);

În continuarea acestei secţiuni, notaţia T desemnează un tip, prefix al


atributului, adică un obiect asupra căruia operează atributul. Trebuie făcută
distincţia dintre acest obiect şi eventualii parametri ai atributului, care apar
între paranteze după numele atributului.

obiect_pe_care_operează_atributul'nume_atribut(parametri)

T'BASE
Rezultatul său este tipul de bază al lui T, unde T poate fi orice tip sau
sub-tip. Acest atribut nu este permis decât în expresia prefixului unui alt
atribut (de exemplu T'BASE'LEFT).

T'HIGH
Rezultatul său este o valoare de tipul T. Prefixul T trebuie să fie de
tip scalar sau să fie un sub-tip scalar. Valoarea returnată este cea mai mare
valoare implementată a tipului T.
88 LIMBAJUL VHDL

-- Vom utiliza tipul INTEGER; variabila INT este iniţializată


-- la cea mai mare valoare întreagă, spre deosebire de
-- iniţializarea implicită, care ar fi egală cu INTEGER'LOW.
variable INT: INTEGER := INTEGER'HIGH;
-- Definirea unui sub-tip MMARE10 cu valori cuprinse între 11
-- şi cel mai mare număr întreg reprezentabil
subtype MMARE10 is INTEGER range 11 to INTEGER'HIGH;

T'LOW
Rezultatul său este o valoare de tipul T. Prefixul T trebuie să fie de
tip scalar sau să fie un sub-tip scalar. Valoarea returnată este cea mai mică
valoare implementată a tipului T.
În cazul tipului ADRESĂ definit la începutul acestei secţiuni,
ADRESĂ'LOW este egal cu 0.

T'LEFT
Rezultatul său este o valoare de tipul T. Prefixul T trebuie să fie de
tip scalar sau să fie un sub-tip scalar. Valoarea returnată corespunde limitei
din stânga a tipului T; aceasta nu trebuie confundată cu cea mai mică
valoare a lui T. T'LEFT constituie valoarea de iniţializare implicită a tuturor
tipurilor scalare.
Pentru tipul INTEGER, INTEGER'LEFT = INTEGER'LOW.
Pentru tipul NIVEL, NIVEL'LEFT = 'U'.
Pentru tipul ADRESĂ, ADRESĂ'LEFT = 7.

T'RIGHT
Rezultatul său este o valoare de tipul T. Prefixul T trebuie să fie de
tip scalar sau să fie un sub-tip scalar. Valoarea returnată corespunde limitei
din dreapta a tipului T; aceasta nu trebuie confundată cu cea mai mare
valoare a lui T.
Pentru tipul INTEGER, INTEGER'RIGHT = INTEGER'HIGH.
Pentru tipul NIVEL, NIVEL'RIGHT = 'Z'.
Pentru tipul ADRESĂ, ADRESĂ'RIGHT = 0.

T'POS (X)
Acest atribut este o funcţie de parametru X; X este de tipul de bază
al lui T. Prefixul T este un tip sau un sub-tip enumerat sau fizic. Valoarea
returnată de către această funcţie este un întreg universal
ATRIBUTE 89
(UNIVERSAL_INTEGER) care dă poziţia valorii parametrului X în cadrul
enumerării. Poziţia primului element este zero.
De exemplu, TIME'POS (1 ns) este de tipul
UNIVERSAL_INTEGER.

T'VAL (X)
Acest atribut este o funcţie de parametru X; X este o expresie de tip
întreg. Prefixul T este un tip sau un sub-tip enumerat sau fizic. Valoarea
returnată de către această funcţie este cea a cărei poziţie este egală cu X.
Poziţia primului element al unei enumerări este zero.
De exemplu, NIVEL'VAL(NIVEL'POS('Z')) are valoarea 'Z'.

T'SUCC (X)
Acest atribut este o funcţie de parametru X; X este de tipul de bază
al lui T. Prefixul T este un tip sau un sub-tip enumerat sau fizic. Valoarea
returnată de către această funcţie este cea a cărei poziţie este egală cu X + 1.

Observaţie
Se va genera o eroare dacă X este egal cu T'BASE'HIGH.

De exemplu, NIVEL'SUCC(NIVEL'LEFT) are valoarea '0'.


COD'SUCC(NOP) are valoarea ADD.
ADRESĂ'SUCC(6) are valoarea 7.

T'PRED (X)
Acest atribut este o funcţie de parametru X; X este de tipul de bază
al lui T. Prefixul T este un tip sau un sub-tip enumerat sau fizic. Valoarea
returnată de către această funcţie este cea a cărei poziţie este egală cu X - 1.

Observaţie
Se va genera o eroare dacă X este egal cu T'BASE'LOW.

De exemplu, NIVEL'PRED('Z') are valoarea '1'.


COD'PRED(PLUS1) are valoarea ADD.
ADRESĂ'PRED(6) are valoarea 5.
90 LIMBAJUL VHDL
T'LEFTOF (X)
Acest atribut este o funcţie de parametru X; X este o expresie al cărei
tip este tipul de bază al lui T. Prefixul T este un tip sau un sub-tip enumerat
sau fizic. Valoarea returnată de către această funcţie este cea a cărei poziţie
se află la stânga celei a lui X.

Observaţie
Se va genera o eroare dacă X este egal cu T'BASE'LEFT.

De exemplu, NIVEL'LEFTOF('Z') are valoarea '1'.


COD'LEFTOF(ADD) are valoarea NOP.
ADRESĂ'LEFTOF(6) are valoarea 7.

T'RIGHTOF (X)
Acest atribut este o funcţie de parametru X; X este o expresie al cărei
tip este tipul de bază al lui T. Prefixul T este un tip sau un sub-tip enumerat
sau fizic. Valoarea returnată de către această funcţie este cea a cărei poziţie
se află la dreapta celei a lui X.

Observaţie
Se va genera o eroare dacă X este egal cu T'BASE'RIGHT.

De exemplu, NIVEL'RIGHTOF('0') are valoarea '1'.


COD'RIGHTOF(ADD) are valoarea PLUS1.
ADRESĂ'RIGHTOF(6) are valoarea 5.

T'ASCENDING
Acest atribut este o funcţie care returnează o valoare booleană TRUE
dacă intervalul tipului este crescător (to) şi FALSE în caz contrar (downto).
Prefixul T este un tip sau un sub-tip enumerat sau fizic.
NIVEL'ASCENDING este TRUE.
COD'ASCENDING este TRUE.
ADRESĂ'ASCENDING este FALSE.

T'IMAGE (X)
Acest atribut este o funcţie care are drept parametru o valoare de tip
scalar X şi care returnează un şir de caractere (de tip STRING) reprezentând
ATRIBUTE 91
valoarea respectivă. Prefixul T este deci un tip sau un sub-tip scalar. Acest
tip este acelaşi cu tipul lui X.
De exemplu, COD'IMAGE(ADD) returnează şirul de caractere
”ADD”. Acest atribut ar putea fi utilizat, de exemplu, în mesajul unei
instrucţiuni assert:

assert CONDIŢIE report "Valoarea este:"& COD'IMAGE(ADD);

T'VALUE (X)
Acest atribut este o funcţie care are drept parametru un şir de
caractere (de tip STRING) şi returnează valoarea de tipul T care îi
corespunde. Dacă nu se recunoaşte nici o valoare a tipului, se generează o
eroare. Prefixul T este un tip sau un sub-tip scalar. X este deci de tipul
STRING. Acest atribut nu este sensibil la majuscule; este complementarul
atributului precedent.
COD'VALUE("ADD") returnează valoarea ADD de tipul COD.
COD'VALUE("adD") returnează valoarea ADD de tipul COD.
COD'VALUE("ALPHA") returnează o eroare.

2.2.2 Atribute definite pe tipuri sau pe sub-tipuri tablou


Vom utiliza în continuare următorul cadru de exemple:

constant NUME: STRING := "MIHĂILESCU";


variable PRENUME: STRING (8 downto 1) := "BOGDAN";
subtype CUVÂNT is BIT_VECTOR (0 to 7);
subtype BIT_COD is BIT_VECTOR (3 downto 1);
type MEMORIE is array (INTEGER range <>) of CUVÂNT;
subtype BLOC is MEMORIE (16 downto 1);
type ECRAN is array(INTEGER range<>, INTEGER range<>) of BIT;
variable MEM: MEMORIE (1 to 256);
variable BL: BLOC;
variable CV: CUVÂNT;
alias COD: BIT_COD is CV (0 to 2);
variable VIZ: ECRAN (255 downto 1, 1 to 1024);

În continuarea acestei secţiuni, notaţia A desemnează un tablou (sau


un sub-tip de tablou), prefix al atributului, adică un obiect asupra căruia
operează atributul. Acest obiect trebuie distins de eventualii parametri ai
atributului, aceştia din urmă apărând între paranteze după numele
atributului.
92 LIMBAJUL VHDL

obiect_pe_care_operează_atributul'nume_atribut(parametri)

A'LEFT[(N)]
Acest atribut este o funcţie care are drept parametru o expresie
statică de tip întreg universal N a cărui valoare nu trebuie să depăşească
numărul de dimensiuni ale tabloului A. N este opţional, având implicit
valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un obiect de sub-
tip constrâns de tablou, sau un alias.
Valoarea returnată de această funcţie este valoarea capătului din
stânga al celui de-al N-lea index în intervalul de variaţie al declaraţiei lui A.
Dacă A este un alias al unui obiect de tip tablou, valoarea returnată va fi
extrasă din declaraţia lui A şi nu din cea a obiectului.
Aşadar, NUME'LEFT este egal cu un întreg universal de valoare
necunoscută, în schimb NUME (NUME'LEFT) este egal cu 'M'.
PRENUME'LEFT este 8.
MEM'LEFT este 1.
VIZ'LEFT (2) este 1.
BL'LEFT este 16.
CV'LEFT este 0.
COD'LEFT este 3.

A'RIGHT[(N)]
Acest atribut este o funcţie care are drept parametru o expresie
statică de tip întreg universal N a cărui valoare nu trebuie să depăşească
numărul de dimensiuni ale tabloului A. N este opţional, având implicit
valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un obiect de sub-
tip constrâns de tablou, sau un alias.
Valoarea returnată de această funcţie este valoarea capătului din
dreapta al celui de-al N-lea index în intervalul de variaţie al declaraţiei lui
A. Dacă A este un alias al unui obiect de tip tablou, valoarea returnată va fi
extrasă din declaraţia lui A şi nu din cea a obiectului.
Aşadar, NUME'RIGHT este egal cu un întreg universal de valoare
necunoscută, în schimb NUME (NUME'RIGHT) este egal cu 'U'.
PRENUME'RIGHT este 1.
MEM'RIGHT este 256.
VIZ'RIGHT (2) este 1024.
BL'RIGHT este 1.
ATRIBUTE 93
CV'RIGHT este 7.
COD'RIGHT este 1.

A'HIGH[(N)]
Acest atribut este o funcţie care are drept parametru o expresie
statică de tip întreg universal N a cărui valoare nu trebuie să depăşească
numărul de dimensiuni ale tabloului A. N este opţional, având implicit
valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un obiect de sub-
tip constrâns de tablou, sau un alias de tablou.
Valoarea returnată de această funcţie este valoarea capătului superior
al celui de-al N-lea index în intervalul de variaţie al declaraţiei lui A. Dacă
A este un alias al unui obiect de tip tablou, valoarea returnată va fi extrasă
din declaraţia lui A şi nu din cea a obiectului.
Aşadar, NUME'HIGH este egal cu un întreg universal de valoare
necunoscută, în schimb NUME (NUME'HIGH) este egal cu 'U', deoarece
tipul STRING este definit cu un index de tip POSITIVE, care este crescător
de la 1 la INTEGER'HIGH.
PRENUME'HIGH este 8.
MEM'HIGH este 256.
VIZ'HIGH (2) este 1024.
BL'HIGH este 16.
CV'HIGH este 7.
COD'HIGH este 3.

A'LOW [(N)]
Acest atribut este o funcţie care are drept parametru o expresie
statică de tip întreg universal N a cărui valoare nu trebuie să depăşească
numărul de dimensiuni ale tabloului A. N este opţional, având implicit
valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un obiect de sub-
tip constrâns de tablou, sau un alias de tablou.
Valoarea returnată de această funcţie este valoarea capătului inferior
al celui de-al N-lea index în intervalul de variaţie al declaraţiei lui A. Dacă
A este un alias al unui obiect de tip tablou, valoarea returnată va fi extrasă
din declaraţia lui A şi nu din cea a obiectului.
Aşadar, NUME'LOW este egal cu un întreg universal de valoare
necunoscută, în schimb NUME (NUME'LOW) este egal cu 'M', deoarece
tipul STRING este definit cu un index de tip POSITIVE, care este crescător
de la 1 la INTEGER'HIGH.
94 LIMBAJUL VHDL
PRENUME'LOW este 1.
MEM'LOW este 1.
VIZ'LOW (2) este 1.
BL'LOW este 1.
CV'LOW este 0.
COD'LOW este 1.

A'RANGE [(N)]
Acest atribut este un interval de variaţie care are drept parametru o
expresie statică de tip întreg universal N a cărui valoare nu trebuie să
depăşească numărul de dimensiuni ale tabloului A. N este opţional, având
implicit valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un alias
de tablou, sau un obiect de sub-tip constrâns de tip tablou.
Valoarea returnată de această funcţie este intervalul de variaţie al
celui de-al N-lea index din declaraţia lui A. Dacă A este un alias al unui
obiect de tip tablou, valoarea returnată va fi extrasă din declaraţia lui A şi nu
din cea a obiectului.
Dacă cel de-al N-lea index este crescător, atunci A'RANGE(N) va
avea ca valoare intervalul de variaţie A'LEFT(N) to A'RIGHT(N). Dacă cel
de-al N-lea index este descrescător, atunci A'RANGE(N) va avea ca valoare
intervalul de variaţie A'RIGHT(N) downto A'LEFT(N).
Aşadar, NUME'RANGE este un interval de variaţie crescător căruia
nu i se cunosc decât valorile inferioară şi superioară. Totuşi, se poate scrie:

for i in NUME'RANGE loop


-- Se vor baleia indicii corespunzători lui 'M', apoi lui
-- 'I',... până la 'U'

PRENUME'RANGE este 8 downto 1.


MEM'RANGE este 1 to 256.
VIZ'RANGE (2) este 1 to 1024.
BL'RANGE este 16 downto 1.
CV'RANGE este 0 to 7.
COD'RANGE este 3 downto 1.

A'REVERSE_RANGE [(N)]
Acest atribut este un interval de variaţie care are drept parametru o
expresie statică de tip întreg universal N a cărui valoare nu trebuie să
ATRIBUTE 95
depăşească numărul de dimensiuni ale tabloului A. N este opţional, având
implicit valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un alias
de tablou, sau un obiect de sub-tip constrâns de tip tablou.
Valoarea returnată de această funcţie este intervalul de variaţie al
celui de-al N-lea index din declaraţia lui A, luat însă în ordine inversă. Dacă
A este un alias al unui obiect de tip tablou, valoarea returnată va fi extrasă
din declaraţia lui A şi nu din cea a obiectului.
Dacă cel de-al N-lea index este crescător, atunci
A'REVERSE_RANGE(N) va avea ca valoare intervalul de variaţie
A'RIGHT(N) downto A'LEFT(N). Dacă cel de-al N-lea index este
descrescător, atunci A'REVERSE_RANGE(N) va avea ca valoare intervalul
de variaţie A'LEFT(N) to A'RIGHT(N).
Aşadar, NUME'REVERSE_RANGE este un interval de variaţie
descrescător căruia nu i se cunosc decât valorile superioară şi inferioară.
Totuşi, se poate scrie:

for i in NUME'REVERSE_RANGE loop


-- Se vor baleia indicii corespunzători lui 'U', apoi lui
-- 'C',... până la 'M'

PRENUME'REVERSE_RANGE este 1 to 8.
MEM'REVERSE_RANGE este 256 downto 1.
VIZ'REVERSE_RANGE (2) este 1024 downto 1.
BL'REVERSE_RANGE este 1 to 16.
CV'REVERSE_RANGE este 7 downto 0.
COD'REVERSE_RANGE este 1 to 3.

A'LENGTH[(N)]
Acest atribut returnează o valoare de tip întreg universal care
corespunde numărului de valori din cel de-al N-lea index al declaraţiei lui
A. N este o expresie statică de tip întreg universal a cărei valoare nu trebuie
să depăşească numărul de dimensiuni ale tabloului A. N este opţional, având
implicit valoarea 1. Prefixul A poate fi un obiect de tip tablou, sau un alias
de tablou sau un obiect de sub-tip constrâns de tip tablou. Dacă A este un
alias al unui obiect de tip tablou, valoarea returnată va fi extrasă din
declaraţia lui A şi nu din cea a obiectului.
De fapt, A'LENGTH(N) este egal cu A'HIGH(N) - A'LOW(N) + 1.
NUME'LENGTH este 10.
96 LIMBAJUL VHDL
PRENUME'LENGTH este 8.
MEM'LENGTH este 256.
VIZ'LENGTH (2) este 1024.
BL'LENGTH este 16.
CV'LENGTH este 8.
COD'LENGTH este 3.

A'ASCENDING[(N)]
Acest atribut returnează o valoare booleană, care este TRUE dacă cel
de-al N-lea index al tabloului A este crescător (to) şi FALSE în caz contrar
(downto). N este o expresie statică de tip întreg universal a cărei valoare nu
trebuie să depăşească numărul de dimensiuni ale tabloului A. N este
opţional, având implicit valoarea 1. Prefixul A poate fi un obiect de tip
tablou, sau un alias de tablou, sau un obiect de sub-tip constrâns de tip
tablou. Dacă A este un alias al unui obiect de tip tablou, valoarea returnată
va fi extrasă din declaraţia lui A şi nu din cea a obiectului.
NUME'ASCENDING este TRUE.
PRENUME'ASCENDING este FALSE.
MEM'ASCENDING este TRUE.
VIZ'ASCENDING (2) este TRUE.
BL'ASCENDING este FALSE.
CV'ASCENDING este TRUE.
COD'ASCENDING este TRUE.

2.2.3 Atribute definite pe semnale


Atributele definite pe semnale pot fi grupate în două categorii care
diferă prin tipul rezultatului: semnal sau funcţie.
În continuarea acestei secţiuni, notaţia S desemnează un semnal,
prefix al atributului, adică un obiect asupra căruia operează atributul. Acest
obiect trebuie distins de eventualii parametri ai atributului, aceştia din urmă
apărând între paranteze după numele atributului.

a) Atribute semnal

S’DELAYED [(T)]
Acest atribut este un semnal identic cu S, dar întârziat cu T unităţi de
timp. T este deci o expresie statică pozitivă sau nulă de tipul fizic predefinit
TIME. Ea poate fi omisă, caz în care va lua valoarea 0 ns. Atenţie,
ATRIBUTE 97
S’DELAYED (0 ns) nu este identic cu S atunci când S tocmai şi-a schimbat
valoarea în acelaşi pas al simulării.
Fiind dat T un timp pozitiv sau nul, iar R – un semnal de acelaşi sub-
tip ca şi cel al semnalului S, dacă R şi S au aceleaşi valori iniţiale, atunci R
este identic cu S’DELAYED(T) oricare ar fi T.
Procesul echivalent ar putea fi următorul:

process(S)
begin
R <= transport S after T;
end process;

S’STABLE [(T)]
Acest atribut este un semnal de tip boolean. S este un semnal static.
T este o expresie statică pozitivă sau nulă de tipul fizic predefinit TIME. Ea
poate fi omisă, caz în care va lua valoarea 0 ns. Rezultatul este adevărat
dacă în decursul intervalului de timp T nu a apărut nici un eveniment pe
semnalul S şi fals în caz contrar.

Observaţii
S’STABLE(0 ns) este fals dacă S tocmai şi-a schimbat valoarea.
Aceasta se reduce la a afirma că dacă semnalul S întârziat cu 0 ns este egal
cu semnalul S, atunci S este stabil de 0 ns, ceea ce se scrie:

S’STABLE(0 ns) = (S’DELAYED(0 ns) = S)

Dacă S’STABLE(T) are valoarea FALSE, atunci înseamnă că, prin


definiţie, există un timp t, cu 0 ns ≤ t ≤ T, astfel încât S’DELAYED(t) să fie
diferit de S.
Dacă T este cea mai mică valoare de timp astfel încât S’STABLE(T)
are valoarea FALSE, atunci pentru toţi t (0 ns ≤ t ≤ T), S’DELAYED(t) = S.

S’QUIET [(T)]
Acest atribut este un semnal de tip boolean. S este un semnal static.
T este o expresie statică pozitivă sau nulă de tipul fizic predefinit TIME. Ea
poate fi omisă, caz în care va lua valoarea 0 ns. Rezultatul este adevărat
dacă în decursul intervalului de timp T semnalul S „a rămas liniştit” (pe el
nu a apărut nici un eveniment şi nici o tranzacţie) şi este fals în caz contrar.
98 LIMBAJUL VHDL
Termenul „liniştit” a fost introdus aici pentru a traduce cuvântul QUIET; de
fapt, în contextul limbajului VHDL, el are o semnificaţie opusă termenului
ACTIV.
Pentru un ciclu de simulare dat, S’QUIET(0 ns) are valoarea TRUE
dacă şi numai dacă semnalul S este „liniştit” în decursul acestui ciclu de
simulare, adică dacă semnalul S nu se află pe lista semnalelor ai căror piloţi
(driver-e) sunt luate în considerare pentru ciclul de simulare curent.

S’TRANSACTION
Acest atribut este un semnal de tip predefinit BIT. Dacă S este
numele unui semnal static, acest atribut îşi schimbă valoarea (din ‘0’ devine
‘1’ şi invers) de fiecare dată când semnalul S devine activ în decursul unui
ciclu de simulare.

b) Atribute funcţie

S’EVENT
Acest atribut este o funcţie de tip boolean. Prefixul S identifică un
semnal static. Valoarea returnată de această funcţie este TRUE atunci când
tocmai a avut loc un eveniment pe semnalul S în decursul ciclului de
simulare.
Un eveniment este caracterizat de o schimbare a valorii unui semnal
activ. Pentru un semnal de tip compus, este suficient ca un eveniment să
aibă loc pe unul dintre sub-elementele sale pentru ca această funcţie atribut
să returneze valoarea TRUE.

S’ACTIVE
Acest atribut este o funcţie de tip boolean. S este numele unui
semnal static. Valoarea returnată de această funcţie este TRUE atunci când
semnalul S este activ în decursul ciclului de simulare şi FALSE în caz
contrar. Dacă semnalul este de tip compus, este suficient ca unul dintre sub-
elementele sale scalare să fie activ.

S’LAST_EVENT
Acest atribut este o funcţie care returnează o valoare de tipul
predefinit TIME. Prefixul S este numele unui semnal static. Valoarea
returnată de această funcţie reprezintă timpul scurs de la ultimul eveniment
survenit pe semnalul S.
ATRIBUTE 99
Pentru un semnal S compus, S’LAST_EVENT returnează valoarea
minimă a lui R’LAST_EVENT a fiecărui sub-element R al lui S.

S’LAST_ACTIVE
Acest atribut este o funcţie care returnează o valoare de tipul
predefinit TIME. Prefixul S este numele unui semnal static. Valoarea
returnată de această funcţie reprezintă timpul scurs din momentul în care
semnalul S a fost activ pentru ultima dată.
Pentru un semnal S compus, S’LAST_ACTIVE returnează valoarea
minimă a lui R’LAST_ ACTIVE a fiecărui sub-element R al lui S.

S’LAST_VALUE
Acest atribut este o funcţie care returnează o valoare de tipul
semnalului S. Prefixul S este numele unui semnal static. Valoarea returnată
de această funcţie este cea a semnalului S imediat înaintea ultimei
modificări a lui S.
Pentru un semnal S de tip scalar, S’LAST_VALUE este egal cu
S’DELAYED(T), unde T este de tip TIME, este ≥ 0 ns şi ia valoarea cea
mai mică astfel încât S’STABLE(T) să fie FALSE. Dacă o asemenea
valoare de timp nu există, S’LAST_VALUE returnează valoarea semnalului
S.

S’DRIVING_VALUE şi S’DRIVING
Aceste două atribute sunt funcţii şi sunt oarecum diferite de cele
precedente datorită modului lor de utilizare. Ele au fost introduse pentru a se
evita necesitatea de a citi valoarea unui port de ieşire (a unui port de mod
out).

Figura 5.1 Exemplu ilustrativ pentru atributele S’DRIVING_VALUE şi


S’DRIVING
100 LIMBAJUL VHDL
Examinând schema precedentă, am înclina să o descriem astfel:

S <= A and B;
NS<= not S;

Din păcate, această descriere este ilegală căci portul de ieşire S nu


poate fi citit şi deci nu poate apărea în membrul drept al unei atribuiri.
Această restricţie asupra utilizării în limbajul VHDL a unui port de
ieşire poate părea incomodă. De fapt, demersul proiectanţilor VHDL-ului se
explică foarte bine. A avea acces la valoarea S semnifică, dacă S este un
semnal rezolvat, a avea acces la valoarea rezolvată, adică la valoarea
obţinută după apelarea funcţiei de rezoluţie. Or, această valoare rezolvată
poate să depindă la rândul ei de alte semnale sursă, exterioare blocului
respectiv. A utiliza această valoare în interiorul descrierii noastre ar fi
constituit un mijloc (e drept, indirect) de a face ca informaţia să intre pe un
port de ieşire (în celelalte blocuri care se execută concurent cu blocul
nostru).
Confruntat cu această problemă, proiectantul nu ar fi avut altă soluţie
decât:
a) Să schimbe natura portului de ieşire în port bidirecţional (ceea ce
ar fi avut urmări profund nocive asupra logicii descrierii: un port de ieşire
nu este în nici un caz acelaşi lucru cu un port bidirecţional!)
b) Să declare o variabilă locală (într-un context secvenţial) sau un
semnal local (într-un context concurent) care să joace rolul de tampon. Şi în
acest caz, această declaraţie inutilă nu este acceptabilă din punct de vedere
conceptual.
Atributul DRIVING_VALUE permite rezolvarea acestei probleme.
Într-adevăr, spre deosebire de S, care desemnează în cazul precedent
semnalul, eventual rezolvat, prezent la ieşire, S'DRIVING_VALUE
desemnează exclusiv valoarea cu care procesul contribuie la eventuala
funcţie de rezoluţie. S'DRIVING_VALUE poate deci să fie subiectul unei
instrucţiuni de atribuire. Astfel, exemplul anterior se va scrie de data
aceasta:

S <= A and B;
NS<= not S'DRIVING_VALUE;
ATRIBUTE 101
Atributul S'DRIVING este mai puţin folosit. Într-adevăr, în cazul în
care, în mod dinamic, nu se ştie dacă semnalul S este deconectat sau nu,
următoarea porţiune de cod permite evitarea unei erori:

if S'DRIVING then
V := S'DRIVING_VALUE;
end if;

S'DRIVING este un atribut care returnează o valoare booleană cu


valoarea TRUE dacă semnalul S nu este deconectat.

5.2.2.4 Atribute definite pe obiecte în sens larg


Următoarele trei atribute au particularitatea de a se putea aplica pe
majoritatea obiectelor, în sens larg, din VHDL. Astfel, prefixul X poate
desemna o entitate, o variabilă, o etichetă etc.
Nici unul dintre aceste atribute nu are parametri şi toate returnează o
valoare de tip STRING (şir de caractere) scris cu litere mici. Principala
întrebuinţare a acestor atribute constă în elaborarea de mesaje (sau report)
care permit trasarea unui cod sursă fără utilizarea instrumentelor software de
depanare (debugging).

X'SIMPLE_NAME
Acest atribut returnează, sub forma unui şir de caractere, numele X.
Astfel, dacă SIG este un semnal, SIG'SIMPLE_NAME va returna şirul de
caractere "sig".

X'PATH_NAME
Acest atribut returnează un şir de caractere care, pe lângă numele X,
conţine şi suita de etichete care permite revenirea, de-a lungul structurii
descrierii date de către proiectant, până la X: etichete de blocuri, de instanţe
de componente etc.

X'INSTANCE_NAME
Acest atribut amestecă informaţiile structurale oferite de
PATH_NAME cu alte informaţi relative la configuraţie (numele entităţii,
numele arhitecturii, numele configuraţiei etc.)
102 LIMBAJUL VHDL
2.3 Atribute definite de către proiectant

Modul de utilizare a acestor atribute poate fi rezumat astfel:


1. Un atribut care nu este predefinit trebuie în primul rând să fie
declarat, fapt care permite stabilirea tipului atributului. Sintaxa este
următoarea:

attribute nume_atribut: tip_atribut;

Iată câteva exemple:

type IMPLEMENTARE is
record
NUMĂR_FIR: INTEGER;
NUMĂR_PLACĂ: INTEGER;
end record;
attribute LOCALIZARE: IMPLEMENTARE;
attribute NUMĂR_MULŢIME: INTEGER;
type CAPACITY is range 0 to 1E15
units
ff; -- femtofarad
pf = 1000 ff; -- picofarad
nf = 1000 pf; -- nanofarad
uf = 1000 nf; -- microfarad
mf = 1000 uf; -- milifarad
end units;
attribute CAPACITATE: CAPACITY;

2. Atributul trebuie să fie specificat, ceea ce-i conferă o valoare.


Aceasta va permite specificarea elementelor la care se referă atributul şi,
pentru fiecare dintre aceste elemente, valoarea atributului. O specificaţie de
atribut nu se referă decât la elementele declarate în aceeaşi parte declarativă
ca şi specificaţia în sine.
Un atribut care nu este predefinit nu poate fi decât o constantă, cu
alte cuvinte, el este static. Sintaxa este următoarea:

attribute nume_atribut of obiect_la_care_se_referă is


expresie;
ATRIBUTE 103
Un atribut definit de către proiectant se poate raporta la următoarele
elemente VHDL: entităţi, arhitecturi, configuraţii, proceduri, funcţii,
pachete, tipuri, sub-tipuri, constante, semnale, variabile, componente şi
etichete. Pentru a le desemna, se vor folosi, respectiv, următoarele cuvinte
cheie: entity, architecture, configuration, procedure, function, package,
type, subtype, constant, signal, variable, component şi label.
Un atribut se raportează la anumite elemente, dar el se mai poate
raporta şi la toată clasa de elemente (toate entităţile) sau numai la unele
dintre ele, în mod obligatoriu numite. Această informaţie va fi dată în cadrul
câmpului desemnat mai sus drept „obiect_la_care_se_referă”. Acest
câmp are următoarea sintaxă:

nume_element {, nume_element}: clasă_element

sau, dacă dorim să precizăm valoarea atributului pentru toate celelalte


elemente ale unei clase:

others: clasă_element

sau, dacă dorim, la modul global, să asociem o valoare a atributului pentru o


întreagă clasă de elemente:

all: clasă_element

Expresia dată în sintaxa atributului trebuie, în mod evident, să


returneze o valoare compatibilă cu tipul atributului definit în declaraţia
atributului. Reluând ultimele exemple, iată câteva specificaţii posibile:

attribute NUMĂR_MULŢIME of DATA1: signal is 10;


attribute NUMĂR_MULŢIME of DATA2: signal is 11;
attribute NUMĂR_MULŢIME of others: signal is 0;
attribute CAPACITATE of all: signal is 10 pf;

Semnalele DATA1 şi DATA2, în mod obligatoriu declarate în


aceeaşi zonă declarativă ca şi această specificaţie, vor avea asignate pentru
atributele lor NUMĂR_MULŢIME valorile întregi 10 şi respectiv 11. Orice
alt semnal declarat în aceeaşi zonă declarativă ca şi această specificaţie va
avea valoarea întreagă 0 ca atribut NUMĂR_MULŢIME.
104 LIMBAJUL VHDL
Orice semnal declarat în aceeaşi zonă declarativă ca şi această
specificaţie va avea un atribut CAPACITATE de valoare 10 pf .
3. Atributul este folosit cu ajutorul notaţiei introduse de către
apostrof după obiectul (în sensul larg) la care se referă atributul. Sintaxa este
următoarea:

obiect_la_care_se_referă'nume_atribut

Expresia DATA1'NUMĂR_MULŢIME va avea deci valoarea


întreagă 10. Expresia DATA2'CAPACITATE va avea deci valoarea 10 pf.

3. Desfăşurarea lucrării
3.1 Se vor testa toate atributele prezentate pe cadrul de exemple din
lucrare. Se vor utiliza atributele predefinite pentru descrierea
comportamentului unui bistabil JK Master-Slave.
3.2 Se va defini atributul NUMĂR_APARIŢII care să acţioneze
asupra unui obiect de tip TIME.
3.3 Se va testa următorul exemplu de utilizare a atributului
LAST_EVENT:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity BISTABIL_D is
generic(SETUP_TIME: TIME:= 1 ns; HOLD_TIME: TIME:= 2 ns);
port(D, CLK: in STD_LOGIC;
Q: out STD_LOGIC);
begin -- Verificarea respectării timpului de prepoziţionare (set-up)
VERIF_SETUP: process(CLK)
begin
if (CLK = ‘1’) and (CLK’EVENT) then
assert (D’LAST_EVENT >= SETUP_TIME)
report “Încălcarea condiţiei de timp de set-up” severity ERROR;
end if;
end process VERIF_SETUP;
end BISTABIL_D;

architecture COMPORTAMENTALĂ of BISTABIL_D is


begin
process(CLK)
begin
if (CLK = ‘1’) and (CLK’EVENT) then Q <= D after 10 ns;
end if;
end process;
end COMPORTAMENTALĂ;
LUCRAREA NR. 6
DOMENIUL SECVENŢIAL. PROCESE

1. Scopul lucrării

Lucrarea oferă o perspectivă detaliată asupra noţiunilor fundamentale


referitoare la domeniul comportamental al descrierilor VHDL. Se prezintă
noţiunea de proces, modul de execuţie, de suspendare şi de reactivare al unui
proces, problemele legate de instrucţiunea wait şi structurile echivalente ei,
precum şi lista de sensibilitate a unui proces. Se studiază rolul şi importanţa
variabilelor în cadrul proceselor, cu exemple concludente şi bogat
comentate.

2. Consideraţii teoretice

2.1 Procese

Domeniul secvenţial este strâns legat de stilul de descriere


comportamental. Specificaţia comportamentală constă de fapt într-o listă de
operaţii care trebuie efectuate pentru a se obţine rezultatele dorite. Procesul
constituie o modalitate formală de a realiza o listă de operaţii secvenţiale în
VHDL. Un proces are un format foarte bine structurat, chiar dacă el
reprezintă comportamentul unei părţi minore a proiectului.
Procesul este obiectul fundamental manipulat de către simulator,
pentru care orice descriere VHDL se rezumă la un set de procese
caracterizate de semnalele la care sunt sensibile şi de operaţiile secvenţiale
executate de către fiecare dintre ele.
Specificarea unui proces se va face întotdeauna utilizând cuvântul
cheie process, după care urmează o zonă de declaraţii locale procesului şi
apoi cuvântul cheie begin (care arată momentul de început al secvenţei de
instrucţiuni care se va executa), iar în final, cuvintele cheie end process
indică terminarea procesului. Sintaxa completă a unui proces este prezentată
în continuare.
106 LIMBAJUL VHDL
În partea declarativă a procesului nu se admite declararea oricăror
obiecte. Cel mai important aspect este acela că în această zonă este interzisă
declararea semnalelor; în schimb, este permisă declararea variabilelor şi a
sub-programelor interne.

{etichetă:} process {listă_de_sensibilitate}


...
... Zona de declaraţii locale procesului
...
begin
...
... Instrucţiuni secvenţiale
...
end process {etichetă};

Imediat înaintea cuvântului cheie process, se permite (opţional)


cuvântul cheie postponed, pentru a arăta caracterul „amânat” al procesului.
Acelaşi cuvânt cheie postponed poate să apară şi la sfârşitul procesului, în
instrucţiunea end process (aceasta, în scopuri de documentare).

{etichetă:} {postponed} process {listă_de_sensibilitate}


...
... Zona de declaraţii locale procesului
...
begin
...
... Instrucţiuni secvenţiale
...
end {postponed} process {etichetă};

De vreme ce construcţiile VHDL reprezintă nişte sisteme reale,


conceptul de terminare a procesului este diferit de cel din limbajele de
programare clasice. În VHDL procesul este conceput astfel încât după
executarea ultimei instrucţiuni el va relua imediat execuţia primei
instrucţiuni din lista de instrucţiuni secvenţiale. Prin urmare, un proces nu se
termină niciodată. Figura 6.1 ilustrează aceste noţiuni într-o formă grafică.
DOMENIUL SECVENŢIAL. PROCESE 107
VHDL Limbaj de programare clasic
(ex. Pascal)
process (semnale)
begin begin {programul principal}
Instrucţiunea_1; Instrucţiunea_1;
Instrucţiunea_2; Instrucţiunea_2;
Instrucţiunea_3; Instrucţiunea_3;
end process; end.

Clauza END provoacă încheierea execuţiei?

Instrucţiunea_1 Instrucţiunea_1
NU DA

În VHDL toate
instrucţiunile sunt
Instrucţiunea_2 executate secvenţial Instrucţiunea_2
într-o buclă infinită

Instrucţiunea_3 Instrucţiunea_3

Figura 6.1 Execuţia unui proces în VHDL

Standardul VHDL afirmă că:


- Un proces nu poate conţine decât instrucţiuni secvenţiale;
- Orice instrucţiune concurentă poate întotdeauna să fie tradusă în
termenii unui proces (este „procesul său echivalent”);
- Un proces "trăieşte" nedefinit (este „global”); durata sa de viaţă
este cea a simulării. El nu se poate termina. Într-adevăr, o dată
ajuns în punctul terminal (după executarea ultimei instrucţiuni,
end process) el se execută din nou de la început (de la cuvântul
cheie begin). Procesul este întotdeauna ciclic (iterativ). Dacă
procesul posedă o instrucţiune wait fără nici o condiţie, el se va
executa o singură dată şi apoi se va suspenda definitiv. Dacă
procesul nu are nici o instrucţiune wait, atunci el se va executa în
buclă infinită.
108 LIMBAJUL VHDL
Vom vedea în continuare că un proces poate fi suspendat şi apoi
reactivat - astfel, comportarea lui devine mai apropiată de cea a sistemelor
din lumea reală.

2.2 Suspendarea şi reactivarea proceselor

În mod obişnuit, dispozitivele electronice operează în buclă infinită.


După activarea lor, ele efectuează operaţiile specifice pentru care au fost
proiectate, după care revin la o stare de aşteptare a îndeplinirii condiţiilor
de activare. După încheierea operaţiilor curente, dispozitivele îşi suspendă
funcţionarea şi îşi reiau activitatea atunci când condiţiile specificate sunt din
nou îndeplinite.

process Instrucţiunea_1
begin
Instrucţiunea_1;
Instrucţiunea_2;
Instrucţiunea_3;
Instrucţiunea_2
wait <condiţie>;
end process;
Dispozitivul reia
operaţiile
Instrucţiunea_3

Dispozitivul suspendă
operaţiile

Condiţie DA
îndeplinită

NU

Figura 6.2 Modul de operare al instrucţiunii wait

Acest mod de funcţionare a dispozitivelor este descris în VHDL de


către instrucţiunea wait, care suspendă procesul atunci când toate operaţiile
prevăzute au fost efectuate şi apoi îl reactivează când condiţiile specificate
DOMENIUL SECVENŢIAL. PROCESE 109
sunt din nou îndeplinite. Reactivarea unui proces poate fi realizată prin
intermediul unei singure instrucţiuni de forma „aşteaptă (wait) îndeplinirea
condiţiilor specifice”.

2.3 Instrucţiunea wait

Înainte ca un proces suspendat să poată fi reactivat, el trebuie să


aştepte îndeplinirea condiţiei specifice. În această direcţie, instrucţiunea
wait acţionează astfel:
a) Stopează necondiţionat (suspendă) execuţia procesului;
b) Evaluează condiţiile care vor reactiva procesul.
Dacă procesul conţine o instrucţiune wait, el va executa toate
instrucţiunile, în ordine, până la întâlnirea instrucţiunii wait. Apoi, procesul
va fi suspendat şi se va aştepta îndeplinirea condiţiei specificate în cadrul
instrucţiunii wait. În momentul în care condiţiile respective sunt îndeplinite,
procesul este reactivat şi îşi execută toate instrucţiunile până când întâlneşte
din nou instrucţiunea wait.
De vreme ce în mediul industrial real există o mare varietate de
condiţii care pot declanşa reactivarea unui proces, limbajul VHDL oferă trei
tipuri de instrucţiuni wait:
- wait for expresie_de_tip_TIME. În cadrul acestei instrucţiuni, se
aşteaptă scurgerea unui anumit interval de timp până la
reactivarea procesului;
- wait until condiţie_de_tip_BOOLEAN. În cadrul acestei
instrucţiuni se aşteaptă până la îndeplinirea unei anumite condiţii
de tip BOOLEAN (până când condiţia respectivă devine
adevărată, TRUE);
- wait on listă_de_sensibilitate. În cadrul acestei instrucţiuni se
aşteaptă până când unul dintre semnalele din cadrul listei de
sensibilitate suferă o modificare a valorii sale.
Prin combinarea acestor condiţii se poate forma o a patra construcţie
numită condiţie complexă.
Sintaxa completă a instrucţiunii wait este prezentată mai jos:

wait {on listă_de_semnale}{until condiţie_booleană} {for timp};


110 LIMBAJUL VHDL

Instrucţiunea_1

Instrucţiunea_2

Procesul este suspendat Instrucţiunea_4


process
begin
Instrucţiunea_1;
Instrucţiunea_2; NU
wait <condiţie1>; Procesul este reactivat
Condiţie1
Instrucţiunea_3; îndeplinită
wait <condiţie2>;
Instrucţiunea_4; DA
end process; DA
Condiţie2
Procesul este reactivat îndeplinită
NU

Instrucţiunea_3 Procesul este suspendat

Figura 6.3 Execuţia unui proces care conţine mai multe instrucţiuni wait

Observaţie
Instrucţiunea wait until condiţie suspendă procesul până când
respectiva condiţie DEVINE adevărată datorită unei MODIFICĂRI a
oricărui semnal dintre cele care participă la formarea condiţiei. Trebuie
subliniată diferenţa dintre termenii „devine” şi „se modifică”: dacă un
semnal rămâne neschimbat, instrucţiunea wait until nu va reactiva procesul,
chiar dacă se îndeplineşte condiţia!

Toate cele patru cazuri sunt ilustrate în exemplele de mai jos:

signal SIG1, SIG2: BIT;


...
proc1: process
begin
...
wait on SIG1, SIG2;
end process proc1;
DOMENIUL SECVENŢIAL. PROCESE 111
După executarea tuturor instrucţiunilor, procesul va fi suspendat în
momentul în care se ajunge la instrucţiunea wait şi va fi reactivat atunci
când unul dintre semnalele SIG1 sau SIG2 îşi va schimba valoarea.

wait until ENABLE = '1';

În acest exemplu instrucţiunea wait va reactiva procesul atunci când


semnalul ENABLE îşi va modifica valoarea la '1'.

wait for 50 ns;

În acest exemplu, procesul va fi suspendat pe o durată de 50 de


nanosecunde.

proc2: process
begin
wait on SIG1, SIG2 until CLOCK = '1';
...
end process proc2;

Procesul proc2 este reactivat după o modificare a semnalului SIG1


sau după o modificare a semnalului SIG2, însă numai atunci când valoarea
semnalului CLOCK va fi egală cu '1'.

2.4 Localizarea instrucţiunii wait

După executarea ultimei instrucţiuni a procesului (care este


localizată imediat înaintea instrucţiunii end process), urmează să fie
executată prima instrucţiune (localizată imediat după instrucţiunea begin).
La prima vedere, acest mod de funcţionare poate sugera ideea că nu
contează unde este localizată instrucţiunea wait: la începutul sau la sfârşitul
procesului. În realitate, de cele mai multe ori această presupunere nu este
adevărată. De exemplu, dacă simulatorul întâlneşte o instrucţiune wait chiar
la începutul procesului, atunci procesul va fi suspendat imediat fără ca vreo
instrucţiune să fie executată. Pe de altă parte, dacă instrucţiunea wait este
localizată undeva pe la mijlocul secvenţei de instrucţiuni a procesului, vor
112 LIMBAJUL VHDL
exista câteva instrucţiuni care vor fi executate înainte de suspendarea
acestuia.
Instrucţiunea wait poate apărea oriunde în cadrul unui proces. De
regulă, ea este plasată la începutul sau la sfârşitul secvenţei de instrucţiuni.
În plus, este permisă apariţia mai multor instrucţiuni wait în cadrul unui
proces.

process
begin
wait on SIGA;
Instrucţiunea1;
Instrucţiunea2;
Instrucţiunea3;
end process;

În acest caz, dacă instrucţiunea wait apare ca prima instrucţiune din


proces, atunci procesul va fi suspendat imediat după lansarea lui în execuţie.

process
begin
Instrucţiunea1;
Instrucţiunea2;
Instrucţiunea3;
wait on SIGB;
end process;

În acest caz, dacă instrucţiunea wait apare ca ultima instrucţiune din


proces, atunci toate instrucţiunile vor fi executate o dată înainte de
suspendarea procesului.

2.5 Lista de sensibilitate a unui proces

Instrucţiunea wait on listă_de_sensibilitate este probabil cea mai des


folosită condiţie pentru reactivarea proceselor; prin urmare, VHDL oferă o
construcţie numită lista de sensibilitate a unui proces. Această listă va fi
specificată alături de cuvântul cheie process şi este întru totul identică cu
lista de sensibilitate din cadrul instrucţiunii wait on listă_de_sensibilitate
care apare la sfârşitul procesului.
DOMENIUL SECVENŢIAL. PROCESE 113

Observaţii
Datorită regulilor de execuţie a proceselor (descrise anterior), o
instrucţiune wait on plasată la începutul procesului NU este echivalentă cu
un proces cu listă de sensibilitate, chiar dacă semnalele din lista de
sensibilitate a procesului sunt aceleaşi cu cele care ar apărea în lista de
sensibilitate a instrucţiunii wait on.
Un proces înzestrat cu listă de sensibilitate nu poate să conţină nici o
altă instrucţiune wait explicită.

În exemplul de mai jos, procesul BISTABIL_D este sensibil la


semnalele RST şi CLK, ceea ce înseamnă că orice eveniment survenit pe
oricare dintre aceste două semnale va provoca reactivarea procesului. Cele
două procese de mai jos sunt echivalente:

-- Primul proces, cu listă de sensibilitate


BISTABIL_D: process (CLK, RST)
begin
if RST = '1'
then Q <= '0';
elsif (CLK'EVENT) and (CLK = '1')
then Q <= D;
end if;
end process BISTABIL_D;
-- Procesul echivalent, exprimat fără listă de sensibilitate
BIST_D: process
begin
if RST = '1'
then Q <= '0';
elsif (CLK'EVENT) and (CLK = '1')
then Q <= D;
end if;
wait on CLK, RST;
end process BIST_D;

În general, cele mai multe procese descrise în VHDL posedă listă de


sensibilitate.
La demararea simulării, procesul este executat o singură dată,
deoarece o listă de sensibilitate este echivalentă cu o instrucţiune wait
114 LIMBAJUL VHDL
localizată la sfârşitul procesului, iar instrucţiunea wait opreşte execuţia
procesului.
În continuare, procesul este suspendat până când oricare dintre
semnalele din lista de sensibilitate îşi modifică valoarea. O astfel de
modificare va declanşa reactivarea procesului, deci toate instrucţiunile din
cadrul procesului, de la început până la sfârşit, vor fi executate în ordinea
specificată.

Observaţie
„Toate instrucţiunile” înseamnă într-adevăr toate instrucţiunile din
cadrul procesului, nu numai cele legate de semnalul care a reactivat
procesul!

După executarea ultimei instrucţiuni, procesul va fi din nou


suspendat.
Există o serie de restricţii care trebuie cunoscute:
- Semnalele care fac parte din lista de sensibilitate a procesului
trebuie să fie statice. Un semnal este static dacă numele său este
cunoscut la compilarea unităţii de proiectare în care se găseşte.
De exemplu, semnalul TAB(I), unde TAB este un tablou şi I este
valoarea returnată de o funcţie sau un parametru de procedură, nu
este static;
- Instrucţiunea wait nu poate fi utilizată în interiorul unui proces
decât dacă acesta nu are listă de sensibilitate după cuvântul cheie
process. De asemenea, instrucţiunea wait nu poate fi utilizată în
procedurile eventual apelate de procesul respectiv (aceasta, tot în
cazul în care lista de sensibilitate nu este vidă). Această restricţie
este riguros verificată de către analizoare. Într-adevăr, o funcţie
trebuie să returneze imediat un rezultat (în sensul simulării).
Orice altă variantă ar deschide calea unor efecte laterale deosebit
de greu de controlat.
În practică se foloseşte deseori scrierea fără listă de sensibilitate,
căci în acest mod se obţine un câştig în privinţa generalităţii.
DOMENIUL SECVENŢIAL. PROCESE 115

2.6 Procese pasive

În partea de specificare a unei entităţi nu sunt admise decât anumite


instrucţiuni concurente. De fapt, nu sunt autorizate decât acele instrucţiuni al
căror proces este pasiv.
Să ne imaginăm că avem un fişier sursă VHDL în care există o
mulţime de procese. Să presupunem că la un moment dat sosesc stimuli de
intrare. Aceste semnale se află pe lista de sensibilitate a anumitor procese,
care vor fi declanşate. Procesele sunt activate; execuţia lor modifică
eventual anumite alte semnale (cele care se află în membrul stâng al
instrucţiunilor de asignare). Aceste semnale modificate se află la rândul lor
în lista de sensibilitate a altor procese, care sunt la rândul lor activate etc.
Se numesc pasive acele procese în care nici un semnal nu va apărea
în membrul stâng al unei instrucţiuni de asignare. Aceste procese se numesc
pasive nu pentru că nu se execută, ci pentru că execuţia lor nu va antrena
execuţia altor procese. Instrucţiunea concurentă assert şi, în anumite
condiţii (neutilizarea parametrilor de mod out sau inout) apelul concurent
de proceduri, au procese echivalente pasive.

2.7 Procese amânate

Pentru asimilarea cunoştinţelor expuse în această secţiune este


indicată reluarea informaţilor din lucrarea nr. 3 referitoare la întârzierea
„delta”.
În continuare vom lucra pe următorul exemplu:

S2 <= not S1; --Teoretic, S1 nu este niciodată egal cu S2


S1 <= '0', '1' after 10 ns;--Impunem o formă de undă pt. S1
-- Testăm totuşi...
assert S2 = not S1 report "Comportare neaşteptată!";

Instrucţiunea assert va genera mesajul său aparent neobişnuit la


momentele indicate în figura 6.4.
Într-adevăr, verificarea condiţiei din instrucţiunea assert se
efectuează la fiecare schimbare a valorii lui S1 sau a lui S2. În consecinţă, în
acest caz precis, acest lucru se întâmplă în decursul anumitor întârzieri
116 LIMBAJUL VHDL
„delta”, care nu sunt, de fapt, decât nişte etape tranzitorii de calcul ale
simulatorului, care pot provoca alarme false.

S1

S2

0 ns 10 ns + 1 delta
0 ns + 1 delta 10 ns
În aceste două momente de
timp, semnalele au valori egale!
Figura 6.4 Întârzierile „delta” provoacă alarme false

Pentru evitarea acestor situaţii au fost introduse procesele amânate.


Un proces amânat nu este activat decât în momentul ultimei întârzieri
„delta” a unui ciclu de simulare, adică chiar înainte ca simulatorul să facă să
avanseze timpul de simulare curent (TIME).
Pe lângă procesul în sine, mai există încă trei instrucţiuni concurente
care pot fi transformate în procese amânate:
- instrucţiunea concurentă assert;
- apelul concurent de procedură;
- asignarea concurentă de valori unui semnal.
Sintaxa este foarte simplă: nu trebuie decât să se adauge cuvântul
cheie postponed imediat după etichetă (opţional).

P: postponed process ...


postponed assert ...
postponed PROCEDURA_MEA...

Prin urmare, următoarea linie de cod permite evitarea alarmelor false


din schema precedentă:

postponed assert S2 = not S1 report „Acest mesaj nu va mai


apărea!”;
DOMENIUL SECVENŢIAL. PROCESE 117
Există două restricţii de care este necesar să ţinem seama când
utilizăm acest gen de procese:
- Un proces amânat nu poate să conţină instrucţiuni de asignare cu
întârziere nulă. Dacă un asemenea proces ar conţine o astfel de
instrucţiune, ar putea să genereze la rândul lui o nouă întârziere
„delta”… chiar după ultima!
- Valoarea atributelor predefinite asupra semnalelor nu poate fi
utilizată în cadrul acestor procese. În practică, ea creează
numeroase şi dificile probleme de definiţie.

2.8 Semnale şi variabile în procese

Scopul principal al descrierii comportamentale VHDL este acela de a


descrie reacţia ieşirilor la intrări. Atât intrările cât şi ieşirile sunt semnale,
astfel că reacţiile semnalelor de ieşire sunt date sub forma unor instrucţiuni
de asignare de valori unor semnale. Semnalele şi instrucţiunile de asignare
apar în interiorul proceselor. Cu toate acestea, utilizarea semnalelor în
procese este guvernată de trei restricţii importante:
1. Semnalele nu pot fi declarate în interiorul proceselor;
2. Orice asignare a unei valori unui semnal are efect numai atunci
când procesul se suspendă. Până în momentul suspendării, toate
semnalele îşi păstrează valorile anterioare;
3. Numai ultima asignare a unei valori unui anumit semnal din
cadrul procesului este luată în considerare. Aşadar, nu are rost să
asignăm mai mult decât o singură valoare unui semnal în cadrul
procesului respectiv.
Dacă unui semnal îi este asignată o valoare într-un proces, iar
semnalul respectiv se află pe lista de sensibilitate a acelui proces, atunci
orice schimbare a valorii semnalului va provoca reactivarea procesului:

signal A, B, C, X, Y, Z: INTEGER;
process (A, B, C)
begin
X <= A + 1;
Y <= A * B;
Z <= C - X;
B <= Z * C;
Y <= B;
end process;
118 LIMBAJUL VHDL
Atunci când procesul va fi activat de o modificare a valorii
semnalului C, aceasta va provoca o modificare a valorii semnalului B din
interiorul procesului. Acest eveniment survenit pe semnalul B va provoca la
rândul lui o nouă reactivare a procesului, deoarece semnalul B face parte din
lista de sensibilitate a procesului.
La execuţia acestui proces, asignările de semnale sunt efectuate
secvenţial, în ordine, însă cea de-a doua asignare (Y <= A * B;) nu va fi
executată niciodată deoarece se va activa numai ultima asignare a
semnalului Y. Mai mult, în cea de-a treia asignare (Z <= C - X;) va fi
folosită numai valoarea anterioară a lui X, deoarece prima asignare (X <= A
+ 1;) va avea efectiv loc numai la suspendarea procesului, adică după
parcurgerea integrală şi efectuarea tuturor instrucţiunilor din lista de
instrucţiuni secvenţiale.
Restricţiile impuse asupra semnalelor au un impact deosebit de
puternic asupra aplicaţiilor lor practice. Imposibilitatea de a declara semnale
în cadrul proceselor nu constituie o problemă majoră, însă regulile de
asignare de valori semnalelor au consecinţe dintre cele mai serioase.
De vreme ce semnalele pot stoca numai ultima lor valoare asignată,
ele nu pot fi utilizate pentru stocarea datelor temporare sau intermediare în
interiorul proceselor.
Un alt inconvenient major îl constituie faptul că noile valori le sunt
asignate semnalelor nu în cadrul execuţiei instrucţiunilor de asignare, ci doar
după suspendarea procesului. Aceasta face ca analiza proiectului să devină
destul de dificilă, aşa cum se poate vedea din exemplul prezentat mai jos.

signal A, B, C, D, E: INTEGER;
process (C, D)
begin
A <= 2; -- Instrucţiunea 1
B <= A + C; -- Instrucţiunea 2
A <= D + 1; -- Instrucţiunea 3
E <= A * 2; -- Instrucţiunea 4
end process;

Să presupunem că, iniţial, toate cele 5 semnale (A, B, C, D şi E) au


valoarea 1 (A = 1, B = 1, C = 1, D = 1 şi E = 1). La un anumit moment de
timp, valoarea semnalului D se modifică de la 1 la 2. Acest eveniment
declanşează activarea procesului, pentru că semnalul D se află pe lista de
DOMENIUL SECVENŢIAL. PROCESE 119
sensibilitate a acestuia. Prin urmare, instrucţiunile din cadrul procesului vor
fi executate secvenţial, după cum urmează:
Instrucţiunea 1 (A <= 2;): semnalul A este pregătit să ia valoarea 2,
însă această asignare nu are deocamdată nici un efect asupra valorii
semnalului A. În continuare, semnalele îşi păstrează aceleaşi valori ca la
activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1).
Instrucţiunea 2 (B <= A + C;): semnalul B este pregătit să ia
valoarea (A + C = 2), însă această asignare nu are deocamdată nici un efect
asupra valorii semnalului B. În continuare, semnalele îşi păstrează aceleaşi
valori ca la activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1).
Instrucţiunea 3 (A <= D + 1;): semnalul A este pregătit să ia
valoarea (D + 1 = 3). Această asignare va „suprascrie” (sau se mai poate
spune că va înlocui) asignarea precedentă a semnalului A (cea din cadrul
instrucţiunii 1), însă ea nu are deocamdată nici un efect asupra valorii
semnalului A. În continuare, semnalele îşi păstrează aceleaşi valori ca la
activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1).
Instrucţiunea 4 (E <= A * 2;): semnalul E este pregătit să ia
valoarea (A * 2 = 2), însă această asignare nu are deocamdată nici un efect
asupra valorii semnalului E. În continuare, semnalele îşi păstrează aceleaşi
valori ca la activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1).
Instrucţiunea 5 este „ascunsă”: este vorba despre o instrucţiune wait
on C, D; (ştim că prezenţa listei de sensibilitate este echivalentă cu o
instrucţiune wait on plasată la sfârşitul procesului). În acest punct, procesul
este suspendat şi toate asignările de semnale, care până acum au fost doar
„pregătite”, vor deveni efective. Prin urmare,
- semnalul A va căpăta valoarea 3 (pregătită în cadrul instrucţiunii 3);
- semnalul B va căpăta valoarea 2 (pregătită în cadrul instrucţiunii 2);
- semnalul E va căpăta valoarea 2 (pregătită în cadrul instrucţiunii 4).
Situaţia finală va fi deci: (A = 3, B = 2, C = 1, D = 2 şi E = 2).
Deoarece nici A, nici B şi nici E nu se află pe lista de sensibilitate a
procesului, procesul rămâne suspendat până la o nouă activare a sa, care va
fi dată de apariţia unui eveniment pe semnalele C sau D (aceste evenimente
pot fi provocate de către asignările de valori semnalelor C şi D în cadrul
altor procese).
De vreme ce semnalele nu pot fi declarate în interiorul proceselor şi
întrucât asignarea valorilor lor nu are loc decât după suspendarea procesului
din care fac parte, a apărut necesitatea unui obiect care să poată fi declarat în
120 LIMBAJUL VHDL
interiorul unui proces şi care să ofere posibilitatea stocării temporare a
datelor. Un asemenea obiect există în VHDL şi se numeşte variabilă.
În exemplul de mai jos, unuia dintre semnale i se asignează de mai
multe ori noi valori. Cu toate acestea, respectivele asignări nu sunt vizibile
în exteriorul procesului. Această problemă poate fi rezolvată prin
introducerea variabilelor:

entity E1 is
port (A: in NATURAL := 1;
B : inout NATURAL := 1);
end entity E1;
architecture ARH1 of E1 is
begin
P1: process (A)
begin
B <= A + 2;
B <= B + 3;
B <= B * 2;
B <= B + 1; -- Numai această ultimă asignare are loc
end process P1;
end architecture ARH1;
-- Rezolvarea problemei prin introducerea de variabile
architecture ARH2 of E1 is
begin
P2: process (A)
variable B_VAR: NATURAL;
begin
B_VAR := A + 2;
B_VAR := B_VAR + 3;
B_VAR := B_VAR * 2;
B <= B_VAR + 1;
end process P2;
end architecture ARH2;

Variabilele sunt asemănătoare semnalelor, însă cu deosebirile expuse


mai sus. Datorită acestor caracteristici, ele pot fi utilizate în anumite aplicaţii
în care utilizarea semnalelor nu poate fi satisfăcătoare. De exemplu, ele pot
fi utilizate pentru descrierea algoritmilor în interiorul proceselor.
Întrucât variabilele sunt limitate numai la procese, ele sunt declarate
în interiorul proceselor (în zona de declaraţii a proceselor) şi nu pot fi
folosite în afara acestora. O declaraţie de variabilă seamănă cu declaraţia de
DOMENIUL SECVENŢIAL. PROCESE 121
semnal, cu singura diferenţă că se foloseşte cuvântul cheie variable, în locul
cuvântului cheie signal.
Asignarea de valori unei variabile se face prin intermediul
simbolului „:=”. Instrucţiunea de asignare de valori unei variabile modifică
pe loc valoarea acelei variabile (instantaneu - nu mai are loc amânarea
executării sale efective până la suspendarea procesului) şi fiecărei variabile i
se pot asigna noi valori ori de câte ori este necesar.
O variabilă poate fi declarată ca având orice tip sau subtip posibil,
atât constrâns cât şi neconstrâns:

variable MEM is array(NATURAL range<>,NATURAL range<>) of


STD_LOGIC;
variable DELTA1, DELTA2: TIME;
variable RAM1: MEM (0 to 1023, 0 to 7);

Valoarea iniţială a unei variabile poate fi dată de o expresie statică


globală. Expresia trebuie să fie de acelaşi tip ca şi variabila însăşi:

variable COND: BOOLEAN := TRUE;

În exemplul de mai jos, se prezintă mai întâi variabile cu nume


simple, apoi variabile cu nume parţiale (slice names) şi în final variabile cu
nume indexate.

variable X, Y: REAL;
variable A, B: BIT_VECTOR (0 to 7);
-- Variabile cu nume simple
X := 108.0;
A := B;
A := "11110000";
-- Variabile cu nume parţiale
A(3 to 6) := ('1', '1', '1', '1');
A(0 to 5) := B(2 to 7);
-- Variabile cu nume indexate
A(7) := '0';
B(0) := A(6);

Revenind la un exemplu anterior, să vedem modificările care apar în


cazul introducerii variabilelor:
122 LIMBAJUL VHDL
signal A, B, C, D, E: INTEGER;
process (C, D)
variable A_VAR, B_VAR, E_VAR: INTEGER := 0;
begin
A_VAR := 2; -- Instrucţiunea 1
B_VAR := A_VAR + C; -- Instrucţiunea 2
A_VAR := D + 1; -- Instrucţiunea 3
E_VAR := A_VAR * 2; -- Instrucţiunea 4
A <= A_VAR; -- Instrucţiunea 5
B <= B_VAR; -- Instrucţiunea 6
E <= E_VAR; -- Instrucţiunea 7
end process;

Să presupunem din nou că, iniţial, toate cele 5 semnale (A, B, C, D şi


E) au valoarea 1 (A = 1, B = 1, C = 1, D = 1 şi E = 1). La un anumit moment
de timp, valoarea semnalului D se modifică de la 1 la 2. Acest eveniment
declanşează activarea procesului, pentru că semnalul D se află pe lista de
sensibilitate a acestuia. Prin urmare, instrucţiunile din cadrul procesului vor
fi executate secvenţial, după cum urmează:
Instrucţiunea 1 (A_VAR := 2;): variabila A_VAR ia valoarea 2. În
continuare, semnalele îşi păstrează aceleaşi valori ca la activarea procesului
(A = 1, B = 1, C = 1, D = 2 şi E = 1; A_VAR = 2, B_VAR = 0 şi E_VAR =
0).
Instrucţiunea 2 (B_VAR := A_VAR + C;): variabila B_VAR ia
valoarea (A_VAR + C = 3). În continuare, semnalele îşi păstrează aceleaşi
valori ca la activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1;
A_VAR = 2, B_VAR = 3 şi E_VAR = 0).
Instrucţiunea 3 (A_VAR := D + 1;): variabila A_VAR ia valoarea
(D + 1 = 3). Această asignare va „suprascrie” (sau se mai poate spune că va
înlocui) valoarea precedentă a variabilei A (cea din cadrul instrucţiunii 1). În
continuare, semnalele îşi păstrează aceleaşi valori ca la activarea procesului
(A = 1, B = 1, C = 1, D = 2 şi E = 1; A_VAR = 3, B_VAR = 3 şi E_VAR =
0).
Instrucţiunea 4 (E_VAR := A_VAR * 2;): variabila E ia valoarea
(A_VAR * 2 = 6). În continuare, semnalele îşi păstrează aceleaşi valori ca la
activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1; A_VAR = 3,
B_VAR = 3 şi E_VAR = 6).
Instrucţiunea 5 (A <= A_VAR;): semnalul A este pregătit să ia
valoarea A_VAR = 3, însă această asignare nu are deocamdată nici un efect
DOMENIUL SECVENŢIAL. PROCESE 123
asupra valorii semnalului A. În continuare, semnalele îşi păstrează aceleaşi
valori ca la activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1;
A_VAR = 3, B_VAR = 3 şi E_VAR = 6).
Instrucţiunea 6 (B <= B_VAR;): semnalul B este pregătit să ia
valoarea B_VAR = 3, însă această asignare nu are deocamdată nici un efect
asupra valorii semnalului B. În continuare, semnalele îşi păstrează aceleaşi
valori ca la activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1;
A_VAR = 3, B_VAR = 3 şi E_VAR = 6).
Instrucţiunea 7 (E <= E_VAR;): semnalul E este pregătit să ia
valoarea E_VAR = 6, însă această asignare nu are deocamdată nici un efect
asupra valorii semnalului E. În continuare, semnalele îşi păstrează aceleaşi
valori ca la activarea procesului (A = 1, B = 1, C = 1, D = 2 şi E = 1;
A_VAR = 3, B_VAR = 3 şi E_VAR = 6).
Instrucţiunea 8 este „ascunsă”: este vorba despre o instrucţiune wait
on C, D; (ştim că prezenţa listei de sensibilitate este echivalentă cu o
instrucţiune wait on plasată la sfârşitul procesului). În acest punct, procesul
este suspendat şi toate asignările de semnale, care până acum au fost doar
„pregătite”, vor deveni efective. Prin urmare:
- semnalul A va căpăta valoarea 3 (pregătită în cadrul instrucţiunii 5);
- semnalul B va căpăta valoarea 3 (pregătită în cadrul instrucţiunii 6);
- semnalul E va căpăta valoarea 6 (pregătită în cadrul instrucţiunii 7).
Situaţia finală va fi deci: (A = 3, B = 3, C = 1, D = 2 şi E = 6).
Deoarece A, B şi E nu se află pe lista de sensibilitate a procesului,
procesul rămâne suspendat până la o nouă activare a sa, care va fi dată de
apariţia unui eveniment pe semnalele C sau D (aceste evenimente pot fi
provocate de către asignările semnalelor C şi D în cadrul altor procese).
În tabelul de mai jos este prezentată o comparaţie între semnale şi
variabile.
124 LIMBAJUL VHDL
SEMNALE VARIABILE
Semnalul are 3 trăsături caracteristice: Variabila are numai 2 trăsături:
- tip - tip
- valoare - valoare
- timp
Există o legătură foarte strânsă între Variabila nu păstrează decât
valoare şi timp: fiecare semnal are o valoarea sa curentă. Nu are
istorie a valorilor pe care le-a avut în istorie.
timp, în decursul simulării.
Atâta timp cât semnalele şi variabilele sunt de acelaşi tip, ele pot fi
asignate reciproc.

3. Desfăşurarea lucrării

3.1 Se vor testa cele 2 variante de descriere prin procese pentru


BISTABIL_D.
3.2 Se vor verifica cele două variante de definire ale arhitecturii
pentru entitatea E1. Se vor modifica valorile semnalelor şi ale
variabilelor şi se vor evidenţia diferenţele.
3.3 Se va proiecta şi simula sistemul numeric care comandă
funcţionarea unei firme luminoase care funcţionează astfel:
a) firma rămâne aprinsă o perioadă de timp TA = 5 secunde.
b) firma „pâlpâie” (sistemul de iluminare este aprins şi,
alternativ, stins de 5 ori) o perioadă TB = TA
c) procesul se reia de la a).
3.4 Se va proiecta un sistem logic secvenţial care citeşte date de pe o
linie serială şi detectează apariţia secvenţei 0110 din şirul de
intrare, afişând numărul de asemenea secvenţe detectate.
Atenţie: în cazul apariţiei secvenţei 0110110, sistemul va detecta
secvenţa dată de 2 (două) ori.
LUCRAREA NR. 7
INSTRUCŢIUNI SECVENŢIALE

1. Scopul lucrării
Lucrarea prezintă pe larg toate instrucţiunile secvenţiale permise în
cadrul domeniului secvenţial: instrucţiunea assert, instrucţiunea report,
instrucţiunea de asignare de valori semnalelor, apelul secvenţial de
procedură, structura condiţională if-then-else, instrucţiunea case, structura
de buclă loop, instrucţiunea next, instrucţiunea exit, instrucţiunea return şi
instrucţiunea nulă. Pentru toate instrucţiunile se pune la dispoziţie sintaxa
lor completă, însoţită de exemple semnificative.

2. Consideraţii teoretice
2.1 Instrucţiunea assert

Această instrucţiune permite supravegherea unei condiţii şi emiterea


unui mesaj în cazul în care condiţia este falsă (FALSE).
Sintaxa instrucţiunii este următoarea:

assert condiţie {report mesaj} {severity nivel_de_severitate};

În cursul execuţiei acestei instrucţiuni se evaluează condiţia


monitorizată. Dacă această condiţie (booleană) este adevărată, nu se
întâmplă nimic - instrucţiunea este echivalentă cu instrucţiunea nulă.
Dimpotrivă, dacă ea se dovedeşte falsă, atunci mesajul indicat şi nivelul de
severitate al erorii sunt utilizate pentru a „confecţiona” un mesaj care va fi
afişat imediat. Execuţia programului se reia apoi cu instrucţiunea imediat
următoare instrucţiunii assert.
Mesajul este în mod obligatoriu un şir de caractere de un sub-tip al
tipului STRING. În caz de absenţă a clauzei report (care este opţională),
mesajul implicit este „Assertion Violation”.
126 LIMBAJUL VHDL
Nivelul de severitate al erorii este o expresie de tipul
SEVERITY_LEVEL. Acest tip este definit în pachetul STANDARD; este
un tip enumerat definit astfel:

type SEVERITY_LEVEL is (NOTE, WARNING, ERROR, FAILURE);

În caz de absenţă a clauzei severity, se alege implicit nivelul de


severitate ERROR. Numeroase implementări decid oprirea simulării în
cazul întâlnirii unui asemenea nivel de severitate.
Se recomandă ca mesajul emis să conţină, pe lângă informaţiile
expuse mai sus, şi numele entităţii în care se găseşte instrucţiunea assert.
Această recomandare este deosebit de utilă mai ales în fazele incipiente de
proiectare a sistemului numeric în VHDL.
De obicei, o instrucţiune assert cu nivelul de severitate FAILURE
opreşte simularea. Prin urmare, putem controla sfârşitul simulării cu ajutorul
unei condiţii. Un exemplu al acestei practici este următorul:

assert NOW < 1 min report "Sfârşitul normal al simulării"


severity FAILURE;

Această linie va opri simularea, generând mesajul dat de îndată ce


timpul curent (returnat de către NOW, o funcţie din pachetul STANDARD)
va fi egal cu 1 minut. Cu alte cuvinte, se va simula numai primul minut de
funcţionare a sistemului.

2.2 Instrucţiunea report

Această instrucţiune a fost introdusă numai din motive de uşurare a


scrierii în VHDL. Ea nu aduce funcţionalităţi suplimentare faţă de cele
oferite de instrucţiunea assert, ci permite pur şi simplu afişarea unui mesaj
pe ecran. Implicit, simularea se desfăşoară apoi normal, dar există
posibilitatea specificării, la fel ca în cazul instrucţiunii precedente, a unui
nivel de severitate al mesajului.
Aşadar, instrucţiunea:

report "Salut!";
INSTRUCŢIUNI SECVENŢIALE 127
este echivalentă cu:

assert FALSE report "Salut!" severity NOTE;

Sintaxa completă a instrucţiunii report este următoarea:

[etichetă:] report mesaj [nivel_de_severitate_al_mesajului];

În VHDL se permite utilizarea unei etichete pentru fiecare


instrucţiune secvenţială.

2.3 Instrucţiunea de asignare de valori variabilelor

Asignarea semnalelor face parte tot din domeniul secvenţial; aceasta


a fost însă prezentată pe larg în cadrul lucrării nr. 3.
Asignarea de valori variabilelor se notează cu „:=”. Iată câteva
exemple simple:

VAR1 := 7;
VAR2 := VAR1 / (VAR1 + 22);

Aşa cum am arătat mai sus, variabila ia imediat valoarea indicată


(spre deosebire de semnal).
Variabilele pot fi asignate încă de la declarare, cu ajutorul aceluiaşi
operator de asignare:

-- Zona declarativă
variable X: INTEGER := 7;
variable Y: REAL := RADICAL(X);
-- Presupunem că RADICAL este o funcţie definită anterior
-- care returnează un rezultat de tip REAL

În ceea ce priveşte poantorii (tipul acces), este posibilă efectuarea de


asignări dinamice. Iniţializarea „în trecere” a obiectului spre care poantează
o variabilă de acest tip se face astfel:
128 LIMBAJUL VHDL

-- Zona declarativă
type P_POANTOR is acces INTEGER; -- Declaraţia unui tip acces
-- care poantează spre un obiect de tip întreg
variable UN_POANTOR: P_POANTOR;
begin
UN_POANTOR := new INTEGER (3);
-- S-a creat un obiect întreg iniţializat cu valoarea 3.
-- Tipul acces care poantează la acest întreg este stocat în
-- variabila UN_POANTOR.
end;

Observaţie
Instrucţiunea new „consumă” spaţiu de memorie deoarece ea creează
un obiect. La crearea fiecărui tip acces, se creează implicit o procedură
DEALLOCATE (a se vedea lucrarea nr. 4) care permite recuperarea
spaţiului de memorie ocupat de către obiectele de acest tip.

La asignarea tablourilor şi chiar a porţiunilor de tablouri uni-


dimensionale, trebuie să urmărim ca dimensiunile tablourilor din membrul
stâng şi din membrul drept al instrucţiunii de asignare să fie identice. O
diferenţă între dimensiunile celor două porţiuni ale tablourilor va provoca o
eroare la compilare (sau la execuţie, dacă este vorba despre porţiuni
dinamice de tablouri). Iată un exemplu:

type TABLOU is array (1 to 100) of INTEGER;


variable TAB1, TAB2: TABLOU;
...
TAB1 := TAB2;
TAB1(3 to 5):= (1, 2, 3);

2. 4 Instrucţiunea secvenţială de apel de procedură

Să luăm procedura O_PROCEDURĂ a cărei specificaţie este


următoarea:

procedure O_PROCEDURĂ (NUMĂR: INTEGER; MESAJ: STRING);


INSTRUCŢIUNI SECVENŢIALE 129
Apelarea acestei proceduri se realizează prin indicarea numelui său,
urmată de lista de parametri de apel, care se scrie între paranteze. Această
listă poate fi dată:
- într-o formă poziţională (de exemplu: O_PROCEDURĂ(4,
"START"););
- prin numirea parametrilor formali ai procedurii (de exemplu:
O_PROCEDURĂ(NUMĂR =>4, MESAJ =>"START"););
- prin combinarea celor două forme menţionate mai sus, urmărind
însă să plasăm mai întâi partea poziţională (de exemplu,
O_PROCEDURĂ(4, MESAJ => "START");). În acest caz,
ultimii parametri ai apelului pot fi omişi dacă au o valoare
implicită.
Apelul de procedură este detaliat în lucrarea nr. 10 dedicată sub-
programelor.

2.5 Structura condiţională

Aceasta este o instrucţiune structurată care permite executarea


condiţionată a unor secvenţe de instrucţiuni (care pot fi, la rândul lor,
structurate). Sintaxa sa de bază este următoarea:

if Condiţie_booleană then
Secvenţa_de_instrucţiuni_1
else
Secvenţa_de_instrucţiuni_2
end if;

Secvenţa de instrucţiuni numărul 1 nu va fi executată decât atunci


când condiţia booleană este TRUE, pe când secvenţa de instrucţiuni
numărul 2 nu va fi executată decât atunci când condiţia booleană este
FALSE. Aşadar, secvenţele de instrucţiuni numărul 1 şi numărul 2 sunt
mutual exclusive.
Ramura elsif permite înlănţuirea condiţiilor, fără a creşte nivelul de
imbricare:
130 LIMBAJUL VHDL

if A>B then
...
-- Secvenţa_de_instrucţiuni_1
...
else
if A=B then
...
-- Secvenţa_de_instrucţiuni_2
...
else
...
-- Secvenţa_de_instrucţiuni_3
...
end if;
end if;

Acest exemplu conţine două niveluri de imbricare care pot fi reduse


la unul singur prin utilizarea ramurii elsif. Numărul de ramuri elsif nu este
limitat şi această formă poate fi folosită în mod conjugat sau nu cu o ramură
else (şi numai una). Câştigul rezultat din punct de vedere al indentării poate
ameliora semnificativ lizibilitatea codului în cazul structurilor cu un număr
mare de ramuri.

if A>B then
...
-- Secvenţa_de_instrucţiuni_1
...
elsif A=B then
...
-- Secvenţa_de_instrucţiuni_2
...
else
...
-- Secvenţa_de_instrucţiuni_3
...
end if;
INSTRUCŢIUNI SECVENŢIALE 131
2.6 Instrucţiunea case

Această instrucţiune permite selectarea, în funcţie de valoarea unei


expresii, a unei secvenţe de instrucţiuni dintre mai multe alternative. Sintaxa
sa generală este următoarea:

case expresie is
when Valoare_1 =>... -- Secv_instrucţiuni_1
when Val_2|Val_3|Val_4 => ... -- Secv_instrucţiuni_2
when Val_5 to Val_6 =>... -- Secv_instrucţiuni_3
...
when others =>... -- Secv_instrucţiuni_n
end case;

Câmpul „expresie” şi valorile trebuie să aibă acelaşi tip discret


(enumerat). Ele pot avea şi un tip de structură tablou de biţi sau de caractere,
uni-dimensional. În toate aceste cazuri, valorile trebuie să fie statice (cu alte
cuvinte, care pot fi calculate în faza de elaborare).
Semnificaţia instrucţiunii este aceea că se doreşte executarea ramurii
a cărei valoare corespunde valorii expresiei.
Ordinea ramurilor nu are importanţă, cu o singură excepţie: ramura
others trebuie să se găsească la sfârşitul instrucţiunii, pentru a indica o
alegere "în ultimă instanţă". Această ramură (others) este obligatorie în
toate cazurile în care nu este enumerat întregul ansamblu al valorilor
posibile ale expresiei.

2.7 Structura de buclă

Bucla se foloseşte pentru repetarea secvenţei de instrucţiuni din


cadrul ei. Fiecare trecere se numeşte iteraţie.
Sintaxa generală a acestei instrucţiuni este următoarea:

{etichetă:} {schemă de iteraţie} loop


Secvenţă_de_instrucţiuni
end loop {etichetă};

Schema de iteraţie arată de câte ori va fi repetată secvenţa de


instrucţiuni. Dacă această schemă de iteraţie nu este precizată, atunci
132 LIMBAJUL VHDL
numărul de iteraţii va fi infinit. De exemplu, următoarea buclă va fi
executată până la depăşirea limitei INTEGER'HIGH de către variabila N:

variable N: INTEGER;
...
loop
N := N + 1;
end loop;

Există două scheme de iteraţie diferite:


1. Prima formă continuă iteraţiile „atâta timp cât” o condiţie dată
este adevărată; această condiţie este testată din nou la începutul fiecărei
iteraţii:

while condiţie loop


Secvenţă_de_instrucţiuni
end loop;

De exemplu:

variable R, M, N: INTEGER;
...
while R /= 0 loop
R := M mod N;
M := N;
N := R;
end loop;

2. Cea de-a doua formă repetă secvenţa de instrucţiuni de un anumit


număr de ori care nu poate fi cunoscut decât în momentul execuţiei. Există o
variabilă numită variabilă de buclă care permite contorizarea numărului de
cicluri. Această contorizare se efectuează prin parcurgerea unui tip enumerat
oarecare, de exemplu a unui sub-tip al lui INTEGER. Nu există posibilitatea
specificării unui pas (step), însă se poate utiliza eventual o variabilă
intermediară pentru rezolvarea acestei probleme.

for INDICE in 1 to 100 loop


... Secvenţă de instrucţiuni
end loop;
INSTRUCŢIUNI SECVENŢIALE 133
În cazul unui interval vid sau negativ, secvenţa de instrucţiuni este
pur şi simplu ignorată. Variabila de buclă (în exemplul anterior este vorba
despre INDICE) nu trebuie să fie declarată şi ea nu este cunoscută decât în
interiorul buclei. De asemenea, ei nu-i poate fi atribuită nici o altă valoare şi
deci nu poate fi modificată.
Bucla poate avea (opţional) o etichetă, care, dacă există, trebuie să
apară şi la sfârşitul buclei (end loop). Pe lângă avantajele legate de
lizibilitate pe care această etichetă le procură, ea poate fi utilizată şi în
cadrul instrucţiunilor exit şi next, care vor fi prezentate în cele ce urmează.

2.8 Instrucţiunea next

Instrucţiunea next permite oprirea iteraţiei în curs de desfăşurare a


unei bucle. După această instrucţiune, execuţia va continua cu iteraţia
următoare (dacă aceasta există).
Prin urmare, instrucţiunea next poate fi:
- imperativă:

next {eticheta_buclei};

- condiţională:

next {eticheta_buclei} when condiţie;

În acest ultim caz, întreruperea iteraţiei nu are loc decât dacă


respectiva condiţie este adevărată.
Această instrucţiune, dacă nu prezintă o etichetă, se referă la bucla
de nivelul cel mai de jos care o înglobează. Dacă prezintă o etichetă, atunci
instrucţiunea next permite ieşirea din iteraţia curentă a buclei care poartă
aceeaşi etichetă.

2.9 Instrucţiunea exit

La fel ca în cazul instrucţiunii next, instrucţiunea exit permite ieşirea


dintr-o buclă (şi deci întrerupe toate iteraţiile restante ale buclei). După
execuţia acestei instrucţiuni, programul va continua cu instrucţiunea imediat
următoare sfârşitului buclei (end loop).
134 LIMBAJUL VHDL
Prin urmare instrucţiunea exit poate fi:
- imperativă;

exit {eticheta_buclei};

- condiţională.

exit {eticheta_buclei} when condiţie;

În acest ultim caz, ieşirea din buclă nu are loc decât dacă respectiva
condiţie este adevărată.
La fel ca în cazul instrucţiunii next, această instrucţiune, dacă nu are
specificată o etichetă, nu se referă decât la bucla de nivelul cel mai de jos
care o înglobează. Dacă prezintă o etichetă, atunci instrucţiunea exit permite
ieşirea din bucla care poartă aceeaşi etichetă.
Iată un exemplu de utilizare a instrucţiunii exit:

variable R, M, N: INTEGER;
...
loop
R := M mod N;
M := N;
N := R;
exit when R = 0;
end loop;

2.10 Instrucţiunea return

Această instrucţiune este rezervată sub-programelor. La execuţia


acestei instrucţiuni, sub-programul este imediat suspendat, controlul
revenindu-i din nou apelantului.
O funcţie nu trebuie niciodată să fie executată până la cuvântul său
cheie final end, căci aceasta ar provoca o eroare la execuţie. Orice funcţie se
termină în mod dinamic prin instrucţiunea return, căreia i se asociază
întotdeauna valoarea returnată.

return VALOARE;
INSTRUCŢIUNI SECVENŢIALE 135
Tipul lui VALOARE trebuie, bineînţeles, să fie cel declarat în
specificaţia funcţiei. Într-o funcţie pot exista mai multe instrucţiuni return
pentru că pot exista mai multe ramuri de decizie.
Instrucţiunea return poate fi utilizată pentru întreruperea cursului
unei funcţii şi pentru a reveni în programul apelant. În acest caz, nu trebuie
să i se asocieze nici o valoare.

return;

2.11 Instrucţiunea nulă

Sintaxa instrucţiunii nule este următoarea:

null;

Semantica acestei instrucţiuni este clară: se trece la executarea liniei


de cod următoare. În practică, această instrucţiune este utilă pentru scrierea
anumitor instrucţiuni de selecţie (case) atunci când toate cazurile trebuie
luate în considerare, chiar dacă nu implică efectuarea nici unei acţiuni.
Instrucţiunea ;; care simbolizează instrucţiunea nulă în anumite
limbaje de programare este interzisă în VHDL.
Instrucţiunea nulă nu este necesară la compilarea unui proces sau a
unui corp de procedură „vid”. Următoarele porţiuni de cod sunt corecte din
punct de vedere sintactic:

procedure P is -- Prima variantă


begin
end;
Q: process -- A doua variantă
begin
end process Q;

3. Desfăşurarea lucrării
3.1 Se va implementa exemplul prezentat în continuare, care
gestionează traficul la o trecere simplă de pietoni. Dacă există
erori în descriere, acestea se vor detecta şi corecta.
136 LIMBAJUL VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.all
entity SEMAFOR is
port(
CLOCK, RESET, SENZOR1, SENZOR2: in STD_LOGIC;
ROSU1, ROSU2, GALBEN1, GALBEN2, VERDE1, VERDE2: out STD_LOGIC;
);
end SEMAFOR;

architecture SEMAFOR of SEMAFOR is


type STARE_T is (ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7);
signal STARE, NXSTARE: STARE_T;
begin
ACTUALIZEAZĂ_STARE: process (RESET, CLOCK)
begin
if (RESET = ’1’) then
STARE <= ST0;
elsif CLOCK’EVENT and CLOCK = ’1’ then
STARE <= NXSTARE;
end if;
end process ACTUALIZEAZĂ_STARE;
TRANSITIONS: process (STARE, SENZOR1, SENZOR2)
begin
-- iniţializări
ROSU1 <= ‘0’; GALBEN1 <= ‘0’; VERDE1 <= ‘0’;
ROSU2 <= ‘0’; GALBEN2 <= ‘0’; VERDE2 <= ‘0’;
case STARE is
when ST0 => VERDE1 <= ‘1’; ROSU2 <= ‘1’;
if SENZOR2 = SENZOR1 then NXSTARE <= ST1;
elsif (SENZOR1 = ‘0’ and SENZOR2 = ‘1’) then
NXSTARE <= ST2;
else NXSTARE <= ST0;
end if;
when ST1 => VERDE1 <= ‘1’; ROSU2 <= ‘1’;
NXSTARE <= ST2;
when ST2 => VERDE1 <= ‘1’; ROSU2 <= ‘1’;
NXSTARE <= ST3;
when ST3 => GALBEN1 <= ‘1’; ROSU2 <= ‘1’;
NXSTARE <= ST4;
when ST4 => ROSU1 <= ‘1’; VERDE2 <= ‘1’;
if (SENZOR1 = ‘0’ and SENZOR2 = ‘0’) then
NXSTARE <= ST5;
elsif (SENZOR1 = ‘1’ and SENZOR2 = ‘0’) then
NXSTARE <= ST6;
else NXSTARE <= ST4;
end if;
when ST5 => ROSU1 <= ‘1’; VERDE2 <= ‘1’;
NXSTARE <= ST6;
when ST6 => ROSU1 <= ‘1’; VERDE2 <= ‘1’;
NXSTARE <= ST7;
when ST7 => ROSU1 <= ‘1’; GALBEN2 <= ‘1’;
NXSTARE <= ST0;
end case;
end process TRANSITIONS;
end SEMAFOR;
INSTRUCŢIUNI SECVENŢIALE 137
3.2 Se va implementa următorul exemplu şi se va detecta dacă
numărătorul comută pe frontul ascendent sau descendent al
tactului. Se va defini interfaţa numărătorului.

NUMĂRĂTOR: process
variable COUNT: INTEGER := 0;
begin
wait until CLK = ‘1’;
while LEVEL = ‘1’ loop COUNT := COUNT + 1;
wait until CLK = ‘0’;
end loop;
end process NUMĂRĂTOR;
LUCRAREA NR. 8
DOMENIUL CONCURENT

1. Scopul lucrării

Lucrarea urmăreşte familiarizarea cu noţiunile fundamentale


referitoare la domeniul concurent al descrierilor VHDL. Se prezintă detaliat
problematica arhitecturilor concurente, cea a proceselor elementare şi cea a
semnalelor multi-sursă şi a funcţiilor lor de rezoluţie. Noţiunile expuse pot fi
asimilate şi aprofundate cu ajutorul numeroaselor exemple oferite.

2. Noţiuni introductive

2.1 Arhitecturi concurente

În VHDL putem descrie sistemele sub forma unor „mulţimi de


subsisteme funcţionale operând în mod concurent”. Fiecare dintre aceste
sisteme – care pot fi interdependente – vor fi specificate sub forma unor
procese separate. Nivelul de detaliere (sau granularitatea) depinde de
necesităţi – uneori un proces poate descrie funcţionarea unei componente
complexe, cum ar fi un microprocesor, alteori vom avea câte un proces
pentru fiecare componentă elementară de genul porţilor logice
fundamentale.
Unitate Sistem de
centrală de Memorie intrare /
prelucrare ieşire

Unitate
de Hard disc
dischetă

Figura 8.1 Arhitectura clasică a unui microcalculator


DOMENIUL CONCURENT 139
Descrierea comportamentală a unui sistem va consta deci dintr-o
mulţime de procese secvenţiale care operează în mod concurent.
De exemplu, pentru arhitectura clasică a microcalculatorului din
figura 8.1, descrierea VHDL va arăta astfel:

architecture EXEMPLU of MICROCALC is


signal DATABUS: BIT_VECTOR (31 downto 0);
begin
UCP: process -- Unitatea Centrală de Prelucrare
begin
...
end process UCP;
MEM: process -- Memoria
begin
...
end process MEM;
I_E: process -- Sistemul de intrare şi ieşire a datelor
begin
...
end process I_E;
DISC: process -- Hard discul
begin
...
end process DISC;
FLOPPY: process -- Unitatea de dischetă
begin
...
end process FLOPPY;
end architecture EXEMPLU;

Pentru înţelegerea domeniului concurent al VHDL este esenţială o


foarte bună cunoaştere a noţiunilor legate de arhitecturi. Obiectul VHDL
arhitectură este alcătuit dintr-un antet şi un corp.
Antetul specifică numele arhitecturii şi limitele corpului arhitecturii.
De asemenea, se precizează entitatea căreia îi aparţine arhitectura. În plus,
aici se declară eventualele obiecte care vor fi interne arhitecturii.
Corpul arhitecturii are o structură concurentă, care poate fi la început
mai dificil de înţeles, datorită faptului că toate instrucţiunile (procesele şi
conţinutul lor) sunt scrise în ordine secvenţială (este singura posibilitate
existentă în cadrul actualelor sisteme de calcul, bazate pe fişiere). Cu toate
acestea, trebuie să reţinem că toate procesele din interiorul unei arhitecturi
sunt executate concurent unele faţă de celelalte. Toate procesele din cadrul
140 LIMBAJUL VHDL
descrierii în VHDL a sistemului vor fi complet specificate în interiorul
corpului arhitecturii.
Iată în continuare două exemple foarte scurte de descriere
comportamentală. Primul exemplu arată modelarea în VHDL a unui bistabil
D cu acţionare pe frontul ascendent, cu un generator de tact intern, care
poate fi mascat de semnalul M, activ pe '0' logic (este un bistabil D cu
semnalul M pe post de clock enable). Desigur, niciun bistabil fizic nu are un
generator de tact intern, dar codul de mai jos reprezintă un model foarte util
numai pentru simularea mai rapidă şi mai eficientă a bistabilului.

entity BIST_D is
port (M, D: in BIT;
Q, NQ: buffer BIT);
end BIST_D;

architecture ARH of BIST_D is


signal S1, S2, CLOCK: BIT;
begin
CLK: process -- Generatorul de semnal de tact
begin
CLOCK <= '0';
wait for 5 ns;
CLOCK <= '1';
wait for 5 ns;
end process CLK;
N1: process(M)
begin
S1 <= not(M);
end process N1;
NAND1: process(S1, CLOCK)
begin
S2 <= S1 nand CLOCK;
end process NAND1;
D1: process(S2)
begin
if S2 = '1' then
Q <= D;
NQ <= not(D);
end if;
end process D1;
end architecture ARH;

Al doilea exemplu arată implementarea în VHDL a arhitecturii din


figura 8.2, reprezentând un sumator complet de numere pe un bit.
DOMENIUL CONCURENT 141

Figura 8.2 Sumator complet de numere pe un bit

entity SUMATOR_COMPLET is
port (A, B, CARRY_IN: in BIT;
SUM, CARRY_OUT: out BIT);
end SUMATOR_COMPLET ;
architecture ARH1 of SUMATOR_COMPLET is
signal S1, S2, S3, S4: BIT;
begin
P1: process(B, CARRY_IN)
begin
S1 <= B xor CARRY_IN;
end process P1;
P2: process(B, CARRY_IN)
begin
S2 <= B and CARRY_IN;
end process P2;
P3: process(A, B)
begin
S3 <= A and B;
end process P3;
P4: process(A, CARRY_IN)
begin
S4 <= A and CARRY_IN;
end process P4;
P5: process(A, S1)
begin
SUM <= A xor S1;
end process P5;
P6: process(S2, S3, S4)
begin
CARRY_OUT <= S2 or S3 or S4;
end process P6;
end architecture ARH1;
142 LIMBAJUL VHDL
După cum s-a prezentat deja în lucrarea nr. 6, un proces suspendat va
fi reactivat atunci când oricare dintre semnalele de pe lista sa de sensibilitate
îşi modifică valoarea. Această regulă se aplică şi în cazul în care există mai
multe procese într-o arhitectură: atunci când un semnal îşi modifică
valoarea, toate procesele care au respectivul semnal în lista lor de
sensibilitate vor fi reactivate. Instrucţiunile din interiorul proceselor
reactivate vor fi aşadar executate secvenţial, în ordine. Subliniem însă că
aceste instrucţiuni vor fi executate independent de instrucţiunile aflate în
interiorul celorlalte procese.
Conceptul de concurenţă ar fi probabil mult mai uşor de înţeles dacă
am putea scrie procesele unul lângă celălalt, şi nu unul după celălalt, aşa
cum suntem obligaţi să o facem în fişierul sursă, pe orice sistem de calcul
existent în momentul de faţă.
Întrucât procesele nu fac deosebirea între semnalele generate extern
(provenite din mediul exterior) şi cele generate intern (declarate în interiorul
arhitecturii), rezultă că semnalele care activează procesele pot fi generate şi
de către alte procese existente în interiorul aceleiaşi arhitecturi.
De fiecare dată când un semnal aflat pe lista de sensibilitate a unui
anumit proces îşi modifică valoarea, procesul respectiv va fi activat. Acest
lucru se întâmplă indiferent dacă modificarea valorii semnalului a fost
produsă de către mediul exterior sau de către un alt proces.

Observaţie
Pentru a realiza transferuri de informaţie între procese nu se pot
utiliza decât semnale. Datorită faptului că variabilele sunt obiecte locale
proceselor, ele nu pot fi utilizate ca purtătoare de informaţie între procese.

Noţiunile expuse mai sus sunt ilustrate în următorul exemplu: un


bistabil JK construit cu bistabil D, având un generator de semnal de tact
descris în interiorul arhitecturii.
DOMENIUL CONCURENT 143

entity BIST_JK is
port (J, K: in BIT;
Q, NQ: buffer BIT);
end BIST_JK;
architecture ARH of BIST_JK is
signal S1, S2, S3, S4, CLOCK: BIT;
begin
CLK: process -- Generatorul de semnal de tact
begin
CLOCK <= '0';
wait for 5 ns;
CLOCK <= '1';
wait for 5 ns;
end process CLK;
NAND1: process(J, NQ)
begin
S1 <= J nand NQ;
end process NAND1;
NAND2: process(S1, S4)
begin
S2 <= S1 nand S4;
end process NAND2;
NAND3: process(S3, Q)
begin
S4 <= S3 nand Q;
end process NAND3;
NOT1: process(K)
begin
S3 <= not(K);
end process NOT1;
process(CLOCK)
begin
if CLOCK = '1' then
Q <= S2;
NQ <= not(S2);
end if;
end process;
end architecture ARH;

2.2 Procese elementare

În anumite cazuri este necesar să utilizăm porţi logice elementare ca


module separate în cadrul arhitecturii, precum în exemplul de mai jos:
144 LIMBAJUL VHDL
IEŞIRE_ŞI <= IN1 and IN2;

Specificarea unui asemenea comportament foarte simplu cu ajutorul


unui proces ar necesita încă trei instrucţiuni suplimentare (antetul
procesului, clauza begin şi clauza end process), ceea ce este de prisos în
cazul unei simple porţi logice ŞI. Limbajul VHDL permite simplificarea
proceselor de acest gen (cele care conţin o singură instrucţiune) prin
utilizarea instrucţiunilor singulare de asignare concurentă de valori
semnalelor (numite astfel deoarece ocupă, de regulă, o singură linie de cod).
O instrucţiune de asignare concurentă de semnal poate apărea în
interiorul unei arhitecturi, eventual în paralel cu unele procese, şi poate fi
executată concurent cu alte instrucţiuni, în mod similar proceselor. Dacă
două sau mai multe instrucţiuni de asignare apar în cadrul unui proces, ele
vor fi executate secvenţial în ordinea în care au fost scrise. Însă, dacă
reprezintă instrucţiuni de asignare concurentă de semnal (deci apar în afara
oricărui proces), ele vor fi executate în mod concurent.

Observaţie
Instrucţiunile de asignare concurentă a semnalelor sunt echivalente
cu un proces conţinând o singură instrucţiune de asignare de semnal. Dacă
în cadrul unui proces apar două sau mai multe instrucţiuni de asignare, ele
vor fi executate într-o anumită secvenţă predefinită. Cu toate acestea, dacă
aceleaşi instrucţiuni vor apărea ca asignări concurente de semnal (deci apar
în afara oricărui proces), ele vor fi executate concurent.

Implementarea unui bistabil RS asincron constituie o ilustrare foarte


bună a utilizării instrucţiunilor de asignare concurentă:

entity BIST_RS is
port (SET, RESET: in BIT;
Q, NQ: buffer BIT);
end BIST_RS;
architecture ARH of BIST_RS is
begin
Q <= not(NQ and SET) after 1 ns;
NQ <= not(Q and RESET) after 1 ns;
end architecture ARH;
DOMENIUL CONCURENT 145

Cele două arhitecturi din exemplul de mai jos sunt echivalente:

architecture ARH1 of ENTITATE is


signal IN1, IN2: BIT;
begin
P1: process (A, C)
begin
IN1 <= A or C;
end process P1;
P2: process (B, D)
begin
IN2 <= B or D;
end process P2;
P3: process (IN1, IN2)
begin
OUT <= IN1 and IN2;
end process P3;
end architecture ARH1;

architecture ARH2 of ENTITATE is


signal IN1, IN2: BIT;
begin
IN1 <= A or C;
IN2 <= B or D;
OUT <= IN1 and IN2;
end architecture ARH2;

Pentru activarea proceselor am arătat deja că se folosesc listele de


sensibilitate şi instrucţiunile wait. Atunci, cum stau lucrurile cu
instrucţiunile de asignare concurentă a semnalelor, care nu au nici
instrucţiuni wait şi nici liste de sensibilitate?
După cum se poate observa în exemplul de mai sus, listele de
sensibilitate ale tuturor proceselor conţin semnale care apar ulterior în
membrul drept al instrucţiunilor de asignare. Prin urmare, instrucţiunea de
asignare concurentă de semnal este sensibilă la orice modificare a oricărui
semnal care apare în membrul ei drept. Această asignare poate fi întârziată
cu ajutorul clauzei after, folosindu-se în acest scop modelul de întârziere
inerţial sau transport (prezentate în lucrarea nr. 3).
Revenind la exemplul anterior cu descrierea sumatorului complet de
numere pe un bit din figura 8.2, o implementare echivalentă este cea care
urmează:
146 LIMBAJUL VHDL

entity SUMATOR_COMPLET is
port (A, B, CARRY_IN: in BIT;
SUM, CARRY_OUT: out BIT);
end SUMATOR_COMPLET;
architecture ARH2 of SUMATOR_COMPLET is
signal S1, S2, S3, S4: BIT;
begin
S1 <= B xor CARRY_IN;
S2 <= B and CARRY_IN;
S3 <= A and B;
S4 <= A and CARRY_IN;
SUM <= A xor S1;
CARRY_OUT <= S2 or S3 or S4;
end architecture ARH2;

2.3 Pilotul (driver-ul) unui semnal

În cadrul acestei secţiuni vom relua anumite noţiuni referitoare la


semnale, în special cele referitoare la semnalele multi-sursă.
Semnalele primesc noi valori în cadrul proceselor numai în
momentul suspendării acestora, suspendare realizată cu ajutorul instrucţiunii
wait explicite sau implicite (prin lista de sensibilitate a procesului). În plus,
dacă există mai multe asignări de valori pentru un acelaşi semnal, numai
ultima va fi luată în considerare. Atunci, cum se pot stoca informaţiile
referitoare la evenimentele apărute pe un anumit semnal?
Această funcţie este îndeplinită de pilotul (driver-ul) semnalului
respectiv. Compilatorul VHDL creează câte un pilot pentru fiecare semnal
care primeşte o valoare în interiorul unui proces. Regula este foarte simplă:
indiferent câte valori sunt atribuite semnalului în cadrul procesului, există
un singur pilot per semnal per proces. Toate operaţiile sunt efectuate asupra
pilotului, care este copiat în semnalul propriu-zis numai atunci când
procesul este suspendat.
Datorită pilotului său, fiecare semnal cunoaşte valorile sale trecute,
prezente şi viitoare (acestea din urmă pot fi cunoscute deoarece fiecare pilot
poate specifica şi forma de undă proiectată să apară pe semnalul respectiv).
Fiecărui pilot îi poate fi asignată o asemenea formă de undă care va consta
dintr-o secvenţă de una sau mai multe tranzacţii. O tranzacţie constă dintr-o
valoare a semnalului împreună cu o valoare de tip TIME, care va specifica
DOMENIUL CONCURENT 147
momentul de timp când pilotului îi va fi asignată noua valoare specificată de
către tranzacţie.
Forma de undă poate fi specificată în mod explicit ca o secvenţă de
valori împreună cu întârzierile asociate. Formele de undă pot fi considerate
drept viitorul proiectat al semnalului. Întrucât simulatoarele stochează
tranzacţiile fiecărui semnal, ele creează într-adevăr istoria semnalelor.
Un semnal căruia i se poate determina cu uşurinţă istoria şi viitorul
este semnalul CLOCK din cadrul exemplelor anterioare de bistabili cu
generator de tact intern (bistabil D şi bistabil JK).
Atunci când un semnal are un singur pilot, valoarea sa este foarte
uşor de determinat. În foarte multe aplicaţii, semnalele cu mai multe surse
sunt o apariţie obişnuită. De exemplu, în interiorul unui calculator cu
arhitectură clasică von Neumann, magistrala internă primeşte valori din
partea procesorului, a memoriei, a discurilor şi a dispozitivelor de intrare /
ieşire. Fiecare fir metalic din componenţa magistralei va fi deci pilotat de
mai multe dispozitive. De vreme ce VHDL este un limbaj de descriere
hardware specializat pentru sistemele numerice, astfel de situaţii sunt
controlate cu uşurinţă.
Este greu de stabilit dacă un semnal cu mai multe surse va fi
întotdeauna pilotat (comandat), la un anumit moment dat, de către un singur
dispozitiv. În unele sisteme această situaţie trebuie evitată cu stricteţe, pe
când în altele ea este chiar dorită (de exemplu, în cazul implementărilor de
„poartă ŞI cablată” sau „poartă SAU cablată” (wired AND, respectiv wired
OR)). În general, semnalele cu surse multiple necesită stabilirea unei metode
de determinare a valorii rezultante atunci când există mai multe dispozitive
sursă care furnizează date în mod concurent.
În cele ce urmează prezentăm exemplul unui controler de magistrală
utilizat pentru citirea şi scrierea în / din dispozitive de intrare / ieşire.
Magistrala poate fi accesată şi din exterior. De remarcat semnalele multi-
sursă şi asignarea semnalelor în procese diferite.

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity SISTEM is
port (INSTR: in INTEGER range 0 to 4; -- codul instrucţiunii
DATA_BUS: inout STD_LOGIC_VECTOR(7 downto 0));
end SISTEM;
148 LIMBAJUL VHDL
architecture ARH of SISTEM is
signal CIT_SCR: STD_LOGIC; -- '1' = citire, '0' = scriere
signal SELECT_DISP: STD_LOGIC;
-- '1' memoria, '0' interfaţa de I/E

begin
CTRL: process(INSTR) -- Procesul de control
begin
case INSTR is
when 0 => CIT_SCR <= '1'; -- Instrucţiune de citire
SELECT_DISP <= '1'; -- din memorie
when 1 => CIT_SCR <= '0'; -- Instrucţiune de scriere
SELECT_DISP <= '1'; -- în memorie
when 2 => CIT_SCR <= '1'; -- Instrucţiune de citire
SELECT_DISP <= '0'; -- din dispozitivul de I/E
when 3 => CIT_SCR <= '0'; -- Instrucţiune de scriere
SELECT_DISP <= '0'; -- în dispozitivul de I/E
when 4 => CIT_SCR <= 'Z'; -- Instrucţiune nulă
SELECT_DISP <= 'Z';
end case;
end process CTRL;

MEM: process(SELECT_DISP, CIT_SCR) -- Emularea memoriei


variable CEL: STD_LOGIC_VECTOR (7 downto 0);
begin
if SELECT_DISP = '1' then
if CIT_SCR = '1' then
DATA_BUS <= CEL after 7 ns; -- Citire din memorie
else CEL := DATA_BUS; -- Scriere în memorie
end if;
else DATA_BUS <= (others => 'Z'); -- Înaltă impedanţă
end if;
end process MEM;

I_E: process(SELECT_DISP, CIT_SCR) -- Emularea interfeţei de I/E


variable INTR_IES: STD_LOGIC_VECTOR (7 downto 0);
begin
if SELECT_DISP = '0' and SELECT_DISP'EVENT then
if CIT_SCR = '1' then
DATA_BUS <= INTR_IES after 12 ns;-- Citire de la portul de I/E
else INTR_IES:= DATA_BUS; -- Scriere la portul de I/E
end if;
else DATA_BUS <= (others => 'Z'); -- Înaltă impedanţă
end if;
end process I_E;
end architecture ARH;
DOMENIUL CONCURENT 149

2.4 Rezolvarea semnalelor multi-sursă

Simulatorul VHDL nu poate „şti” cu anticipaţie dacă un semnal


multi-sursă va fi sau nu activat din două sau mai multe surse simultan.
Datorită acestui fapt, simulatorul trebuie să fie „pregătit” pentru a mixa
valorile semnalului. Această „mixare” a semnalelor se numeşte în VHDL
rezolvare (resolving).
Regulile de mixare a valorilor semnalelor sunt specificate sub forma
unui tabel, care se numeşte funcţie de rezoluţie. Tabelul conţine toate
valorile posibile ale semnalului şi fiecare poziţie din el conţine informaţii
referitoare la valoarea care va fi generată dacă cele două valori
corespunzătoare liniei şi coloanei respective vor fi mixate.

Figura 8.3 Semnal multi-sursă (Y) cu doi piloţi

În figura 8.3 este prezentat un exemplu foarte simplu de semnal


multi-sursă. Semnalul Y este comandat de doi piloţi. Semnalul de selecţie S
va determina dacă ieşirea Y va fi conectată la A sau la not(A).

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity PILOŢI1 is
port (A, S: in STD_LOGIC;
Y: out STD_LOGIC);
end PILOŢI1;
150 LIMBAJUL VHDL
architecture ARH1 of PILOŢI1 is
begin
P1: process(A, S)
begin
if S = '1' then Y <= A;
else Y <= 'Z';
end if;
end process P1;
P2: process(A, S)
begin
if S = '0' then Y <= not(A);
else Y <= 'Z';
end if;
end process P2;
end architecture ARH1;
Studiind cu mai multă atenţie această problemă, ne putem da seama
că funcţia de rezoluţie are nevoie de mai multe valori decât cele din
componenţa setului de bază. O situaţie similară apare atunci când încercăm
să reprezentăm semnale multi-sursă în VHDL. De exemplu, pentru tipul
BIT, prima problemă care se pune este următoarea: ce se va întâmpla dacă
vom mixa valorile '0' şi '1' (cu alte cuvinte, care este valoarea rezolvată
pentru '0' şi '1'?). Problema nu poate fi rezolvată folosind numai două valori
– prin urmare, întrebarea precedentă nu are răspuns.
Acest aspect are o serie întreagă de consecinţe: dacă vom folosi
numai tipurile de date BIT şi BIT_VECTOR, atunci NU vom putea specifica
un microprocesor (de exemplu) în VHDL. Tipurile de date nerezolvate (aşa
cum sunt cele două tipuri menţionate mai sus, BIT şi BIT_VECTOR) nu pot
fi folosite pentru semnale multi-sursă, care sunt însă indispensabile pentru
specificarea magistralelor de date (care există, în mod evident, în orice
microprocesor). Pentru rezolvarea acestei probleme, trebuie să utilizăm alt
tip de date, care să conţină mai mult de două valori şi care să aibă definită o
funcţie de rezoluţie pentru toate combinaţiile valorilor semnalelor.
library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity PILOŢI2 is
port (A: in BIT;
Y: out BIT;
B: in STD_LOGIC;
Y1: out STD_LOGIC);
end PILOŢI2;
DOMENIUL CONCURENT 151
architecture ARH2 of PILOŢI2 is
begin

P1: process(A)
begin -- Această condiţie este suficientă pentru detectarea
-- frontului ascendent în cazul tipului BIT
if A = '1' and A'EVENT then Y <= A after 1 ns;
end if;
end process P1;

P2: process(B)
begin -- Această condiţie este suficientă pentru detectarea
-- frontului ascendent în cazul tipului STD_LOGIC
if B = '1' and B'EVENT and B'LAST_VALUE = '0'
then Y1 <= B after 2 ns;
end if;
end process P2;
end architecture ARH2;

Uneori, chiar şi semnalele uni-sursă necesită mai mult de două valori


pentru reprezentarea „obiectelor” hardware. Iată câteva dintre cele mai des
întâlnite valori care pot să apară:
- Dacă ştim despre un semnal că a fost iniţializat, dar nu putem
determina cu precizie dacă în acest moment valoarea sa este ‘0’
sau ‘1’ sau ‘Z’, atunci valoarea semnalului va fi notată cu ‘X’;
- Tampoanele (buffers) tri-state deconectează dispozitivele
generatoare de liniile de semnal prin intermediul stării de „înaltă
impedanţă”, notată cu 'Z';
- Uneori, un semnal poate avea o valoarea „neasignată” sau
„necunoscută”, care este diferită de valoarea indiferentă. Această
stare poate apărea, de exemplu, în cazul unui bistabil, a cărui
stare internă nu este cunoscută în momentul punerii sub tensiune
a sistemului, dacă nu există o logică de auto-iniţializare; ea se
notează cu 'U';
- Valoarea notată cu 'L' înseamnă starea '0' slabă (de exemplu,
ieşire emitor deschis în starea logică '0');
- Valoarea notată cu 'H' înseamnă starea '1' slabă (de exemplu,
ieşire emitor deschis în starea logică '1');
- Valoarea notată cu 'W' înseamnă starea necunoscută slabă;
152 LIMBAJUL VHDL
- Uneori, valoarea semnalului nu este importantă. Atunci, spunem
că valoarea sa este indiferentă (don't care) şi se notează cu '-'.
Aceste valori şi altele, mai puţin frecvente, sunt specificate de tipul
de date STD_ULOGIC, definit în cadrul pachetului STD_ULOGIC_1164
(litera U vine de la termenul Unresolved types). Pachetul conţine, de
asemenea, o definiţie a tipului vector, care se bazează pe tipurile
STD_ULOGIC şi STD_ULOGIC_VECTOR. Ambele tipuri au şi un set de
operaţii logice de bază definite asupra lor. Aceste valori nu pot fi utilizate în
cazul semnalelor multi-sursă.
Un semnal care posedă o funcţie de rezoluţie se numeşte semnal
rezolvat, ceea ce înseamnă că poate avea mai multe surse - cu alte cuvinte,
acestui semnal îi pot fi asignate mai multe valori, în cadrul unor procese
diferite.
Funcţia de rezoluţie poate fi menţionată la nivelul unei declaraţii de
sub-tip de date (situaţie în care ea se va aplica tuturor semnalelor de acest
sub-tip) sau a unei declaraţii de semnal.
În decursul simulării, această funcţie este apelată pentru calcularea
valorii pe care o va lua semnalul. Pentru calcul, ea utilizează toate valorile
surselor semnalului respectiv, cu excepţia celor care fac obiectul unei
tranzacţii nule, adică cele care sunt deconectate. În VHDL este posibilă
specificarea deconectării unui semnal, funcţionalitate deosebit de preţioasă
pentru modelarea unei magistrale.
De ce este asociată funcţia de rezoluţie unui sub-tip de date sau unui
semnal şi nu unui tip de date? Această particularitate a limbajului VHDL
permite păstrarea compatibilităţii cu tipul de date de bază, oferind totodată
utilizatorului mai multe sub-tipuri de date, fiecare cu propria sa funcţie de
rezoluţie.
Pentru tipurile compuse, putem avea o funcţie de rezoluţie la nivelul
elementelor şi o alta la nivelul tipului compus însuşi. Apare deci un conflict:
care dintre aceste funcţii de rezoluţie va fi aplicată? Problema este
soluţionată astfel: dacă există o funcţie de rezoluţie definită la nivelul tipului
compus, ea va fi cea luată în considerare, şi va masca funcţia de rezoluţie a
elementelor constituente ale respectivului tip de date.
Funcţia de rezoluţie nu are decât un singur parametru de intrare care
este un vector (tablou uni-dimensional) neconstrâns (numărul de elemente
nu este impus), ale cărui elemente sunt de acelaşi tip ca şi semnalul. În
DOMENIUL CONCURENT 153
timpul ciclului de simulare, ea va fi utilizată automat de fiecare dată când se
va pune problema calculării valorii rezultante.

Observaţie
O funcţie de rezoluţie trebuie în principiu să fie comutativă şi
asociativă, dacă se urmăreşte modelarea corectă a unui conflict de valoare.
Dacă nu se doreşte aşa ceva, atunci ordinea apelurilor, care nu se află sub
controlul proiectantului, va influenţa valoarea rezultantă a unui conflict. Cu
toate acestea, aceste proprietăţi (comutativitatea şi asociativitatea) nu fac
obiectul unor verificări din partea compilatorului.

Cele mai importante aspecte de reţinut în cazul funcţiilor de rezoluţie


sunt următoarele:
1. Argumentul funcţiei este unic şi este un vector cu elemente de
acelaşi tip ca şi valoarea returnată de funcţie;
2. Proiectantul nu controlează apelul acestei funcţii, care se face
automat de către simulator atunci când apare necesitatea
rezolvării (la conflicte).
Vom prezenta în continuare două exemple. Primul este cel al unei
funcţii de rezoluţie aplicabilă unor semnale de tipul BIT_REZOLV.
BIT_REZOLV este un tip de date care conţine patru valori: '0', '1', 'X' şi 'Z'.
BIT_REZOLV_VECTOR este definit ca un vector cu un număr oarecare de
elemente de tip BIT_REZOLV.
Reamintim că se recomandă includerea funcţiei de rezoluţie într-un
pachet care să conţină şi declaraţia tipului de date rezolvat corespunzător.

function REZ(SURSE: BIT_REZOLV_VECTOR) return BIT_REZOLV is


variable REZULTAT: BIT_REZOLV := 'Z';
begin
-- Algoritmul calculează valoarea returnată (REZULTAT) în
-- funcţie de valorile vectorului SURSE
for i in SURSE'RANGE loop
case SURSE(i) is
when 'X' => return 'X';
when '0' => if REZULTAT = '1' then
return 'X';
else REZULTAT := '0';
end if;
154 LIMBAJUL VHDL
when '1' => if REZULTAT = '0' then
return 'X';
else REZULTAT := '1';
end if;
when 'Z' => null;
end case;
end loop;
return REZULTAT;
end REZ;

Al doilea exemplu este de fapt un extras din codul sursă original din
pachetul IEEE STD_LOGIC şi reprezintă funcţia şi tabelul de rezoluţie
pentru tipul de date STD_LOGIC. Aceasta este cea mai simplă modalitate
de reprezentare a funcţiei de rezoluţie pentru logica cu 9 valori:

package body STD_LOGIC_1164 is


constant RESOLUTION_TABLE: STD_LOGIC_TABLE := (
-------------------------------------------------------------
| U X 0 1 Z W L H - | |
------------------------------------------------------------
('U', 'U', 'U', 'U', 'U', 'U', 'U', 'U', 'U'),-- | U |
('U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'),-- | X |
('U', 'X', '0', 'X', '0', '0', '0', '0', 'X'),-- | 0 |
('U', 'X', 'X', '1', '1', '1', '1', '1', 'X'),-- | 1 |
('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', 'X'),-- | Z |
('U', 'X', '0', '1', 'W', 'W', 'W', 'W', 'X'),-- | W |
('U', 'X', '0', '1', 'L', 'W', 'L', 'W', 'X'),-- | L |
('U', 'X', '0', '1', 'H', 'W', 'W', 'H', 'X'),-- | H |
('U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'),-- | - |
);

function RESOLVED(S: STD_ULOGIC_VECTOR) return STD_ULOGIC is


variable RESULT: STD_ULOGIC := 'Z'; -- implicit e starea cea
-- mai „slabă”
begin

-- Este esenţial să se efectueze un test pentru detectarea


-- cazului în care există un singur pilot, altminteri bucla
-- ar returna valoarea 'X' în cazul în care ar exista un
-- singur pilot de semnal de valoare '-'
if (S'LENGTH = 1) then return S(S'LOW);
else
DOMENIUL CONCURENT 155
for I in S'RANGE loop
RESULT := RESOLUTION_TABLE(RESULT, S(I));
end loop;
end if;
return RESULT;
end RESOLVED;

Recapitulând: tipul de date STD_ULOGIC suportă toate valorile ce


s-ar putea dovedi necesare pentru specificarea oricărui sistem numeric.
Totuşi, acest tip de date este nerezolvat, fiind inutilizabil în cazul semnalelor
multi-sursă. De aceea, în pachetul STD_LOGIC_1164 s-a definit un tip de
date suplimentar: STD_LOGIC, care îmbină puterea expresivă a celor nouă
valori ale lui STD_ULOGIC cu avantajele funcţiei de rezoluţie, oferind
proiectantului un tip de date universal. În momentul de faţă, STD_LOGIC
s-a impus ca un adevărat standard industrial de facto.
Singura diferenţă dintre STD_LOGIC şi STD_ULOGIC este aceea
că primul este o versiune rezolvată a celui de-al doilea. Din acest motiv,
toate operaţiile şi funcţiile (inclusiv RISING_EDGE şi FALLING_EDGE)
definite pentru STD_ULOGIC pot fi utilizate şi pentru STD_LOGIC, fără
nici o altă declaraţie suplimentară. Tipul STD_LOGIC_VECTOR este o
versiune rezolvată a lui STD_ULOGIC_VECTOR.
VALID_A

VALID_B

Figura 8.4 Circuit a cărui ieşire este controlată de doi piloţi independenţi

În circuitul din figura 8.4, ieşirea Y este controlată din două surse
independente. Specificaţia sa în VHDL este prezentată mai jos:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity PILOŢI3 is
port (VALID_A, VALID_B: in BOOLEAN;
A, B: in STD_LOGIC;
Y: out STD_LOGIC);
end PILOŢI3;
156 LIMBAJUL VHDL
architecture ARH3 of PILOŢI3 is
signal SA, SB: STD_LOGIC;
begin
SA <= A when VALID_A else 'Z';
SB <= B when VALID_B else 'Z';
P1: process(SA)
begin
Y <= SA;
end process P1;
P2: process(SB)
begin
Y <= SB;
end process P2;
end architecture ARH2;

3. Desfăşurarea lucrării

3.1 Se va desena structura internă (cu porţi) a bistabilului D descris


în primul exemplu din lucrare.
3.2 Se va testa sumatorul complet de numere pe un bit din figura 8.2.
Cum se poate face trecerea la un sumator de numere pe 2, 3, 4
etc. biţi?
3.3 Se va desena structura internă (cu porţi) a bistabilului JK descris
în exemplul din lucrare.
3.4 Se va proiecta o componentă “buffer tri-state” cu semnalul de
validare (enable) activ pe 0.
3.5 Se va implementa şi testa exemplul funcţiei de rezoluţie pentru
tipul BIT_REZOLV.
LUCRAREA NR. 9
INSTRUCŢIUNI CONCURENTE

1. Scopul lucrării

Informaţiile din această lucrare vin în completarea celor din lucrarea


precedentă. Sunt prezentate pe larg instrucţiunile concurente existente în
VHDL, împreună cu aplicaţiile lor principale şi cu exemple de utilizare
bogat documentate şi comentate. Vor fi analizate: instrucţiunea block,
apelul concurent de procedură, instrucţiunea concurentă assert,
instrucţiunea concurentă de asignare de valori semnalelor, instrucţiunea de
instanţiere a unei componente şi instrucţiunea generate.

2. Consideraţii teoretice

2.1 Descrierea concurentă

Există o diferenţă importantă între noţiunile de concurenţă şi


paralelism, care se cuvine a fi subliniată.
Instrucţiunile paralele se execută în funcţie de priorităţile atribuite de
sistem, fără nici un alt punct de sincronizare în afară de cele programate
explicit (prin construcţii specifice de genul: primitivele P şi V, semafoare,
monitoare etc.). Acesta este paralelismul întâlnit în anumite limbaje de
programare. Fondul acestui mecanism este nedeterminist şi noţiunea de
„timp real” este adeseori prezentă „în fundal”.
În cazul instrucţiunilor concurente, ordinea de execuţie este oarecare.
Aceasta permite punerea în aplicare a unui paralelism real, dar noţiunea de
timp real dispare: simularea nu se efectuează în timp real.
În concluzie, putem spune că paralelismul limbajelor de programare
cunoscute este un paralelism virtual în timp real, în vreme ce concurenţa din
VHDL este un paralelism real în timp virtual (timpul de simulare).
Descrierea unui sistem hardware se face în mod firesc sub o formă
concurentă. Un asemenea sistem se proiectează ca un ansamblu de module,
158 LIMBAJUL VHDL
de flux de date sau de algoritmi (în funcţie de nivelul de descriere ales) care
interacţionează, funcţionând în paralel.
Aceste funcţii sunt descrise de instrucţiuni concurente care se
execută în mod asincron; în plus, ordinea în care sunt scrise instrucţiunile nu
are nici o influenţă asupra ordinii lor de execuţie.
„Puterea” unui limbaj de descriere hardware poate fi evaluată în
funcţie de bogăţia setului său de instrucţiuni concurente. În cazul VHDL,
acesta este deosebit de complet şi de universal. Instrucţiunile sunt utilizate
pentru descrierea comportamentului diferitelor module din componenţa
circuitului. Prin urmare, ele se vor regăsi la nivelul unităţilor de proiectare
prin intermediul cărora se realizează descrierea: entităţile şi arhitecturile.
Într-o descriere concurentă, variabilele cunoscute din limbajele de
programare clasice nu-şi mai au locul, interacţiunea (transportul de
informaţie între procese) fiind modelată numai prin intermediul semnalelor.

2.2 Instrucţiunea block

Blocul reprezintă o modalitate de reunire a unui set de instrucţiuni


concurente pentru a le face să aibă acces la o serie de declaraţii locale,
invizibile din orice altă parte a restului descrierii. El permite de asemenea
scrierea de instrucţiuni de asignare gardate sau ierarhizarea descrierii.
Cele trei funcţii principale ale unui bloc sunt următoarele:
- încapsularea declaraţiilor;
- utilizarea instrucţiunilor de asignare gardate;
- suport pentru ierarhizarea proiectului.
Aceste trei funcţii nu se exclud reciproc: un bloc poate foarte bine
să-şi asume toate cele trei roluri deodată.
Sintaxa generală a unui bloc este următoarea:

etichetă: block {(condiţie_de_gardă)}


{antet_parametri_generici_şi_porturi}
...
... Declaraţii locale
...
begin
...
... Instrucţiuni concurente
...
end block {etichetă};
INSTRUCŢIUNI CONCURENTE 159
Condiţia de gardă trebuie să fie o expresie booleană. Ea poate fi
adeseori exprimată printr-o frază în limbaj natural, de genul „când semnalul
X ia o anumită valoare”.
Antetul opţional indică valorile (pentru parametrii generici) şi
semnalele (pentru porturi) pe care blocul le importă din mediul exterior.

2.2.1 Funcţia de încapsulare a declaraţiilor


Fireşte, partea declarativă a unui bloc nu este vizibilă decât în
interiorul blocului respectiv. Declaraţiile permise în interiorul unui bloc sunt
următoarele:
- declaraţie de sub-program;
- corp de sub-program;
- declaraţie de tip;
- declaraţie de sub-tip;
- declaraţie de constantă;
- declaraţie de semnal;
- declaraţie de fişier;
- declaraţie de alias;
- declaraţie de componentă;
- declaraţie de atribut;
- specificaţie de atribut;
- declaraţie de grup;
- specificaţie de deconectare;
- specificaţie de configuraţie;
- clauza use.
De remarcat interdicţia declarării variabilelor locale blocului
(datorită faptului că natura blocului este concurentă).

2.2.2 Utilizarea instrucţiunilor gardate


Această posibilitate răspunde unei nevoi care apare în mod frecvent
la proiectarea circuitelor logice. O mare parte a circuitului este sincronă,
ceea ce înseamnă că toate asignările de semnale sunt supuse unei condiţii de
tact. În general, un circuit sincron este un circuit în cadrul căruia unul sau
mai multe evenimente se pot produce în acelaşi timp. De exemplu, pentru un
circuit sincronizat pe frontul ascendent al semnalului de tact, fiecare
asignare de valoare unui semnal ar trebui să se scrie astfel:
160 LIMBAJUL VHDL

A <= expresie when (CLOCK = '1' and not CLOCK'STABLE);

Rezultatul acestei asignări poate fi văzut în schema din figura 9.1.


Asignarea rezultatului expresiei semnalului A nu se efectuează decât pe
frontul ascendent al semnalului de tact CLOCK.

CLOCK

Valoarea expresiei A

A Valoarea precedentă a lui A

Figura 9.1 Asignarea semnalului A pe frontul ascendent al semnalului CLOCK

Observaţie
Atributul STABLE returnează valoarea booleană TRUE atunci când
semnalul vizat nu şi-a modificat valoarea în pasul curent al simulării.
Condiţia de mai sus înseamnă deci că semnalul de tact are valoarea '1' şi nu
este stabilă (tocmai s-a modificat) în pasul curent al simulării. Atributul
STABLE este prezentat în detaliu în lucrarea nr. 5.

Ar fi laborios să se scrie în VHDL descrieri care să repete aceeaşi


condiţie de un mare număr de ori. Garda unui bloc oferă însă posibilitatea
„factorizării” acestor condiţii. Aşadar, scrierea blocului gardat (condiţia de
gardă este întotdeauna booleană) este echivalentă cu cea a unui bloc fără
gardă în care se defineşte un semnal boolean numit GUARD, care ia
valoarea expresiei de gardă şi în care instrucţiunile de asignare se efectuează
condiţionat.
În exemplul de mai jos se prezintă două blocuri echivalente:
INSTRUCŢIUNI CONCURENTE 161

BLOC1: block (CLOCK = '1' and not CLOCK'STABLE)


begin
S1 <= guarded DATA1 after 10 ns;
S2 <= guarded DATA2 after 15 ns;
end block BLOC1;

BLOC2: block
signal GUARD: BOOLEAN;
begin
GUARD <= CLOCK = '1' and not CLOCK'STABLE;
S1 <= DATA1 after 10 ns when GUARD else S1;
S2 <= DATA2 after 15 ns when GUARD else S2;
end block BLOC2;

Semnalul GUARD poate fi declarat explicit şi, cu toate acestea,


poate fi recunoscut ca declanşator al asignării gardate (cu ajutorul cuvântului
cheie guarded). În plus, fie că este sau nu declarat, acest semnal poate face
obiectul unei asignări.

2.2.3 Suport pentru proiectarea ierarhică


Blocul este unitatea de bază echivalentă a structurării în VHDL.
În practică, rolul blocului în cadrul structurării este echivalent cu cel
al procesului în cazul simulării. Orice altă formă de structurare a proiectului
nu este, de fapt, decât un artificiu echivalent cu un bloc, căci în cele din
urmă orice construcţie VHDL se reduce la un bloc.
De exemplu, o componentă instanţiată de către o entitate (ansamblul
specificaţie de entitate-arhitectură referit într-o configuraţie) este echivalentă
cu două blocuri imbricate.
Următorul exemplu este extras din Manualul de referinţă (LRM) al
VHDL. Se dă un pachet PAC care exportă o procedură CHECKTIMING a
cărei specificaţie poate fi scrisă după cum urmează:

package PAC is
procedure CHECKTIMING (signal A, B: BIT; T: TIME);
end PAC;

Fie următoarea pereche alcătuită din specificaţia de entitate X şi din


arhitectura Y:
162 LIMBAJUL VHDL
use WORK.PAC.all; -- Referirea pachetului PAC
entity X is
port(P1, P2: inout BIT);
constant DELAY: TIME := 1 ms;
begin
CHECKTIMING(P1, P2, 2*DELAY);
end X;
architecture Y of X is
signal P3: BIT;
begin
P3 <= P1 after DELAY;
P2 <= P3 after DELAY;
B: block
...
begin
...
end block;
end Y;

şi fie o componentă COMP a cărei declaraţie este următoarea:

component COMP
port (A, B: inout BIT);
end component;

şi pe care o instanţiem într-o componentă C plecând de la perechea


specificaţie de entitate X - arhitectură Y. Această entitate având porturile P1
şi P2 şi nu A şi B, trebuie să specificăm corespondenţele:

for C: COMP use entity X(Y) port map (P1 => A, P2 => B);

şi în final urmează să specificăm configuraţia componentei C (în care S1 şi


S2 sunt două semnale care trebuie conectate la această instanţă):

C: COMP port map (A => S1, B => S2);

Această instrucţiune va fi echivalentă cu următoarele două blocuri


imbricate:
INSTRUCŢIUNI CONCURENTE 163
C: block
port(A, B: inout BIT);
port map (A => S1, B => S2);
begin
X: block
port(P1, P2: inout BIT);
port map (P1 => A, P2 => B);
constant DELAY: TIME := 1 ms;
signal P3: BIT;
begin
CHECKTIMING(P1, P2, 2*DELAY);
P3 <= P1 after DELAY;
P2 <= P3 after DELAY;
B: block
...
begin
...
end block;
end block X;
end block C;

Partea declarativă a blocului înglobat (X) conţine atât declaraţiile


specificaţiei de entitate cât şi cele ale arhitecturii Y. De asemenea, partea
rezervată instrucţiunilor blocului înglobat (X) conţine atât instrucţiunile
specificaţiei de entitate cât şi cele ale arhitecturii Y.
În practică, această ierarhie tradusă sub formă de blocuri este
interesantă mai ales pentru programatorii de compilatoare / simulatoare
VHDL. În cazul modelării, ierarhia va fi rareori tradusă prin instrucţiuni
block (se lasă acest lucru pe seama compilatorului), preferându-se noţiunea
de componentă.

2.3 Apelul concurent de procedură

Apelul concurent de procedură are aceeaşi sintaxă ca şi apelul


secvenţial de procedură (singura diferenţă care apare este posibilitatea
specificării unei etichete opţionale).
Întrucât ne aflăm în domeniul concurent, parametrii procedurii nu
pot fi decât semnale sau constante (NU şi variabile!). Un apel de procedură
se efectuează întotdeauna prin indicarea numelui procedurii urmat, între
paranteze, de lista parametrilor de apel (apelul poate fi poziţional, prin nume
164 LIMBAJUL VHDL
sau combinat). Opţional, numele acestei proceduri poate fi precedat de către
o etichetă.
Să luăm exemplul unei proceduri care verifică dacă un semnal A
respectă un timp de prepoziţionare (set-up) TS (valoare declarată ca o
constantă) faţă de frontul ascendent al unui alt semnal, B. Semnalele A şi B
vor fi transmise în mod in (este o verificare – valorile acestor semnale nu
vor suferi modificări). Apelul concurent al procedurii se va putea scrie
astfel:

VERIF1: VERIF_SETUP (A, B, TS);

Un apel concurent de procedură va fi tradus în domeniul secvenţial


printr-un proces care înglobează apelul secvenţial corespondent, urmat de o
instrucţiune wait care va monitoriza semnalele de mod in şi inout date ca
parametri. Cu alte cuvinte, procedura va fi apelată la fiecare eveniment care
va apărea pe parametrii săi care sunt semnale de intrare. Instrucţiunea wait
este indispensabilă pentru evitarea unei bucle infinite, în care procesul
respectiv ar intra în lipsa ei. În cazul în care printre parametrii săi nu există
nici un semnal de mod in sau inout, instrucţiunea wait nu va avea nici o
condiţie: aceasta înseamnă că procedura nu va fi apelată decât o singură
dată, la iniţializare.
În cazul exemplului precedent, procesul echivalent se rezumă la:

VERIF1: process
begin
VERIF_SETUP(A, B, TS);
wait on A, B;
end process VERIF1;

Aşadar, numărul de procese create este egal cu numărul de apeluri


ale procedurii respective. Următoarea porţiune de cod creează trei procese:

VERIF1: VERIF_SETUP(A, B, TS);


VERIF2: VERIF_SETUP(C, D, TS_CD);
VERIF3: VERIF_SETUP(A, B, TS);

Aceste procese vor „trăi” până la sfârşitul simulării.


INSTRUCŢIUNI CONCURENTE 165
Utilizarea imediată a apelului concurent de procedură survine atunci
când trebuie să creăm mai multe procese implementând aceiaşi algoritmi,
dar care fie nu se aplică aceloraşi semnale, fie au constante diferite.
Algoritmul se scrie deci o singură dată sub formă de procedură (de exemplu,
într-un pachet) şi fiecare instrucţiune de apel va crea un proces specific pe
baza parametrilor transmişi.
Printr-o scriere adecvată a procedurii apelate, putem obţine două
efecte foarte interesante:
- Dacă procedura se încheie printr-o instrucţiune wait fără condiţii,
ea se va executa o singură dată, la iniţializare;
- Dacă scriem o procedură sub formă de buclă infinită având grijă
să introducem o instrucţiune wait (sensibilă la semnalele de
intrare, de mod in şi inout) imediat înainte de sfârşitul buclei,
vom suprapune astfel iteraţia procesului peste iterativitatea
derulării procedurii respective.
De exemplu, prin apelul concurent al procedurii:

procedure VERIF_SETUP (signal S, REF: in BIT; TIMP: TIME) is


begin
loop
... -- Algoritmul
...
wait on S, REF;
end loop;
end VERIF_SETUP;

se obţine acelaşi comportament ca şi la apelul aceleiaşi proceduri fără buclă


şi fără instrucţiune wait, cu o singură diferenţă: apelul procedurii
VERIF_SETUP nu este efectuat decât o dată şi prin urmare obiectele
eventual declarate în partea declarativă a acestei proceduri (în cazul de faţă
nu există nici unul) sunt remanente. Acest aspect este deosebit de important
în cazul oricărei proceduri care gestionează starea internă a unui automat
finit.

2.4 Instrucţiunea concurentă assert

Instrucţiunea are o sintaxă identică (cu excepţia etichetei, care este


opţională) cu cea a instrucţiunii secvenţiale assert.
166 LIMBAJUL VHDL

{etichetă:} assert condiţie {report mesaj} {severity


nivel_de_eroare};

La fel ca în cazul instrucţiunii secvenţiale, această instrucţiune are ca


efect monitorizarea unei condiţii şi emiterea unui mesaj în cazul în care
aceasta este falsă. De fapt, ea reprezintă o simplă versiune simplificată de
scriere a următorului proces:

{etichetă:} process
begin
assert condiţie {report mesaj}{severity nivel_de_eroare};
wait on lista_semnalelor_care_apar_în_condiţie;
end process {etichetă};

Observaţie
Este important de reţinut că acest proces echivalent nu are listă de
sensibilitate, ci conţine o instrucţiune secvenţială wait după instrucţiunea
secvenţială assert. Prin urmare, chiar dacă lista semnalelor care apar în
condiţie este vidă, procesul nu se va putea repeta în buclă.

Instrucţiunea assert se poate afla într-o specificaţie de entitate sau


într-o arhitectură. În cadrul unei specificaţii de entitate, ea permite de
exemplu controlarea validităţii semnalelor prezente la porturi sau a valorilor
parametrilor generici.
Asemănarea cu instrucţiunea secvenţială assert nu trebuie să
ascundă puterea acestei instrucţiuni. Într-adevăr, caracterul concurent şi
iterativ al procesului face ca monitorizarea condiţiei date să fie permanentă.
Simpla scriere a unei instrucţiuni concurente assert, de exemplu pentru
monitorizarea verificării timpului de prepoziţionare (set-up) al unui bistabil,
va crea un control permanent al acestei caracteristici.
La fiecare modificare a valorii unui semnal care ar putea modifica
valoarea condiţiei, se va efectua un test şi se va genera, eventual, mesajul de
eroare specificat. Acest caracter de „viaţă proprie” al controlului este foarte
util, cu atât mai mult cu cât absenţa ieşirilor acestui proces (singura „ieşire”
este – eventual – mesajul specificat) evită complicarea inutilă a simulării.
INSTRUCŢIUNI CONCURENTE 167

Observaţie
Scrierea procesului echivalent al acestei instrucţiuni ascunde o
capcană! În cazul în care condiţia nu depinde de nici un semnal, lista
semnalelor care intervin în cadrul condiţiei este vidă şi aceasta va fi
verificată doar o singură dată, la iniţializare, şi niciodată după aceea.
De exemplu, prin utilizarea condiţiei NOW < TIMP_MAX (unde
TIMP_MAX este o constantă şi NOW este funcţia care returnează timpul
curent al simulării), nu vom ajunge niciodată la sfârşitul simulării.
Dacă dorim să testăm o condiţie care nu utilizează nici un semnal,
atunci vom fi nevoiţi să scriem un proces care va conţine instrucţiunea
secvenţială assert şi va avea o listă de sensibilitate adecvată.

2.5 Instrucţiunea concurentă de asignare de valori semnalelor

Asignarea de valori semnalelor poate îmbrăca o formă condiţională


sau o formă selectivă. Fiecăreia dintre aceste forme îi corespunde câte o
formă secvenţială, cea a instrucţiunii de asignare conţinută în procesul
echivalent.
Sintaxa formei condiţionale este următoarea:

{etichetă:} nume_sau_agregat <= {guarded}


formă_de_undă_1 when condiţie_booleană_1 else
formă_de_undă_2 when condiţie_booleană_2 else
...
formă_de_undă_n;

Presupunând că se omite opţiunea guarded, această formă se traduce


în următorul proces echivalent (deci, în instrucţiuni secvenţiale):

if condiţie_booleană_1 then
nume_sau_agregat <= {transport} formă_de_undă_1;
elsif condiţie_booleană_2 then
nume_sau_agregat <= {transport} formă_de_undă_2;
...
else
nume_sau_agregat <= {transport} formă_de_undă_n;
end if;
Această formă se numeşte forma condiţională echivalentă.
168 LIMBAJUL VHDL
Sintaxa formei selective este următoarea:

{etichetă:} with expresie select


nume_sau_agregat <= {guarded}{transport}
formă_de_undă_1 when alegere_1,
formă_de_undă_2 when alegere_2,
...
formă_de_undă_n when alegere_n;

La fel ca în cazul formei condiţionale, presupunând că se omite


opţiunea guarded, această formă se traduce în următorul proces echivalent
(forma selectivă echivalentă):

case expresie is
when alegere_1 =>
nume_sau_agregat <= {transport} formă_de_undă_1;
when alegere_2 =>
nume_sau_agregat <= {transport} formă_de_undă_2;
...
when alegere_n =>
nume_sau_agregat <= {transport} formă_de_undă_n;
end case;

După cum se poate remarca, există două opţiuni comune acestor


două forme:
- Opţiunea transport, care a fost deja prezentată în cadrul lucrării
nr. 3, specifică un model de propagare non-inerţial (toate
impulsurile sunt transmise). Dacă apare în cadrul instrucţiunii
concurente de asignare, această opţiune va fi prezentă la nivelul
fiecărei asignări secvenţiale a formelor echivalente;
- Opţiunea guarded arată că asignarea nu va fi executată decât
dacă semnalul GUARD de tip BOOLEAN (declarat implicit
într-un bloc gardat sau explicit de către proiectant) are sau este
pe cale să primească valoarea TRUE. Eventual se va genera o
instrucţiune de deconectare. O asignare care utilizează această
opţiune se numeşte asignare gardată.
INSTRUCŢIUNI CONCURENTE 169
Observaţie
La declararea sa, un semnal poate fi clasificat ca având tipul registru
(cuvântul cheie register) sau magistrală (cuvântul cheie bus). Atunci este
vorba despre un semnal gardat (guarded signal). Aceste calificări, foarte
apropiate de preocupările fizice, hardware, permit descrierea
comportamentului unui semnal la deconectarea sa.

Aşadar, în cazul unei instrucţiuni concurente de asignare de semnal,


este posibilă apariţia unuia dintre următoarele patru cazuri:
1. Semnal ne-gardat, obiect al unei asignări ne-gardate. Asignarea
concurentă se reduce la forma echivalentă (condiţională sau
selectivă);
2. Semnal ne-gardat, obiect al unei asignări gardate. Asignarea
concurentă se reduce la următoarea condiţie:

if GUARD then
formă echivalentă (condiţională sau selectivă)
end if;

3. Semnal gardat, obiect al unei asignări ne-gardate. Acest caz este


interzis: compilatorul sau simulatorul VHDL va genera o eroare.
Un semnal gardat nu poate fi asignat decât prin intermediul unei
instrucţiuni gardate;
4. Semnal gardat, obiect al unei asignări gardate. Asignarea
concurentă devine foarte puternică în acest caz, căci ea va
gestiona implicit şi deconectarea semnalului, în cazul în care
asignarea (care este gardată) nu va fi executată (cu alte cuvinte,
dacă condiţia de gardă este falsă).

if GUARD then
formă echivalentă (condiţională sau selectivă)
else
instrucţiune de deconectare
end if;

Instrucţiunea de deconectare îi asignează semnalului gardat un


element de formă de undă nul după întârzierea indicată în specificaţia de
170 LIMBAJUL VHDL
deconectare a acestui semnal. Asignarea unui element de formă de undă nul
este rezervată semnalelor gardate şi se scrie astfel:

semnal_sau_agregat <= null after {expresie_de_tip_TIME};

Semnalul astfel asignat rămâne fără sursă („firul este întrerupt”)


după un interval de timp dat. Această instrucţiune înlătură din pilotul
semnalului tot ceea ce este datat „mai târziu” decât timpul dat în momentul
specificării deconectării.

Observaţie
O arhitectură este un bloc (fără condiţie de gardă). Într-un bloc fără
condiţie de gardă putem însă declara un semnal GUARD şi apoi putem
folosi instrucţiuni gardate. Aşadar, exemplul de mai jos este perfect valabil:

architecture ARH1 of ENT1 is


signal A: BIT;
signal GUARD: BOOLEAN;
begin
A <= guarded '1' after 10 ns;
end ARH1;

Aşadar, este inutil să creăm un bloc în interiorul unei arhitecturi,


dacă acest bloc (numit şi instrucţiune block) va fi singura instrucţiune a
respectivei arhitecturi.

2.6 Instrucţiunea de instanţiere a unei componente

2.6.1 Instanţierea
A instanţia o componentă înseamnă a face o copie (o instanţă) a unei
componente deja declarate („modelul”) şi de a o personaliza pentru a da curs
unei necesităţi particulare. De exemplu, dacă am declarat deja o componentă
INVERSOR, atunci putem instanţia o componentă INV1, apoi o
componentă INV2 etc. Sintaxa instrucţiunii este următoarea:

etichetă: numele_componentei_model
{corespondenţa parametrilor generici}
{corespondenţa porturi efective / porturi locale modelului};
INSTRUCŢIUNI CONCURENTE 171
Această instrucţiune permite descrierea unui modul al circuitului ca
fiind o instanţă a unui model. Modelul este definit printr-o declaraţie de
componentă. Se pot preciza următoarele informaţii:
- Valoarea parametrilor generici ai componentei. Acest aspect
reprezintă adaptarea componentei la nevoile de modelare
curente;
- Corespondenţa dintre porturile formale ale componentei model
şi porturile efective, cele pe care le vom conecta în circuitul
nostru actual. Există posibilitatea de a nu conecta toate porturile
modelului, caz în care porturile efective corespunzătoare nu vor
exista (vor fi „în aer”). În cazul porturilor de intrare (de mod in),
acestora li se vor asigna valorile implicite prevăzute eventual
pentru ele.
În exemplul de mai jos, se obţin două componente, C1 şi C2, care
asigură interconectarea semnalelor A, B, C şi D de tip BIT.

signal A, B, C, D: BIT;

-- Declararea componentei INVERSOR


-- INTRARE şi IEŞIRE sunt porturile modelului, iar A, B, C şi
-- D sunt porturile efective ale circuitului nostru
component: INVERSOR
port(INTRARE: in BIT; IEŞIRE: out BIT);
end component;

-- Componenta INVERSOR va fi instanţiată de două ori


C1: INVERSOR port map (INTRARE => A, IEŞIRE => B);
C2: INVERSOR port map (INTRARE => C, IEŞIRE => D);

2.6.2 Diferenţa dintre bloc şi componentă


Ce diferenţe există între un bloc şi o instanţă a unei componente?
Când trebuie creat un bloc şi când trebuie instanţiată o componentă?
Nu există nici o diferenţă fundamentală între un bloc şi o instanţă a
unei componente: după cum am văzut, o componentă este un bloc pentru că
ea este imaginea unei entităţi. Ambele construcţii sunt folosite pentru
ierarhizarea descrierii structurale.
Un bloc poate fi văzut ca o componentă deja instanţiată. În acest
sens, el reprezintă o variantă simplificată de scriere: crearea unui bloc evită
recurgerea la referirea unei entităţi, la declararea unei componente, la
172 LIMBAJUL VHDL
configurarea şi apoi la instanţierea sa. Deşi este ceva mai greu de utilizat în
practică, componenta are un mare avantaj faţă de bloc: ea este reutilizabilă.
Instanţierile componentelor se fac cu ajutorul unor parametri
(porturi) diferite. Această instanţiere este un mecanism foarte puternic,
pentru că permite omiterea anumitor porturi şi mai ales schimbarea
dinamică (prin configuraţie) a entităţii care va fi luată drept model.
Să remarcăm că blocul are o altă funcţie, efemeră în viaţa ciclului de
proiectare: el permite, într-o fază intermediară, crearea şi testarea rapidă a
ceea ce – mai târziu – va deveni o componentă. Acest rol nu este deloc
neglijabil pentru proiectant!
În concluzie, este foarte util să alegem o structurare în blocuri a
proiectului – dacă urmărim o scriere cât mai concisă, o proiectare rapidă sau
dacă excludem orice reutilizare a codului. Dimpotrivă, structurarea în
componente va aduce gradul de generalitate necesar unor aplicaţii complexe.

2.7 Instrucţiunea generate

Această instrucţiune permite elaborarea iterativă sau condiţională a


liniilor de cod sursă VHDL; ea NU efectuează o execuţie condiţională sau
iterativă a instrucţiunilor înglobate.
Cele două forme (condiţională şi iterativă) ale acestei instrucţiuni
sunt:

-- Forma condiţională
etichetă: if condiţie_booleană generate
...
Secvenţă de instrucţiuni concurente
...
end generate {etichetă};
-- Forma iterativă
etich: for nume_param_generare in interval_discret generate
...
Secvenţă de instrucţiuni concurente
...
end generate {etich};

Semantica formei condiţionale este foarte simplă: instrucţiunile


concurente înglobate nu există decât dacă, la elaborare, condiţia respectivă
este adevărată (TRUE).
INSTRUCŢIUNI CONCURENTE 173
Extrem de utilă, forma iterativă creează un număr de ansambluri de
instrucţiuni înglobate egal cu numărul de elemente cuprins în intervalul
discret dat (eventual nici unul). Pentru fiecare ansamblu de instrucţiuni,
parametrul generării (un identificator) este înlocuit, oriunde apare, cu
valoarea iteraţiei.
Prezentăm în continuare un exemplu care utilizează ambele forme
ale acestei instrucţiuni.
Să presupunem că dorim să conectăm o serie de N inversoare, ieşirea
unuia fiind conectată la intrarea următorului. Vom folosi în continuare
exemplul din secţiunea 2.6.1, în care am instanţiat două componente
INVERSOR. În acest caz, problema este diferită, deoarece nu se cunoaşte
dinainte numărul de componente necesare.
P(1) P(2) P(N-1)

Intrare-lanţ Ieşire-lanţ

Figura 9.2 Lanţ de N inversoare: entitatea LANŢ_DE_INVERSOARE

N va fi un parametru generic transmis entităţii


LANŢ_DE_INVERSOARE care trebuie proiectată. Specificaţia acestei
entităţi este următoarea:

entity LANŢ_DE_INVERSOARE is
generic (N: INTEGER);
port (INTRARE_LANŢ: in BIT; IEŞIRE_LANŢ: out BIT);
end LANŢ_DE_INVERSOARE;

Entitatea conţine două porturi: INTRARE_LANŢ şi IEŞIRE_LANŢ.


Mai rămâne de descris arhitectura sa asociată, ştiind că numărul de
inversoare din lanţ este N.
174 LIMBAJUL VHDL
architecture STRUCTURALĂ of LANŢ_DE_INVERSOARE is

-- Declararea unui semnal care memorează cele N-1 noduri


-- interne ale lanţului de inversoare
signal P: BIT_VECTOR (1 to N-1);

--Declararea componentei INVERSOR


component INVERSOR
port (INTRARE: in BIT; IEŞIRE: out BIT);
end component;

-- Toate instanţele componentei INVERSOR precedente vor fi


-- configurate pentru a utiliza specificaţia entităţii cu
-- acelaşi nume(INVERSOR) asociată arhitecturii COMP.
for all: INVERSOR use entity INVERSOR(COMP);
begin

-- Instanţierea primului inversor: forma condiţională a


-- instrucţiunii generate
PRIMUL_INVERSOR: if N > = 1 generate
INVERSOR_1: INVERSOR port map (INTRARE_LANŢ, P(1));
end generate PRIMUL_INVERSOR;

-- Instanţierea inversoarelor intermediare: forma iterativă a


-- instrucţiunii generate
ALTE_INVERSOARE: for I in 1 to N-2 generate
INVERSOR_I: INVERSOR port map (P(I), P(I+1));
end generate ALTE_INVERSOARE;

-- Instanţierea ultimului inversor: forma condiţională a


-- instrucţiunii generate
ULTIMUL_INVERSOR: if N > = 2 generate
INVERSOR_N: INVERSOR port map (P(N-1), IEŞIRE_LANŢ);
end generate ULTIMUL_INVERSOR;
end STRUCTURALĂ;

Mai există încă o posibilitate de specificare a arhitecturii: vom utiliza


numai forma iterativă a instrucţiunii generate şi vom memora N+1 noduri.
Cele două noduri suplimentare sunt legate la intrare şi la ieşire prin
intermediul a două instrucţiuni de asignare concurentă de semnal.
INSTRUCŢIUNI CONCURENTE 175
architecture STRUCTURALĂ_2 of LANŢ_DE_INVERSOARE is
-- Declararea unui semnal care memorează cele N+1 noduri
signal P: BIT_VECTOR (1 to N+1);
component INVERSOR
port (INTRARE: in BIT; IEŞIRE: out BIT);
end component;
for all: INVERSOR use entity INVERSOR(COMP);
begin
-- Instanţierea tuturor componentelor
INVERSOARE: for I in 1 to N generate
INVERSOR_K: INVERSOR port map (P(I), P(I+1));
end generate INVERSOARE;
-- Primul nod se conectează la intrarea lanţului
P(1) <= INTRARE_LANŢ;
-- Al (N+1)-lea nod se conectează la ieşirea lanţului
IEŞIRE_LANŢ <= P(N+1);
end STRUCTURALĂ_2;

Acest exemplu ne permite să facem două observaţii importante:


1. Parametrul N trebuie să fie cunoscut în momentul elaborării: prin
urmare, N poate fi o constantă sau un parametru generic;
2. În cazul de faţă, configurarea inversoarelor se face „din mers”
pentru toate componentele (for all...).

3. Desfăşurarea lucrării

3.1 Se va implementa un DEMULTIPLEXOR 1 la 8 descriind


structura sa internă sub formă de porţi logice, unde fiecare poartă
logică va constitui un bloc intern.
3.2 Se va proiecta şi implementa un registru de deplasare universal
BARREL SHIFTER care poate efectua deplasări la stânga sau la
dreapta de maximum 3 poziţii binare. Se va studia oportunitatea
utilizării instrucţiunii concurente assert şi a blocurilor gardate.
3.3 Pentru implementarea bistabililor din componenţa registrului de
deplasare universal BARREL SHIFTER de la punctul anterior, se
vor instanţia componente BISTABIL_D.
3.4 Se va testa exemplul LANŢ_DE_INVERSOARE din lucrare.
3.5 Se va implementa un sumator de numere pe N biţi folosind
instrucţiunea generate.
LUCRAREA NR. 10
SUB-PROGRAME

1. Scopul lucrării

Lucrarea urmăreşte însuşirea noţiunilor de bază referitoare la sub-


programe în VHDL. Există două tipuri de sub-programe: procedurile şi
funcţiile, fiecare dintre ele fiind analizate şi comentate în detaliu. Se discută
de asemenea problema supraîncărcării atât a funcţiilor şi a procedurilor, cât
şi a operatorilor.

2. Consideraţii teoretice

2.1 Descrierea concurentă

Sub-programele permit scrierea unor algoritmi reutilizabili. Valorile


parametrilor pot fi diferite la fiecare apel, obţinându-se astfel efecte diferite.
Sub-programele pot fi folosite pentru mărirea lizibilităţii unui program,
modularizând codul sursă, chiar dacă unele dintre ele nu vor fi apelate decât
o singură dată.
În practică, sub-programele se folosesc atunci când apare necesitatea
unei secvenţe de instrucţiuni (aşadar, respectivele instrucţiuni vor fi
secvenţiale şi nu concurente) pentru descrierea unei anumite operaţii
complexe, a unei conversii, a anumitor porţiuni din descrierea unor procese
sau a unor funcţii de rezoluţie pentru semnale multi-sursă (funcţiile de
rezoluţie au fost prezentate pe larg în lucrarea nr. 8).
Există două tipuri de sub-programe:
- procedurile (cuvântul cheie procedure);
- funcţiile (cuvântul cheie function).
Procedurile pot acţiona prin efecte laterale. Efectul lateral este, prin
definiţie, o modificare (sau o consultare) a mediului printr-un mijloc care
diferă de parametrii de ieşire ai procedurilor, semnalele de mod out ale
entităţilor sau valorile returnate de către funcţii. De exemplu, asignarea unei
valori unui semnal global (care nu este declarat local, ci în cadrul unui
SUB-PROGRAME 177
pachet) constituie un efect lateral. Procedurile pot modifica, eventual, şi
valoarea parametrilor transmişi la apel.
Funcţiile returnează un rezultat (şi numai unul) şi nu acţionează prin
efecte laterale. Ele nu pot citi sau scrie variabile sau semnale decât dacă
acestea sunt declarate în zona de declaraţii a funcţiei sau în zona declarativă
a sub-programelor apelate. Cu alte cuvinte, dacă funcţia va fi apelată de mai
multe ori cu aceleaşi valori ale parametrilor, ea va returna de fiecare dată
acelaşi rezultat.

Observaţie
O excepţie la această regulă o constituie funcţia NOW care
returnează data curentă a simulării şi care nu are parametri. Această funcţie
se numeşte impură.

Începând cu VHDL'93 a fost introdusă o nouă categorie de funcţii,


numite impure care au posibilitatea de a crea efecte laterale. Ele se declară
folosind cuvântul cheie impure, fiind totodată supuse unor restricţii asupra
utilizării lor:
- o funcţie de rezoluţie nu poate fi o funcţie impură;
- o funcţie impură nu poate fi apelată dintr-o funcţie pură (cu alte
cuvinte, ne-impură).
Compilatorul va verifica întotdeauna aceste restricţii. În mod
simetric, s-a introdus şi cuvântul cheie pure: el permite explicitarea
caracterului pur al unei funcţii. Acest caracter este totodată atribuit în mod
implicit oricărei funcţii, dacă nu se specifică cuvântul cheie impure.
Apelul unei proceduri este o instrucţiune, în vreme ce apelul unei
funcţii se trece, la fel ca o valoare, în membrul drept al simbolului de
asignare.
Un sub-program este alcătuit din două părţi: declaraţia (opţională) şi
corpul său.
Declaraţia unui sub-program (numită şi „specificaţie de sub-
program”) va indica:
− genul sub-programului (procedură sau funcţie);
− numele acestuia;
178 LIMBAJUL VHDL
− lista parametrilor săi (parametrii formali). Pentru fiecare
dintre aceşti parametri, declaraţia sub-programului va preciza
modul (de intrare, de ieşire, de intrare / ieşire) şi tipul său;
− tipul valorii returnate (în cazul unei funcţii).
Corpul sub-programului va conţine algoritmul implementat; el va fi
perfect coerent cu declaraţia sub-programului. Compilatorul are sarcina de a
garanta această compatibilitate.

2.2 Declaraţia de sub-program

Declaraţia de sub-program este opţională. De regulă, în partea de


specificare a pachetelor, vom găsi declaraţia tuturor sub-programelor pe care
respectivul pachet le exportă. Corpul acestor sub-programe se va găsi în
corpul pachetului.
Declaraţia furnizează numeroase informaţii despre funcţionalitatea
sub-programului, în special dacă beneficiază de câteva linii de comentarii.
Ea poate fi deci văzută ca „manualul de utilizare” a sub-programului.
Sintaxa declaraţiilor de sub-program este următoarea:

-- Declaraţia unei proceduri


procedure nume_procedură (lista_parametrilor_formali);

-- Declaraţia unei funcţii


{pure/impure} function nume_funcţie (lista_parametri_formali)
return tipul_rezultatului;

În cazul procedurii, numele este pur şi simplu un identificator (la fel


ca în cazul variabilelor sau semnalelor).
În cazul funcţiei, numele poate fi un identificator, însă şi simbolul
unui operator (cum ar fi +, =, & sau and). În acest caz, simbolul se va scrie
între ghilimele ("+") şi funcţia va putea fi activată sub forma unui operator.
Lista parametrilor formali are următoarea sintaxă:

{clasă_obiect} nume_param_1{, nume_param_2}: {mod_transmitere}


type valoare_implicită;

această declaraţie putându-se repeta de un anumit număr de ori.


Există trei moduri de transmitere a parametrilor:
SUB-PROGRAME 179
1. Modul intrare (cuvântul cheie in) este modul de transmitere
implicit. El corespunde unor parametri care pot fi citiţi în cadrul
sub-programului, dar care nu pot fi modificaţi. Prin urmare, în
interiorul acestui sub-program, ei nu pot apărea în membrul stâng
al nici unei instrucţiuni de asignare şi nici ca parametri de apel ai
vreunei proceduri în alt mod decât modul in;
2. Modul ieşire (cuvântul cheie out) este modul unui parametru
formal a cărui valoare este returnată de către sub-program. În
cazul de faţă, este vorba despre o procedură, căci acest mod le
este interzis parametrilor formali ai unei funcţii (o funcţie nu
returnează decât valoarea sa). În interiorul unei proceduri, este
interzisă citirea unui parametru formal având modul ieşire – prin
urmare, acest parametru nu va fi niciodată întâlnit în membrul
drept al unei instrucţiuni de asignare. În plus, el nu poate fi
folosit nici ca parametru actual de intrare sau de intrare / ieşire în
cazul apelării unui alt sub-program intern;
3. Modul combinat intrare / ieşire (cuvântul cheie inout)
autorizează orice citire şi scriere în interiorul unei proceduri. Din
aceleaşi motive ca şi cele expuse în cazul modului de ieşire
(out), el nu poate fi un parametru formal al unei funcţii.
Un parametru care posedă o valoare implicită poate fi omis la apelul
unui sub-program. Orice semnal are întotdeauna o valoare implicită
asociată. Valoarea implicită este acea valoare care îi este atribuită unui
parametru atunci când apelul nu precizează nimic în această direcţie.
Această valoare este o expresie care trebuie să fie de acelaşi tip ca şi
parametrul şi este evaluată o dată pentru totdeauna în decursul elaborării
(înaintea execuţiei programului VHDL).
În VHDL există trei clase de obiecte:
- Constante;
- Variabile;
- Semnale.
Restricţiile existente asupra clasei unui parametru în funcţie de
modul său sunt date în tabelul de mai jos (figura 10.1):
180 LIMBAJUL VHDL

Clasă Obiect de clasă Obiect de Obiect de


Mod constantă clasă variabilă clasă semnal
Parametru de mod Permis şi luat Permis Permis
in în proceduri implicit
Parametru de mod Permis Permis şi luat Permis
out în proceduri implicit
Parametru de mod Permis Permis şi luat Permis
inout în proceduri implicit
Parametru de mod Permis şi luat Permis
NU
in în funcţii implicit
Parametru de mod
NU NU NU
out în funcţii
Parametru de mod
NU NU NU
inout în funcţii

Figura 10.1 Relaţia dintre clase şi modul de transmitere a parametrilor formali

Sintaxa prezentată în figura 10.1 arată că, în cazul definirii unui


parametru formal, nu este obligatoriu să se precizeze dacă este vorba despre
o constantă, o variabilă sau un semnal. Tabelul de mai sus ilustrează clasele
care sunt luate implicit în funcţie de modul parametrului.
Iată câteva exemple de declaraţii de sub-programe:

procedure VERIF_PERIOADA (signal TACT: in BIT);


procedure CONVERSIE (INTRARE : in REAL; IEŞIRE: out INTEGER);
function CONVERSIE (INTRARE : in REAL) return INTEGER;
function "+"(A, B: INTEGER) return INTEGER;

Următoarele porţiuni de cod sunt variante perfect echivalente:

procedure MIN (A, B: INTEGER; C: out INTEGER);


procedure MIN (A: INTEGER; B: INTEGER; C: out INTEGER);
procedure MIN (A, B: in INTEGER; C: out INTEGER);

function MIN (A, B: INTEGER) return INTEGER;


function MIN (A: INTEGER; B: INTEGER) return INTEGER;
function MIN (A: in INTEGER, B: in INTEGER) return INTEGER);
SUB-PROGRAME 181
2.3 Corpul de sub-program

Corpul sub-programului are menirea de a realiza funcţionalitatea


sub-programului. Sintaxa sa este următoarea:

Antet_sub-program is
{zona declarativă} --Acoladele delimitează părţile opţionale
begin
{zona rezervată instrucţiunilor}
end {nume_sub-program}

Corpul unui sub-program va conţine algoritmul ataşat sub-


programului respectiv. Antetul acestui corp începe prin copierea
specificaţiei sub-programului, singura diferenţă constând în faptul că ea se
încheie cu cuvântul cheie is şi nu cu punct şi virgulă („;”).
Urmează apoi o parte declarativă care va conţine declaraţiile interne
acestui sub-program. Declaraţiile nu vor fi vizibile din exteriorul sub-
programului.
În această zonă se pot găsi şi alte declaraţii sau corpuri de sub-
program, tipuri şi sub-tipuri, constante şi variabile, fişiere, alias-uri şi
atribute. Este permisă specificarea de atribute şi prezenţa clauzei use. Cea
mai importantă restricţie o constituie aceea referitoare la semnale: în cadrul
unui sub-program este interzisă declararea semnalelor, deoarece ne aflăm în
domeniul secvenţial (aici, după încheierea execuţiei sale, sub-programul
„moare”, în timp ce semnalul ar continua să existe!). Zona de declaraţii se
termină o dată cu cuvântul cheie begin, care indică începutul porţiunii care
conţine algoritmul sub-programului.
Această porţiune este alcătuită dintr-o suită de instrucţiuni care se
execută secvenţial. Ea începe deci cu cuvântul cheie begin şi se încheie cu
cuvântul cheie end, urmat eventual de numele sub-programului. În zona de
algoritmi, o funcţie returnează valoarea sa prin intermediul instrucţiunii
return, pe când o procedură va asigna valori parametrilor de mod out şi
inout.
De exemplu: funcţia MIN returnează cel mai mic dintre cei doi
întregi pe care îi primeşte ca parametri. Partea sa declarativă (opţională) este
vidă. Se foloseşte instrucţiunea de selecţie simplă if … then… else… end if.
182 LIMBAJUL VHDL

function MIN (A, B: INTEGER) return INTEGER is


begin
if A < B then
return A;
else
return B;
end if;
end MIN;

Să examinăm în continuare procedura MIN. Partea declarativă a


rămas în continuare vidă:

-- Prima variantă a procedurii MIN


procedure MIN (A, B: in INTEGER; C: out INTEGER) is
begin
if A < B then
C := A;
else
C := B;
end if;
end MIN;
-- A doua variantă a procedurii MIN: se foloseşte în plus o
-- variabilă locală (numai din considerente demonstrative)
procedure MIN (A, B: in INTEGER; C: out INTEGER) is
variable VAR_LUCRU: INTEGER;
begin
if A < B then
VAR_LUCRU := A;
else
VAR_LUCRU := B;
end if;
C := VAR_LUCRU;
end MIN;

2.4 Apelul de sub-program

Deşi un sub-program este de natură secvenţială (instrucţiunile pe


care le conţine sunt executate secvenţial), apelul său se poate face în două
moduri:
a) ca o acţiune secvenţială;
b) ca o acţiune concurentă.
SUB-PROGRAME 183
Există două moduri de a indica parametrii unui sub-program în
momentul apelării sale: prin poziţie sau prin denumire.
Apelul prin poziţie constă în a enumera toţi parametrii, în ordine,
separaţi prin virgule. Corespondenţa dintre parametrii actuali şi parametrii
formali se efectuează în funcţie de această ordine.
Apelul prin denumire constă în a indica numele parametrului formal
înaintea fiecărui parametru actual (parametrul de apel). Corespondenţa este
atunci explicită şi se utilizează simbolul „=>”. În acest caz, ordinea
parametrilor nu mai este semnificativă. Apelul prin denumire se utilizează
pentru ameliorarea lizibilităţii programelor sau pentru a nu modifica decât
câţiva parametri, presupunându-se că ceilalţi au o valoare implicită.
Prezentăm în continuare câteva exemple: procedura şi respectiv
funcţia MIN va fi apelată în ambele moduri (prin poziţie şi prin denumire).

-- Procedura MIN
MIN (VAR, 5, REZULTAT); -- Apel poziţional
MIN (A => VAR, B => 5, C => REZULTAT); -- Apel prin denumire
MIN (B => 5, C => REZULTAT, A => VAR); -- Apel prin denumire

-- Funcţia MIN
REZULTAT := MIN(VAR, 5); -- Apel poziţional
REZULTAT := MIN(A => VAR, B => 5); -- Apel prin denumire

2.5 Supraîncărcarea

Două sub-programe se numesc supraîncărcate dacă au acelaşi nume,


însă profilurile lor diferă.
Profilul unui sub-program este un ansamblu de informaţii care
cuprinde numărul, ordinea şi tipul parametrilor formali, precum şi – în cazul
funcţiilor – tipul rezultatului returnat.
Supraîncărcarea a două sub-programe de profiluri diferite este
deosebit de utilă şi măreşte uşurinţa scrierii de cod sursă VHDL, asigurând
în acelaşi timp o foarte bună lizibilitate a programelor. Pentru a determina
sub-programul pe care trebuie efectiv să-l apeleze, compilatorul va alege
profilul care corespunde apelului. Dacă nici un profil nu corespunde sau
dacă există mai multe profiluri identice candidate, acest fapt va fi semnalat
ca eroare la compilare.
184 LIMBAJUL VHDL

Observaţie
Anumite informaţii conţinute în specificaţia unui sub-program, cum
ar fi numele parametrilor formali, modul lor de transmitere, clasa lor sau
valoarea lor implicită nu fac parte din profilul sub-programului. Două sub-
programe care nu diferă decât prin una sau mai multe dintre aceste
informaţii vor provoca o ambiguitate care va fi semnalată de către
compilator. Prin urmare, următoarele două funcţii nu se vor supraîncărca (se
va genera o eroare la compilare, deoarece simbolul MIN este definit de două
ori):

function MIN(A, B: INTEGER) return INTEGER;


function MIN(C, D: INTEGER) return INTEGER;

Iată câteva exemple de supraîncărcare:

-- Supraîncărcarea a trei proceduri


procedure CONVERSIE (INTRARE: in REAL, IEŞIRE: out INTEGER);
procedure CONVERSIE (INTRARE: in BIT, IEŞIRE: out INTEGER);
procedure CONVERSIE (INTRARE: in OCTET, IEŞIRE: out INTEGER);

-- Supraîncărcarea a trei funcţii


function MIN(A, B: INTEGER) return INTEGER;
function MIN(A: REAL, B: REAL) return REAL;
function MIN(A: in BIT, B: in BIT) return BIT;

Supraîncărcarea poate fi aplicată şi operatorilor. De exemplu, putem


supraîncărca operatorul logic and folosind următoarea funcţie:

function ”AND”(A,B: TIP_LOGIC) return BOOLEAN;

Această declaraţie va autoriza scrierea unor expresii precum:

A and B

unde A şi B sunt de tipul TIP_LOGIC.


Funcţia astfel definită trebuie să aibă respectiv unul singur sau doi
parametri, în funcţie de tipul operatorului: unar sau binar. Operatorii „+” şi
„-” pot fi supraîncărcaţi atât ca operatori binari cât şi ca operatori unari.
SUB-PROGRAME 185
Utilizarea acestei funcţii va putea întotdeauna să se efectueze sub forma sa
clasică:

”AND” (A, B) –- Nu trebuie uitate ghilimelele!

Supraîncărcarea operatorilor prezintă un interes deosebit pentru


simplificarea scrierii programelor care operează pe semnale de alte tipuri
decât tipul BIT.
Iată trei exemple de supraîncărcare a operatorului „+”:

function ”+” (A,B: BIT) return BIT;


function ”+” (A,B: BIT_VECTOR) return BIT_VECTOR;
function ”+” (A,B: IMPEDANŢĂ) return IMPEDANŢĂ;

2.6 Consideraţii suplimentare

2.6.1 Recursivitatea
În VHDL există posibilitatea de a scrie programe recursive. Într-un
limbaj de programare clasic, în general vorbind, recursivitatea reprezintă
proprietatea unui sub-program de a se putea apela pe el însuşi. În VHDL este
permisă şi recursivitatea încrucişată (sub-programul A apelează sub-
programul B, care la rândul său apelează sub-programul A). În cazul
aplicării recursivităţii, trebuie să ne asigurăm că am scris o instrucţiune de
test pentru stoparea sa.
Astfel, este posibil să descriem un sumator complet pe N biţi în
funcţie de el însuşi, instanţiat pe N-1 biţi, şi a unui modul de un singur bit
care va avea rolul de test de stopare a recursivităţii.
Trebuie însă să avem în vedere că, în VHDL, scopul final îl
reprezintă în general obţinerea unei structuri hardware. De aceea, este bine
să ne punem mereu întrebarea: la ce ne ajută acest gen de descriere? Va
exista oare o realitate materială (hardware) corespunzătoare? În acest caz,
răspunsul este din păcate negativ…

2.6.1 Vizibilitatea
În figura 10.2 sunt prezentate regulile de vizibilitate a sub-
programelor în VHDL:
186 LIMBAJUL VHDL

Apelant
(pachet, proces, bloc sau alt sub-program)

Procedura A
Declaraţii
globale Declaraţii locale
(locale procedurii A
apelantului) Procedura B

Declaraţii locale
procedurii B

Figura 10.2 Reguli de vizibilitate

Declaraţiile efectuate în interiorul unui sub-program nu sunt vizibile


(deci nu sunt accesibile) din exteriorul acestuia, însă sunt vizibile de către
procedurile înglobate. Săgeţile din figura 10.2 pornesc de la obiectul care
„vede” către obiectul „văzut”.

2.6.3 Logica sub-programelor


O procedură se execută până la end-ul său final.
O funcţie nu trebuie să ajungă niciodată la instrucţiunea sa end
finală, lucru care ar genera o eroare în cursul execuţiei. Ea trebuie să
întâlnească întotdeauna cuvântul cheie return şi să predea atunci controlul
(împreună cu valoarea pe care trebuie s-o returneze) apelantului.

3. Desfăşurarea lucrării

3.1 Se vor supraîncărca operatorii de adunare şi scădere definiţi pe


întregi pentru valori de tip STD_LOGIC şi
STD_LOGIC_VECTOR.
3.2 Se vor supraîncărca funcţiile MIN şi MAX din lucrare pentru
parametri de tip STD_LOGIC şi STD_LOGIC_VECTOR.
3.3 Se vor defini funcţiile de conversie între următoarele tipuri de
date: BIT_VECTOR şi INTEGER; BIT_VECTOR şi STRING.
3.4 Se va defini un sistem numeric care rezolvă ecuaţii de gradul 2
folosind proceduri.
LUCRAREA NR. 11
MODULE DE SIMULARE

1. Scopul lucrării

Lucrarea abordează problematica simulării în VHDL. Se prezintă


noţiunile fundamentale legate de simularea dispozitivelor numerice
hardware în general, după care se adaptează aceste informaţii în contextul
limbajului VHDL. Se definesc modulele de simulare în VHDL (contextul în
care se utilizează, elementele lor componente etc.), după care se detaliază
fiecare aspect, ilustrându-se prin exemple semnificative noţiunile noi
expuse.
Se evidenţiază importanţa generării rezultatelor simulării şi modurile
în care se pot obţine aceste rezultate în VHDL. Se insistă pe utilizarea
instrucţiunii assert, subliniindu-se universalitatea acesteia şi ilustrându-se
modul său de aplicare cu exemple instructive.

2. Consideraţii teoretice

2.1 Module de simulare

Procesul de proiectare a sistemelor hardware ar fi incomplet fără


verificarea proiectelor. La fel ca şi în cazul altor medii integrate de
dezvoltare, există mai multe modalităţi de verificare a unui proiect VHDL.
Însă dintre toate metodele, soluţia cea mai des utilizată o constituie modulele
de simulare (test benches).
Un modul de simulare este un mediu integrat, în care un proiect
(numit şi unitate supusă testării - UST) este verificat prin aplicarea unor
semnale numite stimuli şi observarea răspunsurilor generate de sistem. Cu
alte cuvinte, un modul de simulare se substituie mediului în care urmează să
se desfăşoare ciclul de viaţă al proiectului, astfel încât să putem observa şi
analiza comportamentului sistemului proiectat.
Un modul de simulare este alcătuit din următoarele elemente:
188 LIMBAJUL VHDL
- un soclu (socket) în care va fi plasat sistemul supus testării;
- un generator de stimuli (un subsistem care aplică stimuli
proiectului aflat în testare, fie generându-i intern, fie preluându-i
de la o sursă exterioară de semnal);
- instrumente de monitorizare a răspunsurilor la stimuli, generate
de către sistemul supus testării.
Noţiunea de module de simulare a fost adaptată şi pentru proiectele
scrise în VHDL. Adaptările se referă la următoarele aspecte: un modul de
simulare nu este un sistem de sine stătător, ci o specificaţie VHDL care va fi
simulată de către simulatorul integrat în sistemul de dezvoltare. Modulul de
simulare este alcătuit din:
- instanţierea unităţii supuse testării;
- procese „sensibile” la stimulii aplicaţi unităţii supuse testării.
În acest fel, se creează o specificaţie hibridă, care combină atât
instrucţiuni specifice tipului de descriere structurală cât şi instrucţiuni
specifice tipului de descriere comportamentală. O asemenea abordare este
validă în VHDL deoarece atât instanţierea componentelor cât şi procesele
reprezintă instrucţiuni concurente.
Stimulii destinaţi unităţii supuse testării (UST) sunt specificaţi în
interiorul arhitecturii modulului de simulare (test bench) sau pot fi citiţi
dintr-un fişier extern. Pe de altă parte, reacţiile unităţii testate pot fi
observate în mai multe feluri:
a) prin intermediul ieşirilor generate de simulatorul VHDL (adică a
formelor de undă care apar pe ecran);
b) prin intermediul fişierelor de raport conţinând mesaje generate de
simulator;
c) prin intermediul mesajelor generate de simulator la consolă;
d) prin intermediul scrierii în fişiere folosind operaţiile de intrări /
ieşiri în mod text care sunt disponibile în VHDL (în pachetul
standard TEXTIO).
Numărul mare de opţiuni puse la dispoziţie de limbajul VHDL oferă
o mare flexibilitate în vederea scrierii de module de simulare. „Reversul
medaliei” l-ar putea constitui însă scrierea de module de simulare cu o
complexitate mai mare chiar decât cea a unităţii testate...
MODULE DE SIMULARE 189

2.2 Elementele unui modul de simulare

Aşa cum am menţionat anterior, în VHDL un modul de simulare


constituie de fapt o specificaţie VHDL care dispune de propria sa entitate şi
arhitectură. Cu toate acestea, modulul de simulare are o structură specială
care conţine o serie de elemente caracteristice:
- entitatea modulului de simulare: aceasta nu are porturi;
- instanţierea componentei unităţii supuse testării (UST) –
relaţia dintre modulul de simulare şi unitatea supusă testării este
specificată prin intermediul instanţierii componentei şi al
specificaţiei de tip structural;
- stimulii – reprezintă un set de semnale care sunt declarate intern
în cadrul arhitecturii modulului de simulare şi care sunt asignate
porturilor unităţii testate la instanţierea acesteia. Stimulii sunt
definiţi ca forme de undă în cadrul unuia sau mai multor procese
comportamentale.
Exemplul următor ilustrează noţiunile expuse până în acest moment:

-- Entitatea modulului de simulare


entity MODUL_DE_SIMULARE is
end MODUL_DE_SIMULARE;
architecture ARH_MODUL_DE_SIMULARE of MODUL_DE_SIMULARE is
-- Declararea componentei
component POARTA_ŞI is
port (A, B: in BIT;
Y: out BIT);
end component;
-- Declararea stimulilor
signal A, B, C: BIT;
begin
-- Instanţierea unităţii supuse testării (UST)
UST: POARTA_ŞI port map (A, B, C);
-- Semnalele de stimulare
A <= '0', '1' after 20 ns, '0' after 40 ns;
B <= '1', '0' after 40 ns, '1' after 80 ns;
end ARH_MODUL_DE_SIMULARE;

În exemplul de mai jos este prezentată o generalizare a utilizării


modulelor de simulare în VHDL:
190 LIMBAJUL VHDL

-- Entitatea modulului de simulare


entity MODUL_DE_SIMULARE is
end MODUL_DE_SIMULARE;
architecture ARH_MODUL_DE_SIMULARE of MODUL_DE_SIMULARE is
-- Declararea componentei
component COMP is
port (...);
end component;
-- Declararea stimulilor
signal A, B, C...: BIT;
signal Z...
begin
-- Instanţierea unităţii supuse testării (UST)
UST: entity WORK.COMP(COMP_ARH) port map (...);
-- Definirea stimulilor
stimuli: process
begin
A <= ...;
B <= ...;
...
wait for...
...
wait;
end process stimuli;
end ARH_MODUL_DE_SIMULARE;

2.3 Utilizarea modulelor de simulare

De fiecare dată când creăm o nouă specificaţie VHDL, trebuie să


avem în vedere faptul, deosebit de important, că proiectul nostru va trebui să
fie (cât mai) uşor de verificat. Evident, fiecare proiect poate fi simulat, dar în
cazul proiectelor de dimensiuni foarte mari, procesul de simulare on-line se
poate dovedi extrem de costisitor din punct de vedere al timpului. Este mult
mai convenabil să utilizăm un modul de simulare pentru verificarea
proiectului. Întrucât scrierea unui modul de simulare se poate dovedi o
sarcină foarte complexă, este recomandabil să scriem (sub formă de
comentarii sau de notiţe separate) o serie de indicaţii generale referitoare la
modul viitor de dezvoltare al stimulilor, pe măsură ce procesul de proiectare
a sistemului avansează. Unii proiectanţi cu experienţă recomandă chiar ca să
se scrie fişiere de stimuli (sau vectori de test) complete în acest moment.
MODULE DE SIMULARE 191
După ce încheiem complet scrierea unităţii supuse testării şi a
modulului de simulare, putem demara verificarea. Trebuie să ne fie foarte
clar faptul că vom simula modulul de simulare şi NU unitatea supusă
testării: aceasta este doar una dintre componentele instanţiate în interiorul
modulului de simulare.
Nu există limitări în privinţa dimensiunii modulului de simulare:
singura limitare o constituie capacitatea şi performanţele simulatorului
VHDL.
Exemplul de mai jos ilustrează o posibilitate de descriere a unui
stimul „semnal de tact (CLOCK)”:

...
-- Constantă declarată în arhitectura modulului de simulare
constant CONSTANTA_TACT: TIME := 20 ns;
signal CLOCK: STD_LOGIC;
...
-- Perioada tactului va fi de: 2*CONSTANTA_TACT = 40 ns;
GENERATOR_TACT: process(CLOCK)
begin
if CLOCK = 'U' then CLOCK <= '0';
else
CLOCK <= not CLOCK after CONSTANTA_TACT;
end if;
end process GENERATOR_TACT;

Noţiunile expuse până în acest punct sunt reluate într-o formă grafică
în figura 11.1.

2.3.1 Entitatea modulului de simulare


Modulul de simulare arată la fel ca oricare altă specificaţie VHDL: el
este alcătuit dintr-o entitate şi o arhitectură. Există însă o deosebire majoră
faţă de celelalte situaţii: această entitate nu conţine nici declaraţii de porturi
şi nici declaraţii de parametri generici.
Motivul este evident: modulul de simulare nu este un dispozitiv real
sau un sistem care să fie nevoit să comunice cu mediul său înconjurător; prin
urmare, el nu are nevoie de intrări sau ieşiri. Toate valorile destinate
porturilor de intrare ale unităţii supuse testării (UST) sunt specificate în
interiorul arhitecturii modulului de simulare, ca stimuli. Ieşirile sunt
192 LIMBAJUL VHDL
monitorizate (de exemplu, cu ajutorul simulatorului) şi apoi pot fi eventual
salvate într-un fişier.

Procesul de proiectare este compus din


două etape: proiectarea şi verificarea.
Scopul proiectării îl constituie crearea
unei noi specificaţii VHDL care să
îndeplinească cerinţele sistemului.

PROIECTARE

Specificarea proiectului Scrierea stimulilor se poate face


concurent cu scrierea specificaţiilor
pentru fiecare nou bloc al proiectului.
Setul stimulilor trebuie să conţină valori
ale semnalelor de intrare şi de stare care
să acopere cât mai complet situaţia reală.

VERIFICARE
Specificarea modulului de simulare se
Definirea stimulilor poate face numai după specificarea
stimulilor. Acest modul conţine stimulii şi
o instanţă a sistemului proiectat (numit
UST – unitate supusă testării). În faza de
Specificarea modulului verificare se va simula acest modul şi NU
de simulare unitatea supusă testării.

Simularea modulului de
simulare

Simularea modulului de simulare reprezintă ultima fază a


procesului de proiectare, în care se poate vedea dacă sistemul
proiectat se comportă conform specificaţiei iniţiale.
Informaţiile obţinute în urma acestei simulări depind în mare
măsură de corectitudinea şi de gradul de acoperire a problemei
de către stimulii (sau vectorii de test) aplicaţi modulului de
simulare.

Figura 11.1 Procesul de proiectare şi verificare a proiectului în VHDL

De ce este oare nevoie de o entitate a modulului de simulare, când el


este practic numai o arhitectură? Răspunsul ne este dat de către regulile de
construcţie a specificaţiilor VHDL: nici o arhitectură nu poate fi specificată
fără entitatea sa – această regulă se aplică inclusiv modulelor de simulare.
În exemplul de mai jos prezentăm un modul de simulare care se
opreşte singur:
MODULE DE SIMULARE 193

-- Entitatea modulului de simulare


entity MS_BISTABIL_D is
end MS_BISTABIL_D;

architecture ARH_MS_BISTABIL_D of MS_BISTABIL_D is


-- Declararea componentei
component COMP_D is
port (...);
end component;
shared variable END_SIM: BOOLEAN := FALSE;
signal CLK, D, Q, NQ: BIT;
-- Perioada semnalului de tact generat
constant PERIOADA_CLK: TIME := 20 ns;

begin
-- Instanţierea unităţii supuse testării (UST)
UST: COMP_D port map (...);
-- Definirea stimulilor
GENERATOR_TACT: process
begin
if not END_SIM then
CLK <= '0';
wait for PERIOADA_CLK / 2;
CLK <= '1';
wait for PERIOADA_CLK / 2;
else wait;
end if;
end process GENERATOR_TACT;
-- Stimulul asignat porturilor de intrare ale COMP_D
STIMUL: process
begin
D <= '1';
wait for 200 ns;
D <= '0';
wait for 200 ns;
END_SIM := TRUE; -- când END_SIM devine TRUE, procesul
-- GENERATOR_TACT este oprit
wait;
end process STIMUL;
end ARH_MS_BISTABIL_D ;

2.3.2 Unitatea supusă testării


Un sistem care va fi verificat cu ajutorul unui modul de simulare nu
are nevoie de modificări sau declaraţii adiţionale. Din acest motiv, oricărei
specificaţii VHDL (chiar şi uneia provenită dintr-o sursă externă) i se poate
aplica un modul de simulare. O astfel de situaţie poate să survină atunci
194 LIMBAJUL VHDL
când este necesară efectuarea unei simulări de mare anvergură în care sunt
implicate mai multe dispozitive.
În mod obligatoriu, unitatea supusă testării trebuie să fie instanţiată
în interiorul arhitecturii modulului de simulare. Acest lucru poate fi realizat
în acelaşi mod ca în cazul oricărei specificaţii structurale, adică fie prin
instanţiere directă, fie prin instanţierea unei componente însoţită de
declaraţia componentei şi de configuraţie. Porturilor instanţei unităţii supuse
testării din cadrul modulului de simulare le vor fi asignate semnalele-stimul.
Întrucât atât procesele cât şi instrucţiunea de instanţiere a unei
componente reprezintă instrucţiuni concurente, nu are importanţă dacă
unitatea supusă testării va fi instanţiată prima şi stimulii vor fi definiţi mai
târziu, sau invers.
Exemplul de mai jos prezintă specificaţia unui demultiplexor 1 la 4
şi modulul de simulare asociat lui:

entity DMUX is
port(X: in BIT;
S: in BIT_VECTOR (1 downto 0);
Y: out BIT_VECTOR (3 downto 0));
end DMUX;
architecture ARH_DMUX of DMUX is
begin
Y <= ('0', '0', '0', X) when S = "00" else
('0', '0', X, '0') when S = "01" else
('0', X, '0', '0') when S = "10" else
(X, '0', '0', '0') when S = "11";
end ARH_DMUX;
-- Entitatea modulului de simulare
entity MS_DMUX is
end MS_DMUX;

architecture ARH_MS_DMUX of MS_DMUX is


-- Declararea componentei
component DMUX is
port(X: in BIT;
S: in BIT_VECTOR (1 downto 0);
Y: out BIT_VECTOR (3 downto 0));
end component DMUX;
signal X: BIT;
signal S: BIT_VECTOR (1 downto 0);
signal Y: BIT_VECTOR (3 downto 0);
MODULE DE SIMULARE 195
begin
-- Instanţierea unităţii supuse testării (UST)
UST: DMUX port map (X => X, S => S, Y => Y);
STIM: process
begin
X <= '1';
S <= "00";
wait for 40 ns;
S <= "01";
wait for 40 ns;
S <= "10";
wait for 40 ns;
S <= "11";
wait for 40 ns;
wait;
end process STIM;
end ARH_MS_DMUX ;

2.3.3 Stimuli
Un element esenţial al oricărui modul de simulare îl reprezintă setul
de stimuli: o secvenţă de valori aplicate în timp fiecărui semnal de intrare al
unităţii supuse testării. De vreme ce modulul de simulare nu comunică cu
mediul său înconjurător prin semnale, toţi stimulii trebuie să fie declaraţi
intern în arhitectura modulului de simulare. Ei vor fi declaraţi la fel ca
oricare alt semnal, în zona declarativă a arhitecturii.
Stimulii pot fi specificaţi astfel:
- ca instrucţiuni concurente de asignare de valori unor semnale (în
care modificările survenite pe semnalele de intrare sunt date ca
forme de undă, cu ajutorul cuvântului cheie after şi alegând
modelul de propagare dorit, transport sau inerţial);
- în interiorul unui proces care conţine instrucţiuni de asignare de
valori unor semnale separate prin intermediul instrucţiunilor wait
for (care introduc întârzieri între execuţia instrucţiunilor de
asignare menţionate mai sus). În acest caz, este necesară
adăugarea unei instrucţiuni wait vide (fără nici o condiţie) pe
ultima poziţie a listei de instrucţiuni secvenţiale executate în
cadrul procesului. Această instrucţiune va suspenda procesul pe
termen nelimitat (altminteri - aşa cum am arătat în lucrarea nr. 6
dedicată proceselor - procesul ar fi executat din nou de la
început).
196 LIMBAJUL VHDL
Relaţia dintre stimuli şi unitatea supusă testării este specificată prin
intermediul asignărilor realizate în cadrul clauzei port map a instrucţiunii
de instanţiere a unităţii testate.
Se prezintă în continuare exemplul unui modul de simulare al unui
numărător pe 8 biţi, în care stimulii sunt specificaţi atât în formă secvenţială
cât şi în formă concurentă:

-- Entitatea modulului de simulare


entity MS_NUMĂRĂTOR_8 is
end MS_NUMĂRĂTOR_8;
architecture ARH_MS_NUMĂRĂTOR_8 of MS_NUMĂRĂTOR_8 is
-- Declaraţia componentei
component NUMĂRĂTOR_8 is
port (CLK, RESET: in STD_LOGIC;
CE: in STD_LOGIC; -- Clock enable
LOAD: in STD_LOGIC; -- Încărcare
paralelă
DIR: in STD_LOGIC; -- Numărare sus /
jos
DIN: in INTEGER range 0 to 255; -- Intrări de date
COUNT: out INTEGER range 0 to 255-- Ieşiri
);
end component NUMĂRĂTOR_8;
signal CLK, RESET, CE, LOAD, DIR: STD_LOGIC;
signal DIN: INTEGER range 0 to 255;
signal COUNT: INTEGER range 0 to 255;
begin
-- Instanţierea unităţii supuse testării (UST)
UST: NUMĂRĂTOR_8 port map (...);
process(CLK)
begin
if CLK = 'U' then CLK <= '0';
else CLK <= not CLK after 15 ns;
end if;
end process;
RESET <= '1','0' after 15 ns; -- Iniţializare, apoi
-- funcţionare în regim normal
DIN <= 250; -- Intrările de date
STIM1: process
begin
LOAD <= '0';
wait for 5 us;
LOAD <= '1'; -- Se încarcă starea 250
wait for 100 ns;
LOAD <= '0';
wait;
end process STIM1;
MODULE DE SIMULARE 197

STIM2: process
begin
DIR <= '1'; -- Numărare jos
CE <= '1'; -- Clock Enable permite funcţionarea
wait for 2 us;
DIR <= '0'; -- Numărare sus
wait for 1 us;
CE <= '0'; -- Clock Enable nu permite funcţionarea
DIR <= '1'; -- Numărare jos
wait for 1 us;
DIR <= '0'; -- Se reia Numărarea crescătoare
wait;
end process STIM2;
end ARH_MS_NUMĂRĂTOR_8;

2.3.4 Instrucţiunea assert


Ultimul element al oricărei verificări încununate de succes îl
reprezintă afişarea sau raportarea rezultatelor simulării. Acest lucru poate fi
realizat în mai multe moduri: prin afişarea listei valorilor semnalelor care se
modifică în timp (echivalentă cu afişarea formelor de undă), prin scrierea
rezultatelor simulării într-un fişier „jurnal al simulării” (log file) sau prin
utilizarea instrucţiunii VHDL assert.
Ultima opţiune este uşor de aplicat şi este folosită adeseori pentru
afişarea unui mesaj atunci când se detectează o eroare în funcţionarea
sistemului proiectat.
Atât instrucţiunea concurentă assert, cât şi instrucţiunea secvenţială
assert, au fost deja prezentate în detaliu în cadrul lucrărilor nr. 7, respectiv 9.
Instrucţiunea assert se întrebuinţează cel mai adesea, în cadrul
modulelor de simulare, pentru raportarea răspunsurilor eronate generate de
unitatea supusă testării la recepţionarea stimulilor definiţi. Iată câteva
moduri posibile (cele mai frecvent întâlnite) de utilizare a acestei
instrucţiuni:
- se aplică o instrucţiune assert de fiecare dată când se aşteaptă o
nouă valoare a unui semnal de ieşire al unităţii supuse testării;
- valoarea prognozată se specifică drept condiţie în cadrul
instrucţiunii assert;
- se vor folosi mesaje de eroare cât mai precise şi detaliate: un
simplu mesaj de genul „Eroare!” nu va oferi prea multe
informaţii cu privire la ce nu funcţionează corect în simulare...
198 LIMBAJUL VHDL
Trebuie scris CE nu funcţionează şi CÂND a avut loc respectivul
eveniment (de exemplu, în ce condiţii de intrare).
Reamintim că noile valori vor fi asignate semnalelor numai în
momentul suspendării proceselor; ne vom aştepta să obţinem nişte rezultate
ale simulării corelate cu acest aspect.
Exemplul unui multiplexor 2:1, de semnale pe 2 biţi va ilustra modul
de utilizare a instrucţiunii assert pentru generarea rezultatelor simulării, în
cadrul proceselor din interiorul arhitecturii modulului de simulare:

-- Entitatea Multiplexor 2:1 de semnale pe 2 biţi

entity MUX2_LA_1_PE_2_BIŢI is

generic(MUX_DELAY: TIME := 5 ns);

port(A, B: in STD_LOGIC_VECTOR(1 downto 0);


SEL: in STD_LOGIC;
Y: out STD_LOGIC_VECTOR(1 downto 0));
end entity MUX2_LA_1_PE_2_BIŢI;

architecture ARH of MUX2_LA_1_PE_2_BIŢI is

begin
with SEL select
Y <= A after MUX_DELAY when '0',
B after MUX_DELAY when '1',
"XX" when others;
end architecture ARH;

entity MS is
end entity MS;

architecture ARH_MS of MS is

signal A, B, Y: STD_LOGIC_VECTOR(1 downto 0);


signal SEL: STD_LOGIC;

component MUX2_LA_1_PE_2_BIŢI is

generic(MUX_DELAY: TIME := 5 ns);

port(A, B: in STD_LOGIC_VECTOR(1 downto 0);


SEL: in STD_LOGIC;
Y: out STD_LOGIC_VECTOR(1 downto 0));
end component;
MODULE DE SIMULARE 199

begin

UST: entity MUX2_LA_1_PE_2_BIŢI port map (A, B, SEL, Y);

STIMULI: process
begin
SEL <= 'X';
A <= "00";
B <= "11";
wait for 0 ns;
assert (Y = "XX") report "Testul a eşuat pt. SEL = X";
SEL <= '0';
wait for 40 ns;
assert (Y = "00") report "Testul a eşuat pt. SEL = 0";
A <= "01";
wait for 20 ns;
assert (Y = "01") report "Testul a eşuat pt. SEL = 0 -
Y nu s-a modificat";
SEL <= '1';
wait for 20 ns;
assert (Y = "11") report "Testul a eşuat pt. SEL = 1";
B <= "10";
wait for 20 ns;
assert (Y = "10") report "Testul a eşuat pt. SEL = 1 -
Y nu s-a modificat";
...
end process STIMULI;

end architecture ARH_MS;

3. Desfăşurarea lucrării
3.1 Se vor testa şi implementa toate exemplele de module de
simulare din lucrare.
3.2 Se vor determina stimulii de test optimi pentru exemplele din
această lucrare.
3.3 Se vor crea module de simulare, inclusiv alegerea setului adecvat
de stimuli de test, pentru exemplele din lucrările nr. 6 şi 7.
3.4 Pentru exemplul de mai jos, se va crea un modul de simulare şi
se vor alege vectorii de test cei mai adecvaţi:
200 LIMBAJUL VHDL
library IEEE;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_1164.all;

entity COUNTER is
port( ZERO : out STD_LOGIC;
CLK, LOAD_COUNT, RESET : in STD_LOGIC;
COUNT : in STD_LOGIC_VECTOR(3 downto 0); );
end COUNTER;

architecture COUNTER of COUNTER is

signal Q : STD_LOGIC_VECTOR(3 downto 0);


signal EMPTY : STD_LOGIC;

begin

process (CLK, RESET)


begin
if RESET='1' then
Q <= (others => '0');
EMPTY <= '0';
else
if CLK'EVENT and CLK='1' then
if LOAD_COUNT = '1' then
Q <= COUNT;
EMPTY <= '0';
else
if Q = 0 then
EMPTY <= '1';
else
Q <= Q - 1;
end if;
end if;
end if;
end if;
end process;

ZERO <= EMPTY;

end COUNTER;
LUCRAREA NR. 12
PACHETE STANDARD ŞI PREDEFINITE

1. Scopul lucrării
Lucrarea prezintă rolul pachetelor predefinite şi standardizate în
cadrul proiectării sistemelor hardware în VHDL. Se studiază pachetele
STANDARD, TEXTIO şi STD_LOGIC_1164, după care sunt trecute în
revistă pachetele aritmetice şi logice ale principalelor firme producătoare de
instrumente integrate de proiectare cu VHDL.

2. Consideraţii teoretice

2.1 Generalităţi

Pentru a se evita complicarea inutilă a limbajului VHDL şi pentru a i


se păstra supleţea, există un anumit număr de pachete de importanţă majoră
care au fost normalizate. Acestea sunt pachetele standard.
Pachetele standard nu pot fi modificate de către proiectant. Două
dintre aceste pachete (STANDARD şi TEXTIO) sunt definite în manualul
de referinţă al VHDL.
Pachetul STD_LOGIC_1164 nu face parte din mediul standard al
VHDL definit în manualul de referinţă. El constituie o normă IEEE de sine
stătătoare; a fost normalizat pentru a facilita portabilitatea fişierelor care
conţin cod sursă VHDL sintetizabil. Acesta este primul pachet care
urmăreşte acoperirea lipsurilor existente la nivelul semanticii VHDL pentru
sinteză. Acţiunea sa este completată de alte pachete, împreună cu care
alcătuieşte un mediu normalizat pentru sinteză: NUMERIC_STD şi
NUMERIC_BIT (care conţin definirea unor funcţii aritmetice pe tipul BIT
finit) sau pachetele VITAL (prin intermediul cărora devine posibilă luarea în
considerare a unor valori temporale precise). Cel mai utilizat pachet numeric
este, la ora actuală, pachetul STD_LOGIC_ARITH realizat de firma
SYNOPSYS, acest pachet devenind un standard de facto, întrucât este
202 LIMBAJUL VHDL
propus şi vândut de către actualul lider al pieţei mondiale în materie de
instrumente de dezvoltare bazate pe VHDL.

2.2 Pachetul STANDARD

Pachetul STANDARD reuneşte declaraţii de tipuri, de sub-tipuri şi


de funcţii extrem de utile (chiar indispensabile) pentru proiectare. De
exemplu, acest pachet conţine definiţia tipului BIT.
La începutul fiecărei unităţi de concepţie există o clauză use
implicită (pe care proiectantul nu trebuie neapărat s-o scrie). Aşadar, acest
pachet este accesibil de oriunde (din orice unitate de proiectare) în VHDL.
Alcătuirea sa este următoarea:
- definiţia tipului BOOLEAN;
- definiţia tipului BIT;
- definiţia tipului CHARACTER;
- definiţia tipului SEVERITY_LEVEL (pentru instrucţiunile
assert);
- definiţia tipului INTEGER (capetele intervalului de definiţie
depind de implementare);
- definiţia tipului REAL (capetele intervalului de definiţie depind
de implementare);
- definiţia tipului fizic TIME (timpul de simulare);
- specificaţia funcţiei NOW care returnează timpul curent al
simulării (de tip TIME);
- definiţia sub-tipului întreg NATURAL (cu valori cuprinse între 0
şi limita superioară a intervalului de definiţie a tipului
INTEGER);
- definiţia sub-tipului întreg POSITIVE (cu valori cuprinse între 1
şi limita superioară a intervalului de definiţie a tipului
INTEGER);
- definiţia tipului STRING ca vector neconstrâns de caractere (de
tipul CHARACTER);
- definiţia tipului BIT_VECTOR ca vector neconstrâns de biţi.
Specificaţia pachetului STANDARD este dată în continuare:
PACHETE STANDARD ŞI PREDEFINITE 203

package STANDARD is
-- Tipuri enumerate predefinite
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,
’’, ’!’, ’”’, ’#’, ’$’, ’%’, ’&’, ’’’,
’(’, ’)’, ’*’, ’+’, ’,’, ’-’, ’.’, ’/’,
’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,
’8’, ’9’, ’:’, ’;’, ’<’, ’=’, ’>’, ’?’,
’@’, ’A’, ’B’, ’C’, ’D’, ’E’, ’F’, ’G’,
’H’, ’I’, ’J’, ’K’, ’L’, ’M’, ’N’, ’O’,
’P’, ’Q’, ’R’, ’S’, ’T’, ’U’, ’V’, ’W’,
’X’, ’Y’, ’Z’, ’[’, ’\’, ’]’, ’^’, ’_’,
’`’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’, ’g’,
’h’, ’i’, ’j’, ’k’, ’l’, ’m’, ’n’, ’o’,
’p’, ’q’, ’r’, ’s’, ’t’, ’u’, ’v’, ’w’,
’x’, ’y’, ’z’, ’{’, ’|’, ’}’, ’~’, ’DEL’);
type SEVERITY_LEVEL is (NOTE, WARNING, ERROR, FAILURE);
-- Tipuri numerice predefinite
type INTEGER is range –2_147_483_648 to –2_147_483_647;
type REAL is range –16#0.7FFFFF8#E+32 to 16#0.7FFFFF8#E+32;
-- Tipul TIME predefinit
type TIME is range –9_223_372_036_854_775_808
to 9_223_372_036_854_775_807; -- pentru implementarea
-- pe 64 de biţi
units fs; -- femtosecundă
ps = 1000 fs; -- picosecundă
ns = 1000 ps; -- nanosecundă
us = 1000 ns; -- microsecundă
ms = 1000 us; -- milisecundă
sec = 1000 ms; -- secundă
min = 60 sec; -- minut
hr = 60 min; -- oră
end units;
-- Funcţia care returnează timpul curent al simulării
function NOW return TIME;
-- Sub-tipuri numerice predefinite
subtype NATURAL is INTEGER range 0 to INTEGER’HIGH;
subtype POSITIVE is INTEGER range 1 to INTEGER’HIGH;
-- Tipuri tablou predefinite
type STRING is array (POSITIVE range <>) of CHARACTER;
type BIT_VECTOR is array (NATURAL range <>) of BIT;
end STANDARD;
204 LIMBAJUL VHDL

2.3 Pachetul TEXTIO

2.3.1 Prezentare generală


Acest pachet este, din păcate, unul din punctele slabe ale VHDL.
Rolul său este de a furniza primitivele de intrare / ieşire ASCII ale VHDL.
Aşadar, el joacă un rol foarte important şi este utilizat atât pentru
interacţiunea utilizator-consolă (de exemplu, introducerea de parametri) cât
şi pentru scrierea şi citirea fişierelor (de exemplu, citirea conţinutului unui
modul de memorie ROM).
Felul în care este scris aminteşte de intrările şi ieşirile din limbajul
PASCAL; însă aspectul cel mai grav îl constituie faptul că specificaţia
pachetului TEXTIO nu respectă regulile generale VHDL. Orice tentativă de
a compila acest pachet este sortită eşecului (de exemplu, în funcţia
ENDLINE se găseşte un parametru de tip acces, ceea ce este interzis în
VHDL, după cum am arătat în cadrul lucrării nr. 10 dedicată sub-
programelor).
Constructorii de instrumente VHDL au încercat să remedieze această
lipsă de omogenitate, realizând fiecare propriul pachet TEXTIO. A rezultat
o lipsă evidentă de portabilitate.
În cazul acestui pachet, clauza use nu mai este implicită (la fel ca în
cazul pachetului STANDARD), deci pachetul TEXTIO va trebui referit în
mod explicit dacă se intenţionează realizarea de operaţii de intrare / ieşire.
Alcătuirea acestui pachet este următoarea:
- Definiţia tipului LINE. Acest tip va reprezenta linia de fişier
manipulată. El este definit ca un acces la tipul STRING, adică la
un vector neconstrâns de caractere;
- Definiţia tipului TEXT. Este tipul fişierelor de şiruri de caractere
care vor fi manipulate de către acest pachet;
- Definiţia tipului SIDE. Mai puţin important, acest tip enumerat
permite scrierea aliniată la stânga sau la dreapta;
- Definiţia tipului WIDTH. Este un tip de date utilizat pentru
lungimea şirurilor de caractere; este de fapt o redenumire a
tipului NATURAL;
- Definiţia fişierelor de text standard. În funcţie de implementare,
aici se vor găsi corespondenţele dintre fişierele INPUT şi
OUTPUT şi respectiv intrarea de la tastatură şi ieşirea la consolă
PACHETE STANDARD ŞI PREDEFINITE 205
a sistemului. Operaţiile de intrare / ieşire efectuate asupra
fişierelor INPUT şi OUTPUT constituie aşa-numitul „dialog
consolă”;
- Definiţia primitivelor de citire. Primitiva READLINE efectuează
citirea unei linii întregi dintr-un fişier dat. Această linie este
returnată ca parametru de ieşire al acestei proceduri. Pe această
linie vor opera procedurile READ;
- Cele 16 proceduri de citire (READ) primesc ca intrare linia dată,
extrag din ea (dacă este posibil) valoarea de tipul cerut şi
returnează restul liniei, valoarea citită şi, în cazul a 8 dintre ele, o
variabilă booleană care arată dacă operaţia a fost efectuată cu
succes;
- Cele 8 tipuri de date care pot constitui obiectul unei operaţii de
citire sunt: BIT, BIT_VECTOR, BOOLEAN, CHARACTER,
INTEGER, REAL, STRING şi TIME;
- Definiţia primitivelor de scriere. Procedura WRITELINE
efectuează scrierea într-un fişier a unei linii date ca parametru de
intrare. Pentru utilizarea acestei primitive, trebuie ca în prealabil
această linie să fie construită prin primitive de citire sau prin mai
multe apeluri de proceduri WRITE. Există 8 proceduri WRITE,
fiecare dintre ele efectuând scrierea într-o linie a unuia dintre
cele 8 tipuri menţionate mai sus. Această linie este transmisă în
mod inout; prin urmare, mai multe proceduri WRITE pot
completa o aceeaşi linie înainte de scrierea sa cu ajutorul
procedurii WRITELINE;
- Definiţia primitivelor de sfârşit de linie şi de sfârşit de fişier. O
funcţie ENDLINE primeşte ca intrare o linie şi returnează
valoarea booleană TRUE dacă s-a ajuns la sfârşitul liniei. În
anumite implementări, această funcţie este transformată în
procedură pentru a i se permite să se încadreze în sintaxa şi
semantica generală VHDL (parametrul LINE al acestei funcţii
este de tip acces, ceea ce este interzis în VHDL);
- Funcţia ENDFILE este definită pe toate tipurile fişier şi deci şi pe
tipul TEXT. Ea returnează valoarea booleană TRUE dacă s-a
ajuns la sfârşitul fişierului.
În VHDL’87 nu existau primitive de creare, de deschidere sau de
închidere a fişierelor. Deschiderea fişierului (sau crearea lui, dacă încă nu
206 LIMBAJUL VHDL
exista) era efectuată la declararea tipului fişier. Închiderile se efectuau
implicit ori de câte ori era necesar: la fiecare ieşire din blocul în care
variabila era vizibilă, fişierul era închis. O dată cu apariţia VHDL’93, aceste
constrângeri au fost anulate: se presupune că utilizatorul este suficient de
matur pentru a-şi gestiona singur acest nou tip de comunicare. Remarcăm
deci apariţia unei noi modalităţi de comunicare între procese: prin fişiere, nu
doar prin semnale.

Observaţie
Rolul acestor constrângeri în VHDL’87 era acela de a evita
comunicaţia între procese prin intermediul fişierelor. Asemenea manipulări
ar fi generat un cod VHDL dependent de implementare, deci ne-portabil. În
VHDL’87 era imposibil să scriem într-un fişier din interiorul unui proces,
să-l închidem şi apoi să-l redeschidem din interiorul altui proces.
Începând cu apariţia VHDL’93, aceste lucruri devin posibile şi prin
urmare au fost introduse 2 noi primitive: FILE_OPEN şi FILE_CLOSE, care
sunt declarate în mod automat cu tipul TEXT. Alte îmbunătăţiri aduse de
VHDL’93 sunt: trecerea parametrilor procedurilor READLINE şi
WRITELINE în mod inout şi declararea explicită a funcţiei ENDFILE ca
funcţie impură.

2.3.2 Specificaţia pachetului TEXTIO


package TEXTIO is
-- Câteva definiţii de tipuri utile
type LINE is access STRING;
type TEXT is file of STRING;
type SIDE is (RIGHT, LEFT);
subtype WIDTH is NATURAL;

-- Definiţia fişierelor de text standard („dialog consolă”)


file INPUT: TEXT is in "STD_INPUT";
file OUTPUT: TEXT is out "STD_OUTPUT";

-- Primitive de citire a tipurilor standard


procedure READLINE (variable F: in TEXT; L: out LINE);

procedure READ(L: inout LINE; VALUE: out BIT; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out BIT);

procedure READ(L: inout LINE; VALUE: out BIT_VECTOR; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out BIT_VECTOR);

procedure READ(L: inout LINE; VALUE: out BOOLEAN; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out BOOLEAN);
PACHETE STANDARD ŞI PREDEFINITE 207

procedure READ(L: inout LINE; VALUE: out CHARACTER; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out CHARACTER);

procedure READ(L: inout LINE; VALUE: out INTEGER; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out INTEGER);

procedure READ(L: inout LINE; VALUE: out REAL; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out REAL);

procedure READ(L: inout LINE; VALUE: out STRING; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out STRING);

procedure READ(L: inout LINE; VALUE: out TIME; GOOD: out BOOLEAN);
procedure READ(L: inout LINE; VALUE: out TIME);

-- Primitive de scriere a tipurilor standard


procedure WRITELINE (F: out TEXT; L: in LINE);
procedure WRITE(L: inout LINE; VALUE: in BIT; JUSTIFIED: in SIDE :=
RIGHT; FIELD: in WIDTH := 0);
procedure WRITE(L: inout LINE; VALUE: in BIT_VECTOR; JUSTIFIED: in
SIDE := RIGHT; FIELD: in WIDTH := 0);
procedure WRITE(L: inout LINE; VALUE: in BOOLEAN; JUSTIFIED: in
SIDE := RIGHT; FIELD: in WIDTH := 0);
procedure WRITE(L: inout LINE; VALUE: in CHARACTER; JUSTIFIED: in
SIDE := RIGHT; FIELD: in WIDTH := 0);
procedure WRITE(L: inout LINE; VALUE: in INTEGER; JUSTIFIED: in
SIDE := RIGHT; FIELD: in WIDTH := 0);
procedure WRITE(L: inout LINE; VALUE: in REAL; JUSTIFIED: in SIDE :=
RIGHT; FIELD: in WIDTH := 0; DIGITS: in NATURAL := 0);
procedure WRITE(L: inout LINE; VALUE: in STRING; JUSTIFIED: in SIDE :=
RIGHT; FIELD: in WIDTH := 0);
procedure WRITE(L: inout LINE; VALUE: in TIME; JUSTIFIED: in SIDE :=
RIGHT; FIELD: in WIDTH := 0; UNIT: in TIME := NS);

-- Primitive de sfârşit de linie şi de sfârşit de fişier


function ENDLINE(L: in LINE) return BOOLEAN;
function ENDFILE(F: in TEXT) return BOOLEAN; --Opţională (nu apare în
--toate implementările)
end TEXTIO;

2.3.3 Exemple de utilizare


Prima problemă cu care se confruntă proiectantul este, de regulă,
următoarea: linia de cod de mai jos este sistematic respinsă de compilator,
deoarece notaţia ”” desemnează atât valori, cât şi tipuri BIT_VECTOR şi
şiruri de caractere.

WRITE (linie, ”şir de caractere”);


208 LIMBAJUL VHDL
Această ambiguitate nu poate fi rezolvată decât de către compilator;
prin urmare, proiectantul este cel care trebuie să utilizeze expresii calificate,
ca în exemplul de mai jos:

WRITE (linie, STRING’(”şir de caractere”));

Iată în continuare un mic exemplu de pachet care utilizează pachetul


TEXTIO. Singura procedură din componenţa sa efectuează achiziţia de la
consolă a două valori: una de tip TIME şi alta de tip BIT. Acestea sunt
returnate apelantului, însă numai după ce se trasează achiziţia lor prin
scrierea într-un fişier. Pentru simplificare, nu se va face nici o verificare la
achiziţie: presupunem că operatorul va furniza întotdeauna valoarea
adecvată la momentul potrivit.

package EXEMPLU is
procedure ACHIZIŢIE-TRAS(DATĂ: out TIME; VALOARE: out BIT);
end EXEMPLU;

use STD.TEXTIO.all; --Referire la pachetul TEXTIO

package body EXEMPLU is


file FIŞIER: TEXT is out ”TRACE.VHDL”; -- Fişierul de trasare
procedure ACHIZIŢIE-TRAS(DATĂ: out TIME; VALOARE: out BIT) is
--Variabile de lucru
variable LINIE: LINE;
variable DATA: TIME;
variable VALOAREA: BIT;
begin
--Achiziţia unei perechi dată / valoare
READLINE(INPUT, LINIE);
READ(LINIE, DATA);
READ(LINIE, VALOAREA);
--Resetare la valoarea null a variabilei de lucru de tip LINE
LINIE:= null;
--Scriere în fişierul de trasare
WRITE(LINIE, STRING'(”Perechea dată / valoare introdusă: ”));
WRITE(LINIE, DATA);
WRITE(LINIE, STRING'(” ”));
WRITE(LINIE, VALOAREA);
WRITELINE(FIŞIER, LINIE);
--Actualizarea parametrilor de retur
DATĂ:= DATA;
VALOARE:= VALOAREA;
end ACHIZIŢIE-TRAS;
end EXEMPLU;
PACHETE STANDARD ŞI PREDEFINITE 209
La citire, linia este constituită din două elemente: o valoare de tip
TIME şi o valoare de tip BIT. Utilizatorul va introduce deci, de exemplu:

10 ns 1

şi apoi această linie, citită de procedura READLINE, va fi descompusă (prin


intermediul celor două proceduri READ) într-o dată (10 ns) şi o valoare de
bit (’1’).
La scriere, linia este alcătuită din patru elemente. Cu ocazia celor
patru apeluri ale procedurii WRITE, se scrie succesiv: un şir de caractere
(”Perechea dată / valoare introdusă: ”), o dată de tip TIME (aici,
10 ns), un şir de caractere care conţine două spaţii (” ”) şi o valoare de tip
BIT (aici, ’1’). O dată construită, această linie este scrisă în fişierul FIŞIER
cunoscut în sistemul de operare sub numele de TRACE.VHDL, cu ajutorul
procedurii WRITELINE.

Observaţii
Două apeluri succesive ale procedurii ACHIZIŢIE_TRAS provoacă
scrierea celei de-a doua trasări în continuarea celei dintâi, în fişierul FIŞIER.
Dacă declaraţia acestui fişier s-ar fi efectuat în partea declarativă a
procedurii (şi nu, ca în exemplul de mai sus, în zona declarativă a
pachetului), atunci fişierul ar fi fost redeschis la fiecare apel şi nu s-ar fi
păstrat decât ultima trasare!
Pentru a scrie o procedură de achiziţie mai realistă, ar fi preferabil să
utilizăm procedurile READ care returnează o valoare booleană de control
(ultimul parametru). Testarea acestui parametru ar permite detectarea
erorilor (introducerea unei valori de alt tip decât cel aşteptat) şi tratarea lor
(de exemplu, prin declanşarea unei noi achiziţii).

2.4 Pachetul STD_LOGIC_1164

2.4.1 Interpretarea tipului logic: semantica pentru simulare


Acest pachet defineşte logica cu nouă stări; el trebuie să facă parte
din biblioteca IEEE. Aşadar, toate unităţile de compilare care fac referire la
acest pachet trebuie să înceapă cu următoarele clauze library şi use:
210 LIMBAJUL VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.all;

Ansamblul pachetului este bazat pe definiţia tipului enumerat numit


STD_ULOGIC (sau STD_LOGIC, atunci când este rezolvat). Cele nouă
stări au fost deja prezentate în cadrul lucrării nr. 8 referitoare la semnale
multi-sursă; ele sunt următoarele:

type STD_ULOGIC is ( ’U’, --Neiniţializat


’X’, --Necunoscut tare
’0’, --0 tare
’1’, --1 tare
’Z’, --Înaltă impedanţă
’W’, --Necunoscut slab
’L’, --0 slab
’H’, --1 slab
’-’, --Fără importanţă
);

Valoarea ’U’ fiind prima din tipul enumerat STD_ULOGIC, ea va fi


valoarea luată implicit pentru toate obiectele declarate de acest tip (’U’ vine
de la „Uninitialized”). Semantica sa este simplă: dacă această valoare apare
în timpul unei simulări, înseamnă că obiectul respectiv nu a fost iniţializat
sau cu alte cuvinte nu i s-a asignat nici o valoare.
Semantica valorii ’X’ este asemănătoare: ea arată că este imposibil
să se definească o valoare ’0’ sau ’1’ sau ’Z’ (de exemplu ca urmare a unui
conflict între mai multe valori asignate unui semnal multi-sursă). Singura
informaţie disponibilă este că obiectul a fost iniţializat.
Valorile ’0’, ’1’ şi ’X’ dispun de valori asociate numite ”slabe”:
respectiv, ’L’, ’H’ şi ’W’. Scopul acestei duplicări este acela de a lua în
considerare niveluri electrice mai fine decât cele necesare reprezentării lui
’0’ şi ’1’ logic. De exemplu, o magistrală cu o rezistenţă „pull-up”1 va avea,
în starea de repaus, valoarea ’H’. Singura semantică oficială este de fapt cea
rezumată în tabelul de mai jos (care este de fapt, în acelaşi timp, şi funcţia

1
Se spune despre un semnal care are o rezistenţă legată la borna de alimentare de potenţial
pozitiv. În consecinţă, în absenţa oricărei comenzi (de exemplu, în cazul unei intrări
neconectate), semnalul ia valoarea logică ’1’.
PACHETE STANDARD ŞI PREDEFINITE 211
de rezoluţie a semnalelor de acest tip STD_LOGIC, deja prezentată în cadrul
lucrării nr. 8 referitoare la domeniul concurent în VHDL).

------------------------------------------------------------
| U X 0 1 Z W L H - | |
------------------------------------------------------------
('U', 'U', 'U', 'U', 'U', 'U', 'U', 'U', 'U'),-- | U |
('U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'),-- | X |
('U', 'X', '0', 'X', '0', '0', '0', '0', 'X'),-- | 0 |
('U', 'X', 'X', '1', '1', '1', '1', '1', 'X'),-- | 1 |
('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', 'X'),-- | Z |
('U', 'X', '0', '1', 'W', 'W', 'W', 'W', 'X'),-- | W |
('U', 'X', '0', '1', 'L', 'W', 'L', 'W', 'X'),-- | L |
('U', 'X', '0', '1', 'H', 'W', 'W', 'H', 'X'),-- | H |
('U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'),-- | - |

Observaţii
’X’, ’W’ şi ’U’ se numesc valori meta-logice şi natura lor este
diferită de cea a celorlalte semnale. Proiectantul trebuie să manifeste
prudenţă în cazul utilizării lor. De exemplu, rezultatul unei simple
comparaţii poate fi eronat: expresia ’U’ < ’0’ este întotdeauna adevărată
(conform semanticii de simulare), datorită definiţiei tipului enumerat, însă
această expresie nu are un sens real.
Valoarea ’-’ („fără importanţă”) nu are nici o semnificaţie pentru
simulare şi ea nu trebuie să apară niciodată în urma rezultatelor unei
simulări. Utilizarea sa este rezervată sintezei: ea le oferă instrumentelor de
sinteză posibilitatea de a codifica această valoare în ’0’ sau ’1’, în funcţie de
optimizările urmărite.

2.4.2 Descrierea pachetului STD_LOGIC_1164


Acest pachet exportă tipul enumerat cu nouă stări, precum şi vectorii
şi sub-tipurile derivate, însoţite de o funcţie de rezoluţie. Operatorii logici
sunt supraîncărcaţi şi se exportă de asemenea şi funcţii de conversie de tip.
Declaraţia acestui pachet este următoarea:
212 LIMBAJUL VHDL
package STD_LOGIC_1164 is

--Declaraţia tipului enumerat, fundamentul logicii cu nouă stări


type STD_ULOGIC is ( ’U’, --Neiniţializat
’X’, --Necunoscut tare
’0’, --0 tare
’1’, --1 tare
’Z’, --Înaltă impedanţă
’W’, --Necunoscut slab
’L’, --0 slab
’H’, --1 slab
’-’, --Fără importanţă
);

--Tablou neconstrâns de STD_ULOGIC utilizat în funcţia de rezoluţie


type STD_ULOGIC_VECTOR is array (NATURAL range <>) of STD_ULOGIC;

-- Funcţia de rezoluţie: comportamentul acestei funcţii nu face parte


-- din specificaţia pachetului, ea este însă normalizată
function RESOLVED(S: STD_ULOGIC_VECTOR) return STD_ULOGIC;

-- Tipul rezolvat STD_LOGIC este cel care apare cel mai adesea în
-- definiţiile modelelor industriale. Din raţiuni de simplitate, se
-- practică o utilizare abuzivă a tipului rezolvat chiar şi pentru
-- un semnal uni-sursă.
subtype STD_LOGIC is RESOLVED STD_ULOGIC;

-- Tablou neconstrâns de STD_LOGIC pentru declaraţii de tablouri


-- de semnale rezolvate
type STD_LOGIC_VECTOR is array (NATURAL range <>) of STD_LOGIC;

--Alte definiţii de sub-tipuri


subtype X01 is RESOLVED STD_ULOGIC range 'X' to '1'; --'X','0','1'
subtype X01Z is RESOLVED STD_ULOGIC range 'X' to 'Z'; --'X','0','1','Z'
subtype UX01 is RESOLVED STD_ULOGIC range 'U' to '1'; --'U', 'X', '0', '1'
subtype UX01Z is RESOLVED STD_ULOGIC range 'U' to 'Z';--'U','X','0','1',’Z’

--Supraîncărcarea operatorilor
function "and" (L: STD_ULOGIC; R: STD_ULOGIC) return UX01;
function "nand"(L: STD_ULOGIC; R: STD_ULOGIC) return UX01;
function "or" (L: STD_ULOGIC; R: STD_ULOGIC) return UX01;
function "nor" (L: STD_ULOGIC; R: STD_ULOGIC) return UX01;
function 'xor" (L: STD_ULOGIC; R: STD_ULOGIC) return UX01;
function 'not" (L: STD_ULOGIC) return UX01;

--Supraîncărcarea operatorilor pe vectori


function "and" (L, R: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
function "and" (L, R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function "nand" (L, R: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
function "nand" (L, R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function "or" (L, R: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
function "or" (L, R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function "nor" (L, R: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
function "nor" (L, R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function "xor" (L, R: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
function "xor" (L, R: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function "not" (L: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
PACHETE STANDARD ŞI PREDEFINITE 213
function "not" (L: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;

--Funcţii de conversie
function TO_BIT (S: STD_ULOGIC; XMAP: BIT:='0') return BIT;
function TO_BITVECTOR (S: STD_LOGIC_VECTOR; XMAP: BIT:='0') return
BIT_VECTOR;
function TO_BITVECTOR (S: STD_ULOGIC_VECTOR; XMAP: BIT:='0') return
BIT_VECTOR;
function TO_STDULOGIC (B: BIT) return STD_ULOGIC;
function TO_STDLOGICVECTOR (B: BIT_VECTOR) return STD_LOGIC_VECTOR;
function TO_STDLOGICVECTOR (S: STD_ULOGIC_VECTOR) return STD_LOGIC_VECTOR;
function TO_STDULOGICVECTOR (B: BIT_VECTOR) return STD_ULOGIC_VECTOR;
function TO_STDULOGICVECTOR (S: STD_LOGIC_VECTOR) return STD_ULOGIC_VECTOR;

--Transformarea codificării şi conversii de tip


function TO_X01 (S: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR;
function TO_X01 (S: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function TO_X01 (S: STD_ULOGIC) return X01;
function TO_X01 (B: BIT_VECTOR) return STD_LOGIC_VECTOR;
function TO_X01 (B: BIT_VECTOR) return STD_ULOGIC_VECTOR;
function TO_X01 (B: BIT) return X01;
function TO_X01Z (S: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR;
function TO_X01Z (S: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function TO_X01Z (S: STD_ULOGIC) return X01Z;
function TO_X01Z (B: BIT_VECTOR) return STD_LOGIC_VECTOR;
function TO_X01Z (B: BIT_VECTOR) return STD_ULOGIC_VECTOR;
function TO_X01Z (B: BIT) return X01Z;
function TO_UX01 (S: STD_LOGIC_VECTOR) return STD_LOGIC_VECTOR;
function TO_UX01 (S: STD_ULOGIC_VECTOR) return STD_ULOGIC_VECTOR;
function TO_UX01 (S: STD_ULOGIC) return UX01;
function TO_UX01 (B: BIT_VECTOR) return STD_LOGIC_VECTOR;
function TO_UX01 (B: BIT_VECTOR) return STD_ULOGIC_VECTOR;
function TO_UX01 (B: BIT) return UX01;

--Detectarea fronturilor
function RISING_EDGE (signal S: STD_ULOGIC) return BOOLEAN;
function FALLING_EDGE (signal S: STD_ULOGIC) return BOOLEAN;

--Funcţie de testare a obiectelor conţinând o valoare necunoscută 'X'


function IS_X (S: STD_ULOGIC_VECTOR) return BOOLEAN;
function IS_X (S: STD_LOGIC_VECTOR) return BOOLEAN;
function IS_X (S: STD_ULOGIC) return BOOLEAN;
end STD_LOGIC_1164;

2.4.3 Interpretarea tipului logic: semantica pentru sinteză


Scopul grupului de normalizare însărcinat cu definirea acestui pachet
era de a defini o semantică precisă pentru fiecare valoare a tipului enumerat
STD_ULOGIC şi de a propune o interpretare unică şi simplă în vederea
utilizării sale.
Valorile ‘0’ şi ‘1’ trebuie interpretate de către instrumentele de
sinteză, respectiv, „conectat la masă” şi „conectat la alimentare”. Nu există
semantică definită pentru valorile ‘L’ şi ‘H’: instrumentele de sinteză sunt
214 LIMBAJUL VHDL
libere să le interpreteze cum doresc. De exemplu, este posibil ca ‘L’ să fie
interpretat drept ‘0’ şi ca ‘H’ să fie interpretat ca ‘1’.

Observaţie
Luarea în considerare a valorilor implicite sau a valorilor iniţiale
constituie o problemă foarte serioasă care apare la sinteză. Unii proiectanţi
le utilizează în modelele lor ca nişte valori care apar ca urmare a unui
semnal de reset. Cu toate acestea, pentru aceste valori, nu există nici o
interpretare standard unanim recunoscută. Proiectanţii trebuie să fie
conştienţi că un model scris astfel nu este portabil de la un instrument de
sinteză la altul. Dacă funcţionalitatea modelului depinde de valorile iniţiale,
iniţializarea acestora trebuie să fie făcută în mod explicit ca urmare a
apariţiei unui eveniment.

Pentru celelalte valori ale tipului STD_ULOGIC, ‘Z’, ‘U’, ‘W’ şi ‘X’
(valoarea ‘-‘ va fi studiată separat), semnificaţiile respective depind de
utilizarea acestor valori. Se pot distinge patru cazuri:
1. Valoarea este utilizată ca valoare implicită sau ca valoare iniţială.
În acest caz nu este definită nici o semantică (a se vedea
observaţia de mai sus);
2. Valoarea este asignată unei variabile sau unui semnal. În acest
caz, valorile ‘U’, ‘W’ şi ‘X’ vor fi considerate „fără importanţă”.
Valoarea „fără importanţă” (‘-‘) poate fi înlocuită la sinteză fie
cu un ‘0’ fie cu un ‘1’. Valoarea ‘Z’ va solicita instrumentului de
sinteză generarea unui amplificator cu trei stări (tri-state). Ieşirea
acestui amplificator va constitui ţinta instrucţiunii de asignare;
3. Valoarea este utilizată într-o comparaţie implicită prin
intermediul instrucţiunii case. Instrucţiunile care apar atunci sunt
ignorate (aici se vede foarte clar diferenţa de semantică dintre
sinteză şi simulare);
4. Valoarea este utilizată de către un operator de comparare ca
expresie a unei condiţii în cadrul unei instrucţiuni if. Expresiile
A = B şi A /= B returnează FALSE, respectiv TRUE dacă unul
dintre operanzi utilizează una dintre valorile: ‘U’, ‘W’ sau ‘X’.
Comparaţiile realizate cu ajutorul operatorilor „>”, „>=”, „<”,
„<=” returnează, toate, FALSE.
PACHETE STANDARD ŞI PREDEFINITE 215
Valoarea „fără importanţă” este foarte utilă şi este foarte utilizată în
cadrul sintezei. Ea permite două funcţionalităţi:
1. Atunci când valoarea se află în membrul drept al unei
instrucţiuni de asignare, este posibilă optimizarea.

S <= ‘-‘;
V := ‘-‘;
ARRAY_S <= “----“;

Instrumentul de sinteză este liber să înlocuiască fiecare valoare


‘-‘ fie cu un ‘1’, fie cu un ‘0’. Scopul acestei flexibilităţi este de
a oferi posibilitatea de a optimiza hardware-ul generat.
2. Când valoarea apare în contextul unei comparaţii, ea este luată
drept valoare generică (wild card). Acest lucru poate apărea
implicit în cadrul unei instrucţiuni case sau explicit în cadrul
unei instrucţiuni if sau în cadrul unei expresii care întrebuinţează
un operator de comparare. Valoarea generică permite construirea
de filtre. De exemplu, “1101” şi “1--1” se potrivesc (match)
deoarece comparaţia este filtrată (comparaţia nu se realizează
efectiv decât asupra primului şi ultimului bit). Valorile “1101” şi
“10-1” nu se potrivesc din cauza celui de-al doilea bit.
Este uşor de intuit cât de importantă este această posibilitate de
construcţie a filtrelor pentru optimizarea resurselor hardware generate în
urma procesului de sinteză. Din păcate, ea introduce o importantă diferenţă
de semantică între sinteză şi simulare, astfel încât rezultatele obţinute la
simularea efectuată înainte şi după sinteză riscă să difere considerabil.
În urma scrierii expresiei “1101” = “1--1”, evaluarea sa va returna
valoarea FALSE la simulare, dar în urma evaluării aceleiaşi expresii de către
un instrument de sinteză se va obţine valoarea TRUE! Fireşte, astfel se va
obţine un comportament diferit la simularea pre şi post sinteză.
Pentru evitarea unei asemenea situaţii, se recomandă utilizarea unei
funcţii specifice numite STD_MATCH, care a fost definită în cadrul unui
pachet normalizat IEEE 1076.3 NUMERIC_STD. Principiul acestei funcţii
constă în aplicarea unei semantici de valoare generică valorii ‘-‘. În
consecinţă, comparaţiile în care va apărea această valoare vor avea semantici
coerente la simulare şi la sinteză.
216 LIMBAJUL VHDL
În exemplul de mai jos, rezultatul obţinut în urma sintezei va fi
acelaşi şi pentru linia 1 şi pentru linia 2, însă numai linia 2 va fi coerentă la
simulare atât înainte cât şi după sinteză:

signal A: STD_ULOGIC_VECTOR (3 downto 0);


...
if A /= “10--“ then -- Linia 1
...
if STD_MATCH(A, “10--“) then -- Linia 2
...

Se recomandă insistent utilizarea funcţiei STD_MATCH. În cadrul


pachetului normalizat NUMERIC_STD au fost definite mai multe
supraîncărcări ale acestei funcţii (STD_ULOGIC_VECTOR,
STD_ULOGIC, STD_LOGIC, STD_LOGIC_VECTOR, UNSIGNED şi
SIGNED), iar valoarea returnată este de tip BOOLEAN.

2.5 Pachete aritmetice

Un pachet aritmetic are întotdeauna următoarea structură:


- definiţia celor două tipuri numerice SIGNED şi UNSIGNED;
- declararea operaţiilor aritmetice, a comparaţiilor, a deplasărilor
(shift) şi a rotaţiilor (rotate);
- declararea unui ansamblu de funcţii de conversie care permit
trecerea de la domeniul vectorilor de biţi spre întregi, şi invers.

2.5.1 Declaraţii de tipuri


În cazul pachetului NUMERIC_BIT sunt făcute următoarele
declaraţii:

type UNSIGNED is array (NATURAL range <>) of BIT;


type SIGNED is array (NATURAL range <>) of BIT;

În cazul pachetului NUMERIC_STD sunt făcute următoarele


declaraţii:

type UNSIGNED is array (NATURAL range <>) of STD_LOGIC;


type SIGNED is array (NATURAL range <>) of STD_LOGIC;
PACHETE STANDARD ŞI PREDEFINITE 217

Observaţii
Cele două tipuri SIGNED şi UNSIGNED sunt definite ca tipuri noi
şi NU ca sub-tipuri ale lui BIT_VECTOR sau STD_LOGIC_VECTOR.
Graţie acestei precauţii, obiectelor de tipul SIGNED sau UNSIGNED nu li
se vor putea asigna valori decât cu ajutorul procedurilor sau funcţiilor puse
special în acest scop la dispoziţia proiectanţilor. În caz contrar, ar fi fost
posibilă amestecarea obiectelor de tip SIGNED sau UNSIGNED, fără a avea
un control formal asupra lor.
Se observă, în cazul pachetului NUMERIC_STD, că a fost preferat
tipul STD_LOGIC în locul lui STD_ULOGIC, datorită faptului că primul
este mai general, fiind rezolvat, şi astfel poate fi aplicat fără constrângeri în
orice context. Ideal ar fi fost să se ofere ambele posibilităţi, dar această
abordare ar fi impus definirea de multe alte definiţii de tipuri şi de funcţii
asociate, ceea ce ar fi complicat foarte mult contextul general de lucru.

2.5.2 Declaraţii de operatori


Toţi operatorii aritmetici şi de comparare definiţi pe întregi sunt
supraîncărcaţi pe tipurile SIGNED şi UNSIGNED. Astfel, sunt redefiniţi
operatorii „+”, „-“, „*”, „/”, „rem”, „mod”, „>”, „>=”, „<”, „<=”. Pentru
toţi aceşti operatori binari, supraîncărcarea este completă, oferind
utilizatorului un maximum de supleţe şi de uşurinţă în exploatare. Spre
exemplu, prezentăm supraîncărcarea operatorului binar de adunare:

function “+”(L, R: UNSIGNED) return UNSIGNED;


function “+”(L, R: SIGNED) return SIGNED;
function “+”(L: UNSIGNED; R: NATURAL) return UNSIGNED;
function “+”(L: NATURAL; R: UNSIGNED) return UNSIGNED;
function “+”(L: INTEGER; R: SIGNED) return SIGNED;
function “+”(L: SIGNED; R: INTEGER) return SIGNED;

Dimensiunea rezultatului este întotdeauna specificată în mod foarte


precis. Astfel, în cazul adunării (şi la fel stau lucrurile şi pentru scădere),
dimensiunea rezultatului este întotdeauna dimensiunea celui mai mare dintre
cei doi vectori primiţi ca parametri sau dimensiunea singurului vector primit
ca parametru (în cazul în care celălalt operand este de tipul INTEGER sau
NATURAL).
218 LIMBAJUL VHDL
În cazul înmulţirii, dimensiunea rezultatului trebuie să fie egală cu
suma dimensiunilor celor doi operanzi atunci când aceştia sunt vectori.
Atunci când numai un operand este vector (celălalt fiind un întreg),
dimensiunea rezultatului este egală cu dublul dimensiunii operandului
vector (se presupune deci că operandul întreg este mai întâi tradus într-un
vector de dimensiunea celuilalt operand de natură vectorială).
În ceea ce priveşte împărţirile, dimensiunea rezultatului este egală cu
dimensiunea primului operand (deîmpărţitul) atunci când acesta este un
vector. În cazul contrar (deîmpărţitul este de tip INTEGER sau NATURAL),
dimensiunea rezultatului este luată implicit egală cu dimensiunea
împărţitorului (cel de-al doilea operand). În acest ultim caz, conversia
deîmpărţitului se face ţinând cont de dimensiunea împărţitorului, la nevoie
efectuându-se o trunchiere.
Operaţiile modulo (mod) şi „restul împărţirii întregi” (rem) sunt
absolut analoage împărţirii în privinţa calculării şi dimensiunii rezultatului
(numărul calculat este întotdeauna mai mic decât împărţitorul). Rezultă de
aici că dimensiunea rezultatului este egală cu dimensiunea celui de-al doilea
operand (împărţitorul) atunci când acesta este un vector. În caz contrar (dacă
împărţitorul este de tip INTEGER sau NATURAL), atunci dimensiunea
rezultatului este luată implicit drept dimensiunea deîmpărţitului (primul
operand). În această situaţie, conversia împărţitorului se efectuează ţinând
cont de dimensiunea deîmpărţitului, la nevoie efectuându-se o trunchiere.
Comparaţiile sunt efectuate cu operanzi de dimensiuni eventual
diferite (dacă operanzii respectivi sunt vectori).
Deplasările şi rotaţiile sunt definite simultan ca funcţii şi ca
operatori. Motivul este evident: operatorii sll, srl, rol şi ror nu au apărut
decât o dată cu versiunea VHDL'93; deci, pentru păstrarea compatibilităţii
cu vechea versiune VHDL'87, au fost declarate şi funcţiile SHIFT_LEFT,
SHIFT_RIGHT, ROTATE_LEFT şi ROTATE_RIGHT. Dimensiunea
rezultatului trebuie să fie identică cu dimensiunea vectorului asupra căruia
se efectuează deplasarea sau rotaţia.

2.5.3 Declaraţiile funcţiilor de conversie


Funcţiile de conversie definite în pachetele aritmetice sunt de două
feluri:
1. Funcţii care permit trecerea de la o reprezentare vectorială la o
altă reprezentare, neschimbându-se decât dimensiunea rezultatului faţă de
PACHETE STANDARD ŞI PREDEFINITE 219
dimensiunea argumentului. Pe scurt, aceste funcţii efectuează fie o extensie
a numărului (dimensiunea rezultatului este mai mare decât dimensiunea
argumentului de intrare), fie o trunchiere (dimensiunea rezultatului este mai
mică decât dimensiunea argumentului de intrare). Modul de traducere al
extensiei diferă, în funcţie de argument. Dacă argumentul este de tip
SIGNED, atunci bitul de semn (bitul cel mai semnificativ al vectorului
argument) este duplicat de atâtea ori cât este necesar pentru a se ajunge la
dimensiunea rezultatului dorit. Dacă argumentul este de tip UNSIGNED,
atunci se va insera un nou bit de valoare '0' la stânga vectorului argument, de
câte ori este necesar, până se ajunge la dimensiunea rezultatului dorit. Există
două asemenea funcţii, una pentru transformările elementelor de tip
SIGNED, cealaltă pentru transformările elementelor de tip UNSIGNED.
Specificaţiile lor sunt:

function RESIZE (ARG: SIGNED; NEW_SIZE: NATURAL) return SIGNED;


function RESIZE (ARG: UNSIGNED; NEW_SIZE: NATURAL) return UNSIGNED;

2. Funcţii care permit trecerea de la o reprezentare sub formă de


vectori (SIGNED sau UNSIGNED) la o reprezentare sub formă de întregi
(INTEGER sau NATURAL) şi invers. Funcţiile respective sunt:

function TO_INTEGER(ARG: UNSIGNED) return NATURAL;


function TO_INTEGER(ARG: SIGNED) return INTEGER;
function TO_UNSIGNED(ARG, SIZE: NATURAL) return UNSIGNED;
function TO_SIGNED(ARG: INTEGER; SIZE: NATURAL) return SIGNED;

2.5.4 Declaraţii diverse


Toate funcţiile logice au fost supraîncărcate pe tipurile SIGNED şi
UNSIGNED.
Există o funcţie care permite forţarea la valoarea '0' sau '1' a oricărui
element al unui vector de tip SIGNED şi UNSIGNED. Această funcţie se
numeşte TO_01. Parametrul XMAP, având implicit valoarea '+', permite
determinarea valorii finale a elementelor unui vector ale cărui valori nu sunt
'0', '1', 'L' sau 'H'.
Mai multe detalii referitoare la pachetele standard şi nestandardizate
se găsesc în Anexă.
220 LIMBAJUL VHDL
3. Desfăşurarea lucrării
3.1 Se va testa funcţia NOW din cadrul pachetului STANDARD.
3.2 Se va testa funcţia de rezoluţie a tipului STD_LOGIC prin
crearea unui semnal multi-sursă şi luarea în consideraţie a tuturor
cazurilor.
3.3 Se vor utiliza funcţiile de conversie predefinite în cadrul
pachetelor aritmetice pentru implementarea unui numărător
bidirecţional pe 8 biţi.

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