VHDL - logica combinatoria
In questa pagina 7
Una rete combinatoria ha le uscite funzione solo degli ingressi attuali (Porte logiche CMOS - struttura statica complementare e dimensionamentoUna porta CMOS statica complementare ha una rete di pull-down (PDN, n-MOS) verso massa e una di pull-up (PUN, p-MOS) verso V_DD, duali tra loro: serie ↔ parallelo. La PDN conduce quando la funzione f degli ingressi vale 1 e porta l'uscita a 0, quindi F = NOT f (le porte a uno stadio sono invertenti). Serie di n-MOS = AND, parallelo = OR; i p-MOS (chiusi con ingresso basso) fanno l'opposto. Escursione completa, nessun consumo statico, ritardo da R e C di uscita. Dimensionamento: si parte dall'inverter di riferimento (Z_n = 1, Z_p = r ≈ 2,4) e si scala ogni transistor del numero di transistor della serie peggiore a cui appartiene, così che la resistenza nel caso peggiore uguagli quella dell'inverter (NAND2: Z_n = 2, Z_p = r; NOR2: Z_n = 1, Z_p = 2r). Per un obiettivo R_pd = R*: Z_n = m R_n0/R* con m transistor in serie nel cammino peggiore. Per minimizzare la capacità di uscita si collegano all'uscita meno drain possibile.Porte logiche CMOS - struttura statica complementare e dimensionamento →). In VHDL (VHDL - struttura, tipi di dato e processiVHDL è un linguaggio di descrizione dell'hardware: un listato non è una sequenza di istruzioni eseguite da un processore ma la descrizione di un circuito, che la sintesi traduce in uno schema (LUT, flip-flop, multiplexer). Ogni modulo ha una entity (interfaccia: port con modo in, out, inout e tipo; eventuali generic) e una architecture (parte dichiarativa: segnali, costanti, componenti; parte assertiva dopo begin: assegnazioni concorrenti e processi). Le istruzioni nell'architecture sono concorrenti: l'ordine in cui sono scritte non conta. Un process esegue le proprie istruzioni in modo sequenziale quando un segnale della lista di sensibilità cambia. Segnali (<=, aggiornati alla fine del delta cycle, definiti nella parte dichiarativa) e variabili (:=, immediate, solo dentro il processo) hanno semantica diversa. Tipi: bit, boolean, integer, std_logic (a 9 valori, tra cui 'Z', 'X', 'U'), std_logic_vector, unsigned e signed (numeric_std).VHDL - struttura, tipi di dato e processi →) si descrive in tre modi equivalenti.
Multiplexer in quattro stili
entity mux4to1 is
port (x : in std_logic_vector(3 downto 0);
s : in unsigned(1 downto 0);
z : out std_logic);
end mux4to1;
-- 1) assegnazione condizionata (when-else): ha ordine di priorità
architecture bhv1 of mux4to1 is
begin
z <= x(0) when s = "00" else
x(1) when s = "01" else
x(2) when s = "10" else
x(3);
end bhv1;
-- 2) assegnazione selezionata (with-select): nessuna priorità
architecture bhv2 of mux4to1 is
begin
with s select
z <= x(0) when "00",
x(1) when "01",
x(2) when "10",
x(3) when others;
end bhv2;
-- 3) processo con if-then-else (priorità)
architecture bhv3 of mux4to1 is
begin
process (s, x)
begin
if s = "00" then z <= x(0);
elsif s = "01" then z <= x(1);
elsif s = "10" then z <= x(2);
else z <= x(3);
end if;
end process;
end bhv3;
-- 4) processo con case (nessuna priorità)
architecture bhv4 of mux4to1 is
begin
process (s, x)
begin
case s is
when "00" => z <= x(0);
when "01" => z <= x(1);
when "10" => z <= x(2);
when others => z <= x(3);
end case;
end process;
end bhv4;Si può anche scrivere direttamente z <= x(to_integer(s));. Le prime due forme sono concorrenti e si usano fuori dai processi; le altre due solo dentro process. with-select e case devono coprire tutti i valori (con when others); when-else e if/elsif hanno la priorità della prima condizione vera (si sintetizzano come una catena di multiplexer: più ritardo).
Latch inferiti: l'errore tipico
Nel processo di una rete combinatoria ogni uscita deve ricevere un valore in tutti i rami e la lista di sensibilità deve contenere tutti i segnali letti. Se in qualche ramo l'uscita non è assegnata, per VHDL deve mantenere il valore precedente: la sintesi inserisce un latch, cioè memoria.
process (A, B)
begin
if A = '1' then
Y <= B; -- se A = '0' Y non è assegnata: mantiene il valore (latch)
else
null;
end if;
end process;Questo codice descrive un latch trasparente quando , non una porta AND. Si corregge assegnando Y anche nell'else (per esempio Y <= '0';). Un analogo errore è dimenticare un caso del case senza when others. Il valore 'Z' assegnato in un ramo fa inferire un buffer tri-state. Esempio (tema d'esame): il processo
process (inA, inB, inC, sel)
begin
if sel = '1' then
oA <= iA; oB <= iB; oC <= iC;
else
oA <= iB; oB <= 'Z'; -- oC non assegnata nell'else
end if;
end process;descrive: per oA un multiplexer 2-1 (iA se sel='1', iB altrimenti); per oB un buffer tri-state (iB se sel='1', alta impedenza altrimenti); per oC un latch D (se sel='0' mantiene il valore precedente). In totale: un buffer tri-state, un mux 2-1 e un latch D (non tre multiplexer).
Decoder e display a 7 segmenti
Un decoder : case sel is when "000" => y <= "00000001"; ... end case;. Il convertitore esadecimale-7 segmenti del laboratorio (uscite attive basse, segmenti ):
process (data_in)
begin
case data_in is
when "0000" => seg <= "0000001"; -- 0
when "0001" => seg <= "1001111"; -- 1
when "0010" => seg <= "0010010"; -- 2
-- ...
when "1111" => seg <= "0111000"; -- F
when others => seg <= "1111111";
end case;
end process;È una ROM a parole da bit, realizzata nelle LUT della FPGA.
Aritmetica con numeric_std
Con unsigned/signed si usano + - * e i confronti. Per sommare due numeri a bit con riporto in ingresso e in uscita si usa un bit in più:
entity adder is
generic (N : integer := 16);
port (ci : in std_logic;
xin, yin : in std_logic_vector(N-1 downto 0);
sout : out std_logic_vector(N-1 downto 0);
co : out std_logic);
end adder;
architecture bhv of adder is
signal x, y : unsigned(N-1 downto 0);
signal sum : unsigned(N downto 0);
begin
x <= unsigned(xin);
y <= unsigned(yin);
sum <= ('0' & x) + y + 1 when ci = '1' else ('0' & x) + y;
sout <= std_logic_vector(sum(N-1 downto 0));
co <= sum(N);
end bhv;Funzioni: to_integer(u) ( intero), to_unsigned(n, size), to_signed(n, size), resize(u, new_size) (estende o tronca), conversioni di tipo std_logic_vector(u), unsigned(v). Lo shift si fa con shift_left(u, n), shift_right(u, n), rotate_left, rotate_right.
Cicli e generate: hardware replicato
Un for in un processo, o un generate, non è un ciclo nel tempo: si espande in più copie di hardware.
-- ripple-carry con ciclo for in un processo e variabile per il riporto
process (x, y, ci)
variable carry : std_logic_vector(N-1 downto 0);
begin
for i in x'reverse_range loop
if i = 0 then
carry(i) := (x(i) and y(i)) or (x(i) and ci) or (y(i) and ci);
s(i) <= x(i) xor y(i) xor ci;
else
carry(i) := (x(i) and y(i)) or (x(i) and carry(i-1)) or (y(i) and carry(i-1));
s(i) <= x(i) xor y(i) xor carry(i-1);
end if;
end loop;
co <= carry(N-1);
end process;carry è una variabile: si aggiorna subito, quindi il bit usa il riporto appena calcolato. Con un segnale la descrizione sarebbe sbagliata. La stessa logica con for ... generate (istruzione concorrente, indice i costante di elaborazione) e if ... generate per distinguere il primo stadio: ogni iterazione crea un'istanza di assegnazioni concorrenti con un segnale carry.
Descrizione strutturale
Si dichiara un componente (la sua interfaccia) e se ne creano istanze collegandone le porte con port map:
architecture STR of FULL_ADDER is
component NAND2 port (A, B : in std_logic; Z : out std_logic); end component;
component NAND3 port (A, B, C : in std_logic; Z : out std_logic); end component;
component XOR2 port (A, B : in std_logic; Z : out std_logic); end component;
signal X1, X2, X3, X4 : std_logic;
begin
G1: NAND2 port map (A => A, B => B, Z => X1);
G2: NAND2 port map (A => B, B => CI, Z => X2);
G3: NAND2 port map (A => CI, B => A, Z => X3);
G4: NAND3 port map (A => X1, B => X2, C => X3, Z => CO);
G5: XOR2 port map (A => A, B => B, Z => X4);
G6: XOR2 port map (CI, X4, S); -- associazione per posizione
end STR;- Associazione per nome (
porta => segnale) o per posizione (ordine della dichiarazione); una porta di uscita inutilizzata si lasciaopen; a un ingresso si può collegare una costante (A => '0'). - Un'istanza diretta della entity (senza dichiarare il componente) è
U2: entity work.AND2(behavioral) port map (...). - Con i generic (per esempio
N) si parametrizza il componente:U1: R8 generic map (N => 32) port map (D => A, CK => C, Q => Y);. - La gerarchia: il modulo di livello superiore (top) istanzia sottomoduli, ciascuno con la propria entity e architecture.
Sommatore ripple-carry strutturale: istanze di full adder in un
for ... generatecon segnaleC(N downto 0):C(0) <= CI;eFA_i: FA port map (A => A(i), B => B(i), CI => C(i), S => S(i), CO => C(i+1));(Sommatori - full adder e ripple-carryLa somma di due bit con riporto in ingresso è realizzata dal full adder: S = A ⊕ B ⊕ C_in, C_out = AB + BC_in + AC_in. Con generate G = AB, propagate P = A ⊕ B (e delete D = Ā B̄) si scrive C_out = G + P·C_in e S = P ⊕ C_in: G e P non dipendono dal riporto in ingresso (fase di set-up), solo C_out ed S ne dipendono. Il sommatore ripple-carry collega N full adder in cascata: il riporto attraversa gli stadi uno dopo l'altro, nel caso peggiore t_add = (N−1) t_carry + t_sum (lineare in N). Il ritardo effettivo dipende dagli operandi: un generate o un delete azzera la catena, un propagate la prolunga; per operandi uguali bit a bit vale t_carry + t_sum.Sommatori - full adder e ripple-carry →).
Errori comuni
- Lasciare un'uscita non assegnata in un ramo: latch inferito (esempio
if A='1' then Y<=B; else null;). - Lista di sensibilità incompleta: la simulazione non riproduce l'hardware sintetizzato.
- Usare un
signalper il riporto nel ciclofordi un processo (il valore non è ancora aggiornato). - Applicare
+astd_logic_vectorsenza convertirlo inunsigned. - Pensare che un
forin VHDL sia un ciclo temporale: crea hardware replicato.
Versione ripasso
- Quattro stili per un mux :
when-else(assegnazione concorrente con priorità: catena di mux, più ritardo);with-select(concorrente, senza priorità, conwhen others);if/elsifin un processo (priorità);casein un processo (senza priorità, conwhen others). Si può anche scriverez <= x(to_integer(s));. Le prime due forme sono concorrenti, le altre solo dentroprocess. - Latch inferito: in un processo combinatorio ogni uscita va assegnata in tutti i rami e la sensibilità deve contenere tutti i segnali letti. Esempio
if A='1' then Y<=B; else null; end if;: per mantiene il valore, quindi è un latch trasparente per , non una AND. Si corregge conY <= '0';nell'else. Un'Z'assegnato in un ramo fa inferire un buffer tri-state. - Esempio d'esame: nel processo con
if sel='1'(, , ) eelse(, , non assegnata) si ottengono un multiplexer su , un buffer tri-state su e un latch D su , non tre multiplexer. - Decoder e display: un decoder è un
casesusel; il convertitore esadecimale-7 segmenti è uncasesudata_inconwhen others, cioè una ROM realizzata nelle LUT (FPGA - LUT, slice e risorse della famiglia 7Una FPGA è una matrice di blocchi logici configurabili (CLB) immersi in una griglia di interconnessioni programmabili, con risorse dedicate: block RAM, blocchi DSP, gestione del clock (MMCM, PLL), blocchi di I/O e transceiver. La memoria di configurazione è SRAM. L'elemento base è la LUT: una memoria SRAM con 2^k celle che realizza una qualsiasi funzione di k ingressi (i segnali di selezione del multiplexer sono gli ingressi della funzione). Nella famiglia 7 di Xilinx un CLB ha 2 slice; uno slice ha 4 LUT a 6 ingressi (ciascuna divisibile in due LUT a 5), multiplexer larghi F7/F8 (funzioni a 7-8 ingressi), una catena di riporto veloce, 4 flip-flop/latch più 4 flip-flop. Nei SLICEM la LUT può essere usata come RAM distribuita o registro a scorrimento (SRL32), nei SLICEL solo come logica.FPGA - LUT, slice e risorse della famiglia 7 →). - Aritmetica con
numeric_std:unsigned/signed,to_integer,to_unsigned(n,size),resize, conversionistd_logic_vector(u)eunsigned(v); per il riporto si usa un bit in più,sum <= ('0' & x) + y + ci. Shift conshift_left,shift_right,rotate_left,rotate_right(Traslatori, comparatori e moltiplicatoriOltre ai sommatori, la ALU contiene traslatori (shifter), comparatori e moltiplicatori. Lo shift a sinistra di k posizioni moltiplica per 2^k; lo shift a destra aritmetico (con replica del bit di segno) divide per 2^k nel complemento a due; il barrel shifter realizza qualsiasi traslazione fino a N − 1 posizioni con log₂N livelli di multiplexer 2→1. Il comparatore di uguaglianza è l'AND delle XNOR bit a bit; quello di grandezza confronta partendo dal bit più significativo (GT = g_{N−1} + e_{N−1}(g_{N−2} + e_{N−2}(…))) oppure usa la sottrazione e le flag. Il moltiplicatore a matrice (array) forma N² prodotti parziali con porte AND e li somma con N(N−1) full adder; il ritardo cresce come 2N t_carry; moltiplicatori ad albero (Wallace) e sequenziali (shift-and-add) sono alternative; le FPGA hanno blocchi DSP dedicati.Traslatori, comparatori e moltiplicatori →). - For e generate: non sono cicli nel tempo ma hardware replicato. Un riporto di ripple-carry in un
fordeve essere una variabile (carry(i-1)si aggiorna subito); con un segnale la descrizione sarebbe sbagliata. Confor ... generatee segnaleC(N downto 0)si ottiene un ripple-carry strutturale (Sommatori - full adder e ripple-carryLa somma di due bit con riporto in ingresso è realizzata dal full adder: S = A ⊕ B ⊕ C_in, C_out = AB + BC_in + AC_in. Con generate G = AB, propagate P = A ⊕ B (e delete D = Ā B̄) si scrive C_out = G + P·C_in e S = P ⊕ C_in: G e P non dipendono dal riporto in ingresso (fase di set-up), solo C_out ed S ne dipendono. Il sommatore ripple-carry collega N full adder in cascata: il riporto attraversa gli stadi uno dopo l'altro, nel caso peggiore t_add = (N−1) t_carry + t_sum (lineare in N). Il ritardo effettivo dipende dagli operandi: un generate o un delete azzera la catena, un propagate la prolunga; per operandi uguali bit a bit vale t_carry + t_sum.Sommatori - full adder e ripple-carry →). - Strutturale: si dichiarano
componente si creano istanze conport map(per nome con=>o per posizione; porte inutilizzateopen; costanti come'0');entity work.AND2(behavioral)istanzia direttamente un'entity;generic mapparametrizza (); il top istanzia sottomoduli (VHDL - struttura, tipi di dato e processiVHDL è un linguaggio di descrizione dell'hardware: un listato non è una sequenza di istruzioni eseguite da un processore ma la descrizione di un circuito, che la sintesi traduce in uno schema (LUT, flip-flop, multiplexer). Ogni modulo ha una entity (interfaccia: port con modo in, out, inout e tipo; eventuali generic) e una architecture (parte dichiarativa: segnali, costanti, componenti; parte assertiva dopo begin: assegnazioni concorrenti e processi). Le istruzioni nell'architecture sono concorrenti: l'ordine in cui sono scritte non conta. Un process esegue le proprie istruzioni in modo sequenziale quando un segnale della lista di sensibilità cambia. Segnali (<=, aggiornati alla fine del delta cycle, definiti nella parte dichiarativa) e variabili (:=, immediate, solo dentro il processo) hanno semantica diversa. Tipi: bit, boolean, integer, std_logic (a 9 valori, tra cui 'Z', 'X', 'U'), std_logic_vector, unsigned e signed (numeric_std).VHDL - struttura, tipi di dato e processi →). - Errori: latch inferiti per uscite non assegnate; lista di sensibilità incompleta; segnale per il riporto nel
for;+sustd_logic_vectorsenza conversione; pensare che ilforsia un ciclo temporale.