Introduzione al VHDL - entity, architecture, tipi e livelli di descrizione
In questa pagina 7
I circuiti con miliardi di transistor non si disegnano a mano: si descrivono con un linguaggio di descrizione dell'hardware (HDL, Hardware Description Language) e si usano strumenti CAD per simularli e sintetizzarli. I due HDL più diffusi, entrambi standard IEEE, sono VHDL e Verilog. VHDL (Very high speed integrated circuits Hardware Description Language) nasce nel 1987 da un progetto del Dipartimento della difesa statunitense ed è stato riveduto più volte.
Non è un linguaggio di programmazione
In Python o in C le istruzioni sono tradotte in una sequenza eseguita una dopo l'altra da un processore. Un HDL viene invece tradotto in un circuito digitale: le dichiarazioni sono concorrenti, cioè attive tutte nello stesso istante, come le porte di un circuito lavorano in parallelo. Un'istruzione VHDL definisce un blocco di hardware, non un passo di esecuzione; cambiare l'ordine delle istruzioni concorrenti non cambia il circuito.
Usi del VHDL:
- documentare un progetto;
- simulare il comportamento: si applicano segnali in ingresso e si osservano le uscite nel tempo (nel corso si usa un simulatore online);
- sintetizzare: passare da una descrizione comportamentale a un insieme di porte e flip-flop (netlist). Solo un sottoinsieme del linguaggio è sintetizzabile; una descrizione pensata solo per la simulazione (come un testbench) può usare tutto il linguaggio. Nel corso non si studia la sintesi.
Flusso di sintesi (per capire dove si colloca il VHDL): descrizione HDL + vincoli (area, velocità, consumo) + librerie tecnologiche forma intermedia RTL (Register Transfer Level: porte e registri generici) ottimizzazione mappatura tecnologica sui blocchi realmente disponibili netlist, usata per produrre il layout di un circuito integrato o la configurazione di un FPGA (Logica programmabile - ROM, PLA, PAL e FPGAI dispositivi logici programmabili (PLD) hanno strutture logiche configurabili dal costruttore o dall'utente (configurazione $\ne$ programmazione software): ROM ($2^k$ parole da $n$ bit: decoder + OR programmabili), PLA (AND e OR programmabili, termini prodotto condivisi tra le uscite), PAL (AND programmabili, OR fisse, niente condivisione), FPGA (blocchi logici con LUT e flip-flop, interconnessioni e I/O programmabili, memoria di configurazione volatile o no). La programmazione può essere irreversibile (fuse/anti-fuse, mask) o riconfigurabile (SRAM, gate flottante).Logica programmabile - ROM, PLA, PAL e FPGA →). È un processo iterativo.
Caratteristiche generali
- Fortemente tipizzato: dati di tipo diverso non si assegnano l'uno all'altro senza una funzione di conversione.
- Case insensitive:
EntityedENTITYsono la stessa cosa; spazi, tabulazioni e a capo sono ignorati; i commenti iniziano con--. - Ogni istruzione termina con
;. - Si tenga sempre presente l'hardware che si sta descrivendo.
Entity e architecture
Un blocco VHDL ha due parti obbligatorie.
Entity. È la "targa" del blocco: ne dà il nome e definisce l'interfaccia con l'esterno (quali porte, di che direzione e tipo). Non dice che cosa c'è dentro, come il simbolo di un circuito in uno schema. Un blocco deve avere una sola entity.
entity nome_entity is
port ( ingresso1, ingresso2 : in std_logic;
uscita : out std_logic ); -- il ';' non va dopo l'ultima porta
end nome_entity;Modo (direzione) delle porte: in (il valore può essere letto ma non assegnato dal blocco: il driver è fuori), out (il valore può essere assegnato ma non letto: il driver è dentro). Esistono anche inout e buffer, non usati nel corso. L'assegnazione si scrive X <= Y;: si legge "Y è assegnato a X", la freccia va da destra a sinistra; ogni volta che cambia, cambia anche . Lo statement X <= Y è il driver del segnale (la sorgente).
Architecture. È una descrizione particolare del contenuto di una entity: come si comporta e come opera internamente. Una entity può avere più architecture nello stesso progetto (per esempio una structural e una dataflow); un progetto non può avere più entity per lo stesso blocco.
architecture nome_arch of nome_entity is
-- dichiarazioni: component, signal, constant, type
begin
-- corpo: istruzioni concorrenti
end nome_arch;Librerie e package. Si dichiarano prima di ogni entity:
library IEEE;
use IEEE.std_logic_1164.all; -- tipi std_logic e std_logic_vectorLe librerie sono raccolte di package che contengono codice riusabile (tipi, componenti, costanti). .all significa "tutto ciò che il package definisce". Per le operazioni aritmetiche sui vettori ci sono package IEEE (numeric_std) e package Synopsys (std_logic_unsigned, std_logic_signed) che cambiano il modo in cui un std_logic_vector è trattato (con o senza segno).
Tipi di dato
| tipo | che cosa è | esempio di valore |
|---|---|---|
std_logic |
un bit "logico" a 9 valori | '0', '1', 'X', 'Z', '-' |
std_logic_vector |
vettore di std_logic |
"010", x"1F" |
bit |
solo '0' e '1' |
'1' |
boolean |
TRUE / FALSE |
true |
integer |
intero a 32 bit | 100 |
I valori di std_logic (da std_logic_1164): '0' e '1' logici; 'X' valore non noto o in conflitto; 'U' non inizializzato (si vede in simulazione); 'Z' alta impedenza (uscita scollegata); '-' don't care (il sintetizzatore può ottimizzare); 'L', 'H' e 'W' valori "deboli". Per noi contano 0, 1, X, -, Z, U. Un std_logic va tra apici singoli ('1'), un vettore tra virgolette doppie ("010").
Vettori. std_logic_vector(n-1 downto 0) ha il bit più significativo a sinistra, di indice ; std_logic_vector(0 to n-1) ha l'indice a sinistra (è il MSB). Un bit si estrae con x(2), una porzione con x(3 downto 1); il concatenamento si scrive &: "10" & "01" vale "1001". I letterali esadecimali si scrivono x"1F". Il tipo integer si usa per parametri (lunghezza dei vettori, indici dei cicli) e si scrive in decimale senza virgolette.
Signal e constant. Le porte comunicano con l'esterno e sono definite nella entity; i segnali interni (signal) si dichiarano nell'architecture, prima di begin, e servono a collegare nodi interni o blocchi tra loro: signal n1, n2 : std_logic;. Le costanti (constant N : integer := 8;) danno un nome a valori fissi, per leggibilità e per facilitare le modifiche.
I tre livelli di descrizione: il comparatore a 2 bit
Specifica: ingressi , (il pedice è il bit più significativo), uscita AgtB se . Dalla mappa di Karnaugh (Mappe di Karnaugh - implicanti e copertura minimaLa mappa di Karnaugh è la tabella di verità disposta in una griglia con righe e colonne in codice Gray, così che celle adiacenti (anche tra bordi opposti) differiscano in una sola variabile. Si raggruppano gli 1 in rettangoli di $2^k$ celle: ogni gruppo elimina $k$ variabili e dà un prodotto. Implicante primo = gruppo massimo; essenziale = unico a coprire un mintermine; la copertura minima contiene tutti gli essenziali più il minimo di altri primi (può non essere unica). Efficace fino a 4 variabili.Mappe di Karnaugh - implicanti e copertura minima →) si ottiene la SOP minima
Stessa entity per tutti i livelli:
library IEEE;
use IEEE.std_logic_1164.all;
entity comp2 is
port ( A, B : in std_logic_vector(1 downto 0);
AgtB : out std_logic );
end comp2;Dataflow
Descrive il flusso dei segnali con equazioni booleane espresse da assegnazioni concorrenti, senza dettagliare la struttura (nessun componente):
architecture dataflow of comp2 is
begin
AgtB <= (A(1) and not B(1)) or
(A(0) and not B(1) and not B(0)) or
(A(1) and A(0) and not B(0));
end dataflow;Behavioral
Descrive solo il comportamento, lasciando al sintetizzatore la scelta del circuito. Per esempio con la tabella di verità (si elencano le sei combinazioni di con , scritte come ):
architecture behav of comp2 is
signal AB : std_logic_vector(3 downto 0);
begin
AB <= A & B;
with AB select
AgtB <= '1' when "0100" | "1000" | "1001" | "1100" | "1101" | "1110",
'0' when others;
end behav;Structural
Descrive il circuito come componenti interconnessi, equivalente a uno schema a porte: livello di astrazione più basso. Servono le porte come blocchi VHDL; per esempio:
-- inverter
library IEEE; use IEEE.std_logic_1164.all;
entity inv is port (x : in std_logic; y : out std_logic); end inv;
architecture impl of inv is begin y <= not x; end impl;
-- AND a 2 ingressi
library IEEE; use IEEE.std_logic_1164.all;
entity and2 is port (a, b : in std_logic; y : out std_logic); end and2;
architecture impl of and2 is begin y <= a and b; end impl;
-- AND a 3 ingressi
library IEEE; use IEEE.std_logic_1164.all;
entity and3 is port (a, b, c : in std_logic; y : out std_logic); end and3;
architecture impl of and3 is begin y <= a and b and c; end impl;
-- OR a 3 ingressi
library IEEE; use IEEE.std_logic_1164.all;
entity or3 is port (a, b, c : in std_logic; y : out std_logic); end or3;
architecture impl of or3 is begin y <= a or b or c; end impl;
architecture struct of comp2 is
signal nb1, nb0, p1, p2, p3 : std_logic; -- nodi interni
begin
I1 : entity work.inv(impl) port map (B(1), nb1);
I0 : entity work.inv(impl) port map (B(0), nb0);
G1 : entity work.and2(impl) port map (A(1), nb1, p1);
G2 : entity work.and3(impl) port map (A(0), nb1, nb0, p2);
G3 : entity work.and3(impl) port map (A(1), A(0), nb0, p3);
G4 : entity work.or3(impl) port map (p1, p2, p3, AgtB);
end struct;Nell'istanziazione si crea un'istanza di un blocco già definito collegando le sue porte ai segnali desiderati con la port map. La mappatura può essere posizionale (l'ordine è quello della entity, come sopra) o per nome (port map (a => A(1), b => nb1, y => p1)).
Tre modi di istanziare un componente
- Per componente: si dichiara
componentnella parte dichiarativa dell'architecture (prima dibegin), ripetendo le porte della entity, poi si istanzia:
component inv is port (x : in std_logic; y : out std_logic); end component;
...
INV1 : inv port map (input, output);- Diretta: si istanzia scrivendo entity e architecture,
entity work.inv(impl), senza dichiarare il componente (più compatta; è quella usata sopra). - Con package: i componenti sono dichiarati in un package che poi si importa con
use work.MyPackage.all;.
L'istanziazione è sempre contenuta nell'architecture, mai nella entity.
Livello di astrazione e risultato
| stile | descrive | livello | per quando |
|---|---|---|---|
| structural | componenti interconnessi | basso (schema) | gerarchie, controllo diretto della struttura |
| dataflow | equazioni booleane | intermedio | funzioni logiche compatte |
| behavioral | comportamento | alto | circuiti complessi, macchine a stati |
Una descrizione structural non porta automaticamente a una sintesi migliore di una behavioral (né viceversa): il sintetizzatore ottimizza. I tre stili si possono combinare nello stesso progetto. L'architecture più comune per i circuiti sequenziali è la behavioral (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 →).
Errori comuni
- Credere che VHDL sia sequenziale: tutte le assegnazioni concorrenti sono attive insieme.
- Mettere il
;dopo l'ultima porta nella listaport (...)(va dopo la parentesi chiusa). - Dichiarare
signaldopobegin(vanno prima). - Assegnare un valore a una porta di modo
in, o leggere una porta di modoout. - Dimenticare di ripetere
library/useprima di ogni entity. - Mescolare apici e virgolette:
'1'per un bit,"1"per un vettore di un bit.
Versione ripasso
- HDL, non linguaggio di programmazione: istruzioni concorrenti; usi: documentare, simulare, sintetizzare (solo sottoinsieme). Case insensitive, fortemente tipizzato, commenti
--. - Entity (una sola per blocco): nome e porte
in/out(in: solo lettura; out: assegnabile). Architecture (anche più d'una):architecture a of e is <component, signal, constant> begin <corpo> end a;.library IEEE; use IEEE.std_logic_1164.all;prima di ogni entity. - Tipi:
std_logic('0' '1' 'X' 'Z' 'U' '-'),std_logic_vector(n-1 downto 0)("010",x"1F", concatenamento&),bit,boolean,integer.signalinterni prima dibegin;constant. - Stili: structural (component e
port map, posizionale o per nome; istanziazione per componente, direttaentity work.e(a), con package), dataflow (equazioni), behavioral (comportamento). Comparatore a 2 bit: (VHDL - istruzioni concorrenti, process e testbenchLe istruzioni concorrenti VHDL sono l'assegnazione di segnale, when-else (logica prioritaria, condizioni valutate in ordine) e with-select (logica parallela: tutti i casi coperti da una sola scelta, others obbligatorio). Un process è un'istruzione concorrente il cui corpo è sequenziale (if, case, loop); parte quando cambia un segnale della sensitivity list (per la logica combinatoria: tutti gli ingressi); i segnali si aggiornano alla sospensione e vince l'ultima assegnazione. Un if senza else (o un caso non coperto) crea memoria non voluta. Il testbench è codice di simulazione con entity vuota, DUT istanziato e un process di stimoli con wait.VHDL - istruzioni concorrenti, process e testbench →). - Errori:
;dopo l'ultima porta;signaldopobegin; scrivere suin.