This phase is as important as the design phase, and as a general rule, the amount of time spent simulating should be about twice the design time.
Gina R. Smith, FPGA designer
Un sistema descritto in VHDL viene solitamente simulato per analizzarne il comportamento, stimolando i possibili ingressi e verificando se i segnali in uscita sono quelli attesi. Si crea quindi un file di testbench che fornirà gli stimoli necessari.
La procedura di simulazione è composta dall’istanziazione del sistema da testare (UUT, unity under test) e dagli stimoli da applicare alla UUT.
Ora non ci resta che verificare la risposta in uscita del Multiplexer con Xilinx ISim.
Tech Stack
[1] Utilizzo di Ise Project Navigator e ISE Simulator.
Step 1 – Creare un nuovo progetto
Questa volta non utilizzeremo come Top Module lo schematico come avevamo già visto in questo tutorial , ma lavoreremo direttamente sul codice VHDL.
Appena abbiamo creato il nuovo progetto “myhdl_mux” selezioniamo Add Source (1) in modo da importare i 2 files che abbiamo creato precedentemente durante la compilazione in Python che si trovan0 nella cartella di default del progetto.
Selezionati i files ci apparirà una schermata come la seguente e ci basterà premere OK.
Dopo aver importato i files .VHD la prima cosa che dobbiamo fare è controllarne la sintassi selezionado da Synthesize -XST (1) la voce Check Syntax (2) ed infine Run (3).
Se il controllo della sintassi va a buon fine apparirà il check mark verde:
Step 2 – VHDL Test Bench
Prima di procedere con la simulazione comportamentale dobbiamo creare un nuovo file Test Bench che fornisca gli stimoli necessari per la simulazione quindi click destro e selezioniamo New Source.
Scegliamo VHDL Test Bench (1) e gli diamo un nome sognificativo (2) come tb_mux e premiamo Next (3). Associando nella schermata successiva il testbench con il source mux premendo Next e poi Finish.
Step 3 – Inizializzazione della simulazione
In automatico verrà creato un template di codice VHDL con il componente che andremo a testare. Come nei linguaggi di programmazione nell’intestazione possiamo importare delle librerie e dei packages che aggiungendo funzionalità al VHDL standard, infatti come possiamo vedere utilizziamo il package IEEE 1164.
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY tb_mux IS
END tb_mux;
ARCHITECTURE behavior OF tb_mux IS
COMPONENT mux
PORT(
s : IN std_logic;
o : OUT std_logic;
I0 : IN std_logic;
I1 : IN std_logic
);
END COMPONENT;
--Inputs
signal s : std_logic := '0';
signal I0 : std_logic := '0';
signal I1 : std_logic := '0';
--Outputs
signal o : std_logic;
In fase di inizializzazione (tempo 0+0δ) della simulazione vengono assegnati ai segnali in input dei valori attraverso l’operatore :=
--Inputs
signal s : std_logic := '0';
signal I0 : std_logic := '0';
signal I1 : std_logic := '0';
La fase di inizializzazione termina quando tutti i processi raggiungono lo stato di sospensione. Ora la simulazione è pilotata dagli eventi che vanno indicati negli stimoli.
Step 4 – Generazione degli stimoli
Per simulare un sistema descritto in VHDL abbiamo bisogno di fornire degli stimoli (stimulus) all’entità sotto verificia (UUT, unity under test) per controllare se i segnali di uscita erano quelli attesi.
Il template contiene anche delle righe di codice che fanno riferimento al Clock che noi non utilizziamo e che provederemo a rimuovere per evitare errori durante la simulazione.
Dopo aver rimosso ogni riferimento al clock dal process body possiamo modicarlo come segue:
BEGIN
uut: mux PORT MAP ( -- interfaccia dell'unità sotto verifica
s => s,
o => o,
I0 => I0,
I1 => I1
);
-- Stimulus process
stim_proc: process
begin
I0 <= '1'; -- assegnamenti
I1 <= '0';
s <= '0';
wait for 20 ns;
s <= '1';
wait for 20 ns;
I0 <= '0';
I1 <= '1';
s <= '0';
wait for 20 ns;
s <= '1';
wait for 20 ns;
end process;
END;
Assegno manualmente una serie di valori opportuni ai segnali e di istruzioni wait per descrivere gli stimoli. Quando assegniamo ‘0’ ad un segnale dopo di 20 ns, in realtà stiamo producendo un evento che avrà luogo entro 20 ns.
All’interno del corpo di un processo gli statement sono sequenziali come anche le assegnazioni di segnali attraverso l’operatore <= .
Quando il flusso di esecuzione del processo incontra l’istruzione wait viene sospeso e la sua esecuzione è ripresa quando la condizione richiesta è verificata.
nome_process: process
dichiarazione
begin
wait; -- sospensione
end process nome_process;
Va ribadito che nella dichiariazione di un processo non si può utilizzare sia wait che una sensitivity list questo perché le modalità di esecuzione e sospensione di un processo sono molto differenti. Infatti nel caso di una sensitivity list un processo entra in esecuzione solo se uno dei segnali elencati cambia il proprio stato a causa di un evento e viene sospeso quando raggiunge l’end process.
nome_process: process (segnale_1, segnale_n) --sensitivity list
dichiarazione
begin
process body
end process nome_process; -- sospensione
Se volessimo ottenere un comportamento analogo ad una sensitivity list con l’istruzione wait dovremmo scrivere alla fine del processo:
nome_process: process
dichiarazione
begin
process body
wait on (segnale_1, segnale_n); -- aspetta un evento su uno dei segnali
end process nome_process;
Step 5 – Simulation
Selezioniamo il toggle button con la scritta Simulation (1) e il testbench (2) che abbiamo creato e ne controlliamo sintassi (3).
Se il check syntax va a buon fine apparirà il check mark di colore verde e potremo selezionare Simulate Behavioral Model (4) e premere Run.
Step 6 – Output waveform
Ora non ci resta che controllare i valori del diagramma temporale e confrontarli con la Truth Table per sapere se il nostro MUX descritto in Python con MyHDL funziona correttamente.
Ricordandoci che il segnale $$S$$ è il segnale di selezione a 1bit , e sono gli ingressi e è l’uscita.
Dalla Truth Table sappiamo che se in uscita avremo il valore di e viceversa se l’uscita sarà e andando a controllare il diagramma temporale ritroviamo i valori attesi. Per esempio all’istante 167 ns con abbiamo come valore in uscita quello di