Salta al contenuto
Note per Studenti VHDL - macchine a stati finiti e testbench sequenziale

VHDL - macchine a stati finiti e testbench sequenziale

In questa pagina 5

Una macchina a stati finiti (FSM) è un registro di stato più due reti combinatorie (Sintesi delle reti sequenziali - riconoscitore di sequenza e codifica degli statiSintesi di una rete sequenziale sincrona: specifiche $\to$ diagramma degli stati $\to$ tabella (riduzione degli stati equivalenti) $\to$ codifica binaria degli stati $\to$ scelta del FF (D PET) $\to$ equazioni di ingresso dei FF e delle uscite $\to$ minimizzazione $\to$ circuito e verifica. Lo stato riassume la parte di storia utile; per i riconoscitori si ha uno stato per ogni prefisso riconosciuto, riusando gli stati (sovrapposizioni). Reset: stato iniziale noto. Codifica Gray (numero minimo di FF) vs 1-hot (un FF per stato, logica più semplice): per il riconoscitore 1101 Gray costa circa la metà.Sintesi delle reti sequenziali - riconoscitore di sequenza e codifica degli stati →): lo stato futuro e l'uscita. In VHDL la struttura si riflette direttamente nel codice, con tre process che lavorano in parallelo. È lo schema richiesto in questo corso per scrivere una FSM, e il tipico terzo esercizio d'esame.

Il modello a tre process

  1. Registro di stato (parte sequenziale): sul fronte di clock aggiorna state <= next_state, e il reset porta in uno stato iniziale noto.
  2. Stato futuro (logica combinatoria): calcola next_state in funzione di state e degli ingressi.
  3. Uscita (logica combinatoria): la calcola in funzione di state (Moore) oppure di state e ingresso (Mealy).

I process 2 e 3 descrivono logica puramente combinatoria (liste di sensibilità con tutti gli ingressi, assegnazioni complete); solo il primo ha memoria. I tre process interagiscono attraverso i segnali comuni (state, next_state).

Il tipo enumerato

vhdl
type state_type is (S1, S2, S3, S4);          -- nuovo tipo con valori simbolici
signal state, next_state : state_type;

Con type si definisce un tipo elencando i valori tra parentesi, separati da virgole (enumerazione): serve per segnali con un numero finito di valori, indicati con nomi e non con numeri, per leggibilità. Lo stesso std_logic è un'enumerazione (9 valori). Il sintetizzatore sceglie la codifica binaria degli stati, a meno che non la si imponga (per esempio con costanti std_logic_vector, come nell'esempio del semaforo).

Esempio completo: riconoscitore di sequenza 1101 (Mealy)

Ingresso XX, uscita ZZ; Z=1Z=1 quando si è presentata la sequenza 110110 e X=1X=1; reset asincrono in S1S_1. Il diagramma è quello della sintesi (Sintesi delle reti sequenziali - riconoscitore di sequenza e codifica degli statiSintesi di una rete sequenziale sincrona: specifiche $\to$ diagramma degli stati $\to$ tabella (riduzione degli stati equivalenti) $\to$ codifica binaria degli stati $\to$ scelta del FF (D PET) $\to$ equazioni di ingresso dei FF e delle uscite $\to$ minimizzazione $\to$ circuito e verifica. Lo stato riassume la parte di storia utile; per i riconoscitori si ha uno stato per ogni prefisso riconosciuto, riusando gli stati (sovrapposizioni). Reset: stato iniziale noto. Codifica Gray (numero minimo di FF) vs 1-hot (un FF per stato, logica più semplice): per il riconoscitore 1101 Gray costa circa la metà.Sintesi delle reti sequenziali - riconoscitore di sequenza e codifica degli stati →).

vhdl
library IEEE;
use IEEE.std_logic_1164.all;

entity seq_rec is
  port ( clk, rst, X : in  std_logic;
         Z           : out std_logic );
end seq_rec;

architecture beh of seq_rec is
  type state_type is (S1, S2, S3, S4);
  signal state, next_state : state_type;
begin
  -- process 1: registro di stato con reset asincrono
  state_register : process (rst, clk)
  begin
    if rst = '1' then
      state <= S1;
    elsif clk'event and clk = '1' then
      state <= next_state;
    end if;
  end process;

  -- process 2: stato futuro = f(stato, ingresso)
  next_state_comb : process (X, state)
  begin
    case state is
      when S1 =>
        if X = '0' then next_state <= S1; else next_state <= S2; end if;
      when S2 =>
        if X = '0' then next_state <= S1; else next_state <= S3; end if;
      when S3 =>
        if X = '0' then next_state <= S4; else next_state <= S3; end if;
      when S4 =>
        if X = '0' then next_state <= S1; else next_state <= S2; end if;
    end case;
  end process;

  -- process 3: uscita = f(stato, ingresso)   (Mealy)
  output_comb : process (X, state)
  begin
    case state is
      when S4 =>
        if X = '1' then Z <= '1'; else Z <= '0'; end if;
      when others =>
        Z <= '0';
    end case;
  end process;
end beh;

Osservazioni.

Testbench

Due process in parallelo (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 →):

vhdl
library IEEE;
use IEEE.std_logic_1164.all;

entity test_seq_rec is
end test_seq_rec;

architecture test of test_seq_rec is
  signal clock, reset, X, Z : std_logic;
  signal test_sequence : std_logic_vector(0 to 10) := "01110101100";
  constant PERIOD : time := 100 ns;
begin
  DUT : entity work.seq_rec(beh) port map (clock, reset, X, Z);

  generate_clock : process
  begin
    clock <= '1'; wait for PERIOD/2;
    clock <= '0'; wait for PERIOD/2;
  end process;

  apply_inputs : process
  begin
    reset <= '1'; X <= '0';
    wait for 3*PERIOD/2;                 -- reset iniziale: il sistema parte da uno stato noto
    reset <= '0';
    for i in 0 to 10 loop
      X <= test_sequence(i);             -- un bit per ciclo, applicato sul fronte di discesa
      wait for PERIOD;
    end loop;
    std.env.stop;
  end process;
end test;

Il clock sale a 0,100,200,…0,100,200,\dots ns e scende a 50,150,…50,150,\dots; gli ingressi cambiano a 150,250,…150,250,\dots ns, cioè sul fronte di discesa, lontano dal fronte di salita. Il reset iniziale dura 150150 ns e porta lo stato in S1S_1.

Atteso con la sequenza 0111010110001110101100: stati (S1S_1 all'inizio) S1,S2,S3,S3,S4,S2,S1,S2,S3,S4,S1S_1,S_2,S_3,S_3,S_4,S_2,S_1,S_2,S_3,S_4,S_1 dopo i fronti successivi, e Z=1Z=1 solo durante il sesto bit (il bit in cui compare 11011101). Poiché è una macchina di Mealy, ZZ sale appena cambia l'ingresso (a 650650 ns, fronte di discesa), non al fronte di clock successivo: l'uscita può variare anche tra due fronti.

Esempio di Moore con vettore di uscita: controllore semaforico

Ingresso XX = auto in attesa in direzione E-O; sei luci in uscita (RNS,GNS,VNS,REO,GEO,VEO)(R_{NS},G_{NS},V_{NS},R_{EO},G_{EO},V_{EO}) (R = rosso, G = giallo, V = verde). Reset: verde N-S, rosso E-O. Se X=1X=1 la sequenza porta al verde E-O e poi ritorna al verde N-S; prima di ogni rosso c'è un giallo. Stati e uscite (Moore):

stato uscita luci significato se X=0X=0 se X=1X=1
VNS 001100 verde N-S, rosso E-O VNS GNS
GNS 010100 giallo N-S VEO VEO
VEO 100001 verde E-O, rosso N-S GEO GEO
GEO 100010 giallo E-O VNS VNS
vhdl
entity semaforo is
  port ( clk, rst, X : in  std_logic;
         luci        : out std_logic_vector(5 downto 0) );   -- (RNS, GNS, VNS, REO, GEO, VEO)
end semaforo;

architecture impl_beh of semaforo is
  type state_type is (VNS, GNS, VEO, GEO);
  signal state, next_state : state_type;
begin
  state_register : process (clk, rst)
  begin
    if rst = '1' then state <= VNS;
    elsif clk'event and clk = '1' then state <= next_state;
    end if;
  end process;

  next_state_comb : process (X, state)
  begin
    case state is
      when VNS => if X = '0' then next_state <= VNS; else next_state <= GNS; end if;
      when GNS => next_state <= VEO;
      when VEO => next_state <= GEO;
      when GEO => next_state <= VNS;
    end case;
  end process;

  output_comb : process (state)                 -- Moore: solo lo stato
  begin
    case state is
      when VNS => luci <= "001100";
      when GNS => luci <= "010100";
      when VEO => luci <= "100001";
      when GEO => luci <= "100010";
    end case;
  end process;
end impl_beh;

Un testbench che attraversa tutti gli stati e tutte le transizioni: reset; X=0X=0 per 2 cicli (resta in VNS); X=1X=1 per 3 cicli (entra nella sequenza); X=0X=0 per 2 cicli; X=1X=1 ancora per un nuovo ciclo completo.

Schema per l'esame

  1. Scrivere library/use e la entity (clock, reset, ingressi, uscite).
  2. Nell'architecture: tipo enumerato degli stati e segnali state, next_state.
  3. Process 1: registro di stato con reset (nell'esame si indica spesso "FF D positive edge triggered con reset asincrono").
  4. Process 2: case state con tutte le transizioni (default: restare).
  5. Process 3: uscite (solo state per Moore; anche l'ingresso per Mealy).
  6. Testbench: entity vuota, DUT, process del clock, process degli ingressi con reset iniziale e sequenza che raggiunge tutti gli stati (a volte "con il minor numero di transizioni").

Errori comuni

  • Dimenticare il reset (in simulazione lo stato iniziale non è definito: 'U').
  • Mettere nel process 2 o 3 una lista di sensibilità incompleta.
  • Descrivere un'uscita di Moore con una dipendenza dall'ingresso (diventa Mealy).
  • Non coprire tutti gli stati nel case (manca when others o uno stato): memoria involontaria.
  • Cambiare gli ingressi del testbench in coincidenza con il fronte di salita.
  • Pensare che l'uscita di Mealy cambi solo al fronte: può cambiare con l'ingresso.

Versione ripasso

Esercizi su questo argomento

Teoria collegata