Salta al contenuto
Note per Studenti Esercizio 12 · descrizione VHDL di una macchina di Mealy con ingressi a e b e testbench (tema d'esame febbraio 2023)

Esercizio 12descrizione VHDL di una macchina di Mealy con ingressi a e b e testbench (tema d'esame febbraio 2023)

Esame
In questa pagina 4

Testo (tema d'esame febbraio 2023, quarto appello, esercizio 3). 1) Descrivere in VHDL la macchina a stati finiti con ingressi aa e bb (nel testo "x e y") e uscita zz, avente il seguente diagramma degli stati. In tutti i casi non indicati, il sistema non cambia stato e l'uscita è 0. Separare nel codice gli elementi di memoria dalle parti puramente combinatorie. 2) Creare un test bench in VHDL che faccia attraversare tutti gli stati.

Diagramma (dalla figura del tema): stato iniziale S0S_0 (freccia con rst=1rst=1, z=0z=0);

  • S0→S1S_0\to S_1 se a=1a=1 and b=1b=1, con z=1z=1;
  • S0→S3S_0\to S_3 se a=0a=0 and b=1b=1, con z=1z=1;
  • S1→S2S_1\to S_2 se a=1a=1, con z=0z=0;
  • S3→S2S_3\to S_2 se a=0a=0 or b=1b=1, con z=0z=0;
  • S2→S0S_2\to S_0 con etichetta "−-" (nessuna condizione) e z=1z=1.

Teoria usata: 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 →, VHDL - latch, flip-flop e reset sincrono e asincronoIn VHDL un elemento di memoria si descrive con un process il cui ramo non assegna sempre il segnale: latch D = process(D,C) con if C='1' then Q<=D; flip-flop positive-edge-triggered = process(CLK) con if CLK'event and CLK='1'. Reset asincrono: RST nella sensitivity list e testato per primo (if RST='1' ... elsif fronte); reset sincrono: solo CLK in lista e RST testato dentro al ramo del fronte (serve un fronte per averne effetto). Il testbench di un circuito sincrono ha un process che genera il clock e uno che applica gli ingressi lontano dal fronte attivo.VHDL - latch, flip-flop e reset sincrono e asincrono →.

Passo 1: tipo di macchina e tabella

L'uscita è scritta sulle frecce: macchina di Mealy. Differenza con l'esercizio gemello di giugno 2022 (Esercizio 3 · descrizione VHDL di una macchina a stati con ingressi x e y e testbench (tema d'esame giugno 2022)): le condizioni sono diverse e z=1z=1 anche nella transizione S2→S0S_2\to S_0. Tabella (stato futuro / zz; ingressi abab):

stato ab=00ab=00 0101 1010 1111
S0S_0 S0/0S_0/0 S3/1S_3/1 S0/0S_0/0 S1/1S_1/1
S1S_1 S1/0S_1/0 S1/0S_1/0 S2/0S_2/0 S2/0S_2/0
S2S_2 S0/1S_0/1 S0/1S_0/1 S0/1S_0/1 S0/1S_0/1
S3S_3 S2/0S_2/0 S2/0S_2/0 S3/0S_3/0 S2/0S_2/0

(S0S_0: con b=1b=1 si va in S3S_3 (se a=0a=0) o in S1S_1 (se a=1a=1), sempre con z=1z=1; con b=0b=0 resta, z=0z=0. In S3S_3 si resta solo con ab=10ab=10.) Quindi z=1z=1 in S0S_0 se b=1b=1, e sempre in S2S_2: z= rst ‾⋅(S0⋅b+S2).z=\overline{\,rst\,}\cdot\big(S_0\cdot b+S_2\big). Il diagramma indica z=0z=0 con rst=1rst=1: nell'uscita si tiene conto del reset.

Passo 2: codice VHDL

vhdl
library IEEE;
use IEEE.std_logic_1164.all;

entity fsm_ab is
  port ( clk, rst : in  std_logic;
         a, b     : in  std_logic;
         z        : out std_logic );
end fsm_ab;

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

  -- combinatoria: stato futuro
  next_state_comb : process (state, a, b)
  begin
    next_state <= state;                                 -- casi non indicati: resta
    case state is
      when S0 =>
        if    a = '1' and b = '1' then next_state <= S1;
        elsif a = '0' and b = '1' then next_state <= S3;
        end if;
      when S1 =>
        if a = '1' then next_state <= S2; end if;
      when S2 =>
        next_state <= S0;
      when S3 =>
        if a = '0' or b = '1' then next_state <= S2; end if;
    end case;
  end process;

  -- combinatoria: uscita di Mealy (stato e ingressi, e reset)
  output_comb : process (state, a, b, rst)
  begin
    z <= '0';                                            -- default
    if rst = '0' then
      case state is
        when S0 => if b = '1' then z <= '1'; end if;     -- sia (a=1,b=1) sia (a=0,b=1)
        when S2 => z <= '1';
        when others => null;
      end case;
    end if;
  end process;
end beh;

In S0S_0 la condizione di uscita si semplifica: le due transizioni con z=1z=1 (a=1,b=1a=1,b=1 e a=0,b=1a=0,b=1) coprono entrambe i valori di aa, quindi basta b=1b=1. Il process dell'uscita ha nella lista di sensibilità anche gli ingressi, perché è una Mealy.

Passo 3: testbench

Sequenza di (a,b)(a,b) che percorre tutti gli stati e tutte le 8 transizioni, con il valore atteso di zz (Mealy: zz nello stesso ciclo, in funzione di stato e ingressi):

ciclo 0 1 2 3 4 5 6 7 8 9
abab 11 01 10 00 01 10 00 11 10 00
stato presente S0S_0 S1S_1 S1S_1 S2S_2 S0S_0 S3S_3 S3S_3 S2S_2 S0S_0 S0S_0
zz 1 0 0 1 1 0 0 1 0 0
stato futuro S1S_1 S1S_1 S2S_2 S0S_0 S3S_3 S3S_3 S2S_2 S0S_0 S0S_0 S0S_0
vhdl
library IEEE;
use IEEE.std_logic_1164.all;

entity tb_fsm_ab is
end tb_fsm_ab;

architecture test of tb_fsm_ab is
  signal clk, rst, a, b, z : std_logic;
  constant PERIOD : time := 10 ns;
  type vec_array is array (0 to 9) of std_logic_vector(1 downto 0);
  constant tv : vec_array := ("11", "01", "10", "00", "01", "10", "00", "11", "10", "00");
begin
  DUT : entity work.fsm_ab(beh) port map (clk, rst, a, b, z);

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

  apply_inputs : process
  begin
    rst <= '1'; a <= '0'; b <= '0';
    wait for 3*PERIOD/2;
    rst <= '0';
    for i in 0 to 9 loop
      a <= tv(i)(1);
      b <= tv(i)(0);
      wait for PERIOD;
    end loop;
    std.env.stop;
  end process;
end test;

Gli ingressi cambiano sul fronte di discesa. zz è 11 ai cicli 0, 3, 4, 7 (due volte da S0S_0 con b=1b=1, due volte in S2S_2). La tabella del comportamento è stata verificata con un modello.

Errori comuni

  • Dimenticare che z=1z=1 anche nell'arco S2→S0S_2\to S_0.
  • Scrivere due if separati per le transizioni di S0S_0 sullo stesso segnale senza elsif: va bene per il comportamento, ma è facile sbagliare le priorità.
  • Mettere a e b fuori dalla lista di sensibilità del process dell'uscita (in simulazione zz non reagisce all'ingresso).
  • Usare z <= '1' in un case senza valore di default: memoria involontaria.

Versione ripasso

Teoria collegata