VHDL - istruzioni concorrenti, process e testbench
In questa pagina 6
Dopo aver visto la struttura di un blocco (Introduzione al VHDL - entity, architecture, tipi e livelli di descrizioneVHDL è un linguaggio di descrizione dell'hardware (HDL), non di programmazione: le dichiarazioni sono concorrenti e descrivono blocchi di circuito; serve per documentare, simulare e sintetizzare. Un blocco ha una entity (nome e porte in/out) e almeno una architecture (cosa c'è dentro: structural con component e port map, dataflow con equazioni booleane, behavioral con comportamento). Tipi: std_logic (da 1164: '0','1','X','Z','U','-',...), std_logic_vector, bit, boolean, integer. Signal per i collegamenti interni, constant per i valori fissi.Introduzione al VHDL - entity, architecture, tipi e livelli di descrizione →), qui si descrive come scrivere il comportamento dei circuiti combinatori e come verificarli in simulazione con un testbench. Gli stessi costrutti, con il clock, servono per i circuiti sequenziali (VHDL - latch, flip-flop e reset sincrono e asincronoIn VHDL un elemento di memoria si descrive con un process il cui ramo non assegna sempre il segnale: latch D = process(D,C) con if C='1' then Q<=D; flip-flop positive-edge-triggered = process(CLK) con if CLK'event and CLK='1'. Reset asincrono: RST nella sensitivity list e testato per primo (if RST='1' ... elsif fronte); reset sincrono: solo CLK in lista e RST testato dentro al ramo del fronte (serve un fronte per averne effetto). Il testbench di un circuito sincrono ha un process che genera il clock e uno che applica gli ingressi lontano dal fronte attivo.VHDL - latch, flip-flop e reset sincrono e asincrono →).
Istruzioni concorrenti
Nel corpo di un'architecture (tra begin ed end) le istruzioni sono concorrenti: tutte attive in parallelo.
Assegnazione semplice
Definizione (assegnazione concorrente).
y <= (a and b) or c;è un'equazione booleana. Ogni volta che cambia un segnale a destra,yviene rivalutato. Con più assegnazioni allo stesso segnale in istruzioni concorrenti si hanno più driver: in simulazione, se i valori sono diversi, il segnale diventa'X'(conflitto); è un errore di progetto.
Esempio. y <= (a and b) or c; con a='1', b='0', c='1' dà y='1'.
when ... else (logica prioritaria)
Y <= I0 when S = "00" else
I1 when S = "01" else
I2 when S = "10" else
I3;Proprietà (when ... else). Le condizioni sono valutate in ordine: se la prima è vera si sceglie il primo valore e le successive non contano. Serve per le priorità e permette decisioni su segnali diversi in condizioni diverse.
Esempio. Con S = "01" la prima condizione è falsa e la seconda vera: Y <= I1.
with ... select (logica parallela)
with S select
Y <= I0 when "00",
I1 when "01",
I2 when "10",
I3 when others;Proprietà (with ... select). Si valuta una sola espressione di scelta (
S); tutte le scelte hanno la stessa priorità, tutti i valori possibili devono essere coperti da una e una sola scelta ewhen othersè obbligatorio per i valori non elencati. È più semplice delwhen-elseper i circuiti a selezione (multiplexer, decoder, tabelle di verità). Più valori si elencano con|:'1' when "0100" | "1000".
Esempio. Con S = "11" nessuna scelta esplicita corrisponde e vale when others: Y <= I3.
library IEEE;
use IEEE.std_logic_1164.all;
entity decoder2to4 is
port ( A : in std_logic_vector(1 downto 0);
EN : in std_logic;
D : out std_logic_vector(3 downto 0) );
end decoder2to4;
architecture dataflow of decoder2to4 is
begin
D(0) <= EN and not A(1) and not A(0);
D(1) <= EN and not A(1) and A(0);
D(2) <= EN and A(1) and not A(0);
D(3) <= EN and A(1) and A(0);
end dataflow;Con EN = '0' tutte le uscite sono '0'.
Il process
Un process è un'istruzione concorrente (parallela alle altre dell'architecture) dentro la quale le istruzioni sono sequenziali: eseguite una dopo l'altra, nell'ordine in cui sono scritte, in un tempo infinitesimo (se non c'è un wait). Tutti i process di un'architecture lavorano simultaneamente.
etichetta : process ( lista_di_sensibilità )
-- dichiarazioni locali
begin
-- istruzioni sequenziali: assegnazioni, if, case, for ...
end process;Definizione (lista di sensibilità). Lista di sensibilità (sensitivity list): i segnali al cui cambiamento il process si attiva. Per descrivere un circuito combinatorio deve contenere tutti gli ingressi (in VHDL-2008 si può scrivere
process (all)). Può contenere solo segnali leggibili (ingressi osignalinterni, non uscite). Un process senza lista parte subito all'inizio della simulazione e va avanti ripetendosi, a meno di unwait: è lo schema dei testbench.
Esempio. process (a, b) begin y <= a and b; end process; viene rivalutato a ogni cambiamento di a o di b.
Proprietà (aggiornamento dei segnali in un process). Il valore assegnato a un segnale dentro un process viene aggiornato solo quando il process si sospende (finite le istruzioni o a un
wait). Se lo stesso segnale è assegnato più volte nello stesso passaggio, vale l'ultima assegnazione. Quindiprocess (b) begin a <= b; a <= not b; end process;descrive : un semplice inverter, cioè logica combinatoria, e non un flip-flop né un segnale che alterna.
Esempio. Con b='1', le due assegnazioni a <= b; a <= not b; lasciano a='0': vale l'ultima.
if e case sequenziali
L'if è l'analogo sequenziale del when-else (valuta le condizioni in ordine, crea una priorità); il case è l'analogo sequenziale del with-select (scelte tutte coperte, non sovrapposte, when others obbligatorio; null per "nessuna azione"):
-- priority encoder a 4 ingressi con if
process (D)
begin
if D(3) = '1' then A <= "11"; V <= '1';
elsif D(2) = '1' then A <= "10"; V <= '1';
elsif D(1) = '1' then A <= "01"; V <= '1';
elsif D(0) = '1' then A <= "00"; V <= '1';
else A <= "--"; V <= '0'; -- nessun ingresso a 1: A indifferente
end if;
end process;-- numero primo (o 1) a 4 bit con case
process (input)
begin
case input is
when x"1" | x"2" | x"3" | x"5" | x"7" | x"b" | x"d" => isPrime <= '1';
when others => isPrime <= '0';
end case;
end process;(I valori sono i primi minori di , più 1.)
Memoria non voluta
Proprietà (memoria non voluta). Se in un
if(o in uncase) non si specificano tutte le condizioni, il segnale mantiene il valore precedente per quelle non coperte: il sintetizzatore deve introdurre un elemento di memoria (latch). È lo stesso meccanismo con cui si descrive di proposito un latch o un flip-flop; ma in un circuito che deve essere combinatorio è un errore.
Esempio. Numeri primi a 3 bit ():
-- CORRETTO: tutte le condizioni (else)
process (input)
begin
if input = "001" or input = "010" or input = "011" or input = "101" or input = "111" then
isPrime <= '1';
else
isPrime <= '0';
end if;
end process;
-- ERRATO: manca il caso per "000" e "011"
process (input)
begin
if input = "001" or input = "010" or input = "101" or input = "111" then
isPrime <= '1';
elsif input = "100" or input = "110" then
isPrime <= '0';
end if; -- per "000" e "011" isPrime conserva il valore di prima: memoria
end process;Nel secondo codice, se arriva "011" dopo "100" l'uscita resta '0', se dopo "101" resta '1': il circuito ha memoria. Altro esempio tipico: process(A) begin if A = '1' then Q <= B; end if; end process; descrive un latch (memoria), non una porta.
Regola pratica: per la logica combinatoria ogni uscita deve essere assegnata in ogni ramo di if e case, e tutti gli ingressi stanno nella lista di sensibilità.
Testbench
Definizione (testbench). Il testbench è codice VHDL che simula un circuito: applica segnali in ingresso al circuito da verificare (DUT, device under test) e osserva le uscite. Non si sintetizza: serve solo per la simulazione.
Struttura:
- entity vuota (nessuna porta, il testbench non ha ingressi né uscite);
- nell'architecture: segnali per pilotare gli ingressi del DUT e leggerne le uscite;
- istanza del DUT (con la port map);
- uno o più process che applicano gli stimoli, in modo sequenziale, con
wait forper far passare il tempo (wait;senza argomenti sospende il process per sempre).
Esempio: funzione maggioranza ( se almeno due ingressi sono 1):
library IEEE;
use IEEE.std_logic_1164.all;
entity majority is
port ( a, b, c : in std_logic;
y : out std_logic );
end majority;
architecture maj_dataflow of majority is
begin
y <= (a and b) or (a and c) or (b and c);
end maj_dataflow;library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_majority is -- entity vuota
end tb_majority;
architecture test of tb_majority is
signal inp : std_logic_vector(2 downto 0) := "000";
signal y : std_logic;
begin
DUT : entity work.majority(maj_dataflow)
port map (a => inp(2), b => inp(1), c => inp(0), y => y);
stimuli : process
begin
for i in 0 to 7 loop -- tutte le 8 combinazioni
inp <= std_logic_vector(to_unsigned(i, 3));
wait for 10 ns; -- tempo perché l'uscita si stabilizzi
report "in = " & integer'image(i) & " -> y = " & std_logic'image(y);
end loop;
wait; -- fine degli stimoli
end process;
end test;Risultato atteso del report: per e per (cioè ).
Punti da ricordare:
- Dopo aver cambiato gli ingressi si aspetta (
wait for 10 ns) prima di leggere l'uscita: il valore appena assegnato a un segnale è visibile solo dopo che il process si sospende. - In un process di stimoli le istruzioni sono sequenziali (a differenza del resto dell'architecture).
- Per verificare automaticamente si usa
assert:
assert (y = '1') = (i >= 3 and i /= 4)
report "uscita errata per i = " & integer'image(i)
severity error;- Il testbench di un circuito sequenziale ha anche un process che genera il clock (vedi VHDL - macchine a stati finiti e testbench sequenzialeUna macchina a stati finiti (FSM) si descrive in VHDL, in stile behavioral, con un tipo enumerato per gli stati e tre process: (1) registro di stato con reset (asincrono) e aggiornamento state<=next_state sul fronte; (2) stato futuro come funzione combinatoria di stato e ingresso (case state, default next_state<=state); (3) uscita come funzione combinatoria di stato (Moore) o di stato e ingresso (Mealy). Il testbench ha il process del clock e il process degli ingressi (reset iniziale, sequenza di test che percorre tutti gli stati).VHDL - macchine a stati finiti e testbench sequenziale →).
- Per un testbench che esplora tutte le combinazioni di ingressi bastano passi di un ciclo
for; per i circuiti sequenziali non si può in genere (serve scegliere le sequenze significative).
Tabella riassuntiva
| istruzione concorrente | istruzione sequenziale (dentro un process) | |
|---|---|---|
| condizione a priorità | when ... else |
if ... elsif ... else |
| scelta parallela | with ... select |
case ... when ... |
| assegnazione | y <= expr; |
y <= expr; (vince l'ultima) |
Errori comuni
- Lista di sensibilità incompleta in un process combinatorio: in simulazione l'uscita non si aggiorna quando cambia l'ingresso mancante.
ifsenzaelse(ocasesenzaothers): memoria non voluta.- Più assegnazioni concorrenti allo stesso segnale (driver multipli,
'X'). - Leggere un'uscita subito dopo l'assegnazione senza
wait. - Usare
with selectsenzawhen others. - Ritenere che
a <= b; a <= not b;in un process produca due valori in successione: vale l'ultimo.
Versione ripasso
- Assegnazione concorrente:
y <= (a and b) or c;è un'equazione booleana che si rivaluta a ogni cambiamento di un segnale a destra. Più assegnazioni allo stesso segnale danno più driver: se i valori differiscono il segnale diventa'X', cioè un errore di progetto. when ... else: logica prioritaria; le condizioni sono valutate in ordine e la prima vera vince (Y <= I0 when S="00" else I1 when S="01" else ... I3;).with ... select: logica parallela; si valuta una sola espressione, tutte le scelte hanno pari priorità, i valori devono essere coperti da una sola scelta ewhen othersè obbligatorio. Più valori con|:'1' when "0100" | "1000".- Decoder 2-to-4 con enable in dataflow:
D(0) <= EN and not A(1) and not A(0);e le altre tre uscite analoghe; conEN='0'tutte le uscite valgono'0'(Decoder, encoder e priority encoderUn decoder $n$-to-$m$ ($m\le2^n$) converte un ingresso binario a $n$ bit in un'uscita 1-hot (un solo 1, nella posizione indicata): le sue uscite sono i mintermini degli ingressi, realizzati con $m$ AND; per decoder grandi si usa l'approccio gerarchico (costo in ingressi: 3-to-8 = 27, 6-to-64 = 182) e un enable. Ogni funzione = decoder + OR dei suoi mintermini. L'encoder fa l'operazione inversa (1-hot $\to$ binario) ma sbaglia con più ingressi a 1 o tutti a 0: il priority encoder risolve con una priorità e un'uscita V (valid).Decoder, encoder e priority encoder →). - Process: istruzione concorrente il cui corpo è sequenziale (assegnazioni,
if,case,for); tutti i process lavorano in parallelo. Si scriveetichetta : process (lista) ... end process;. - Lista di sensibilità: i segnali che attivano il process. Per la logica combinatoria deve contenere tutti gli ingressi (VHDL-2008:
process (all)). Un process senza lista parte all'inizio e va avanti fino a unwait: è lo schema dei testbench. - Aggiornamento dei segnali: un segnale assegnato in un process prende il valore solo quando il process si sospende, e vince l'ultima assegnazione. Quindi
process (b) begin a <= b; a <= not b; end process;descrive , cioè un inverter combinatorio. ifecasesequenziali:if/elsif/elsecorrisponde awhen-else;casecorrisponde awith-select(scelte coperte,when othersobbligatorio,nullper nessuna azione). Esempio: priority encoder a 4 ingressi conif D(3)='1' then A <= "11"; V <= '1'; elsif ..., e nel ramo finaleA <= "--"; V <= '0';.- Memoria non voluta: se un
ifo uncasenon assegna il segnale in tutti i rami, il sintetizzatore inserisce un latch. Esempio di errore: manca il caso"000"e"011"nel calcolo dei primi a 3 bit, quindi l'uscita conserva il valore precedente. Regola: ogni uscita assegnata in ogni ramo, con lista di sensibilità completa. - Testbench: entity vuota; segnali per pilotare il DUT; istanza
DUT : entity work.majority(maj_dataflow) port map (a => inp(2), b => inp(1), c => inp(0), y => y);; process di stimoli con un ciclofor i in 0 to 7 loopsuto_unsigned(i, 3),wait for 10 ns;ereport. Conassertsi verifica automaticamente;wait;finale sospende per sempre. - Esempio di verifica: la funzione maggioranza ( se almeno due ingressi sono 1) vale 1 per ; il report atteso è quindi per e per .
- Regole pratiche: dopo aver cambiato gli ingressi bisogna aspettare prima di leggere l'uscita, perché il valore è visibile solo dopo la sospensione del process; in un process di stimoli le istruzioni sono sequenziali.
- Errori: lista di sensibilità incompleta (l'uscita non segue l'ingresso mancante);
ifsenzaelseocasesenzaothers; driver multipli sullo stesso segnale; lettura dell'uscita senzawait;with selectsenzawhen others;a <= b; a <= not b;letto come due valori in successione.