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
- Stili: assegnazione condizionata
when-else(priorità), selezionatawith-select(nessuna priorità), processo conif/elsifocase(copre tutti i valori,when others);z <= x(to_integer(s)). - Latch inferito: nel processo combinatorio ogni uscita assegnata in tutti i rami e lista di sensibilità completa;
if A='1' then Y<=B; else null;latch (non AND).'Z'tri-state. - Display:
caseesadecimale 7 segmenti (ROM nelle LUT). - numeric_std:
unsigned/signed,to_integer,to_unsigned(n,size),resize, conversionistd_logic_vector(u); somma con un bit in più ('0' & x); shift conshift_left/right. - for / generate: hardware replicato, non cicli nel tempo; riporto del
forin una variabile. - Strutturale:
component+port map(per nome o posizione,open, costanti),entity work.X,generic map, gerarchia; ripple-carry congenerate. - Errori: latch inferiti; sensibilità incompleta; segnale per il riporto;
+sustd_logic_vector;fortemporale.