Salta al contenuto
Note per Studenti VHDL - logica combinatoria

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 4→14\to1 in quattro stili

vhdl
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.

vhdl
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 A=1A=1, 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

vhdl
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 3→83\to8: case sel is when "000" => y <= "00000001"; ... end case;. Il convertitore esadecimale-7 segmenti del laboratorio (uscite attive basse, segmenti g..ag..a):

vhdl
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 1616 parole da 77 bit, realizzata nelle LUT della FPGA.

Aritmetica con numeric_std

Con unsigned/signed si usano + - * e i confronti. Per sommare due numeri a NN bit con riporto in ingresso e in uscita si usa un bit in più:

vhdl
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) (→\to 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.

vhdl
-- 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 ii 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:

vhdl
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;

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 signal per il riporto nel ciclo for di un processo (il valore non è ancora aggiornato).
  • Applicare + a std_logic_vector senza convertirlo in unsigned.
  • Pensare che un for in VHDL sia un ciclo temporale: crea hardware replicato.

Versione ripasso

  • Stili: assegnazione condizionata when-else (priorità), selezionata with-select (nessuna priorità), processo con if/elsif o case (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; ⇒\Rightarrow latch (non AND). 'Z' ⇒\Rightarrow tri-state.
  • Display: case esadecimale →\to 7 segmenti (ROM 16×716\times7 nelle LUT).
  • numeric_std: unsigned/signed, to_integer, to_unsigned(n,size), resize, conversioni std_logic_vector(u); somma con un bit in più ('0' & x); shift con shift_left/right.
  • for / generate: hardware replicato, non cicli nel tempo; riporto del for in una variabile.
  • Strutturale: component + port map (per nome o posizione, open, costanti), entity work.X, generic map, gerarchia; ripple-carry con generate.
  • Errori: latch inferiti; sensibilità incompleta; segnale per il riporto; + su std_logic_vector; for temporale.

Esercizi su questo argomento

Teoria collegata