Testen einer VHDL-Beschreibung in einer Testbench

Unit Under Test (uut.vhd):

Zustandsautomat in VHDL

-- Die benötigten Libraries für das nachfolgende Entity
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;

use ieee.numeric_std.all;

ENTITY automat IS
PORT(
rst, clk : IN std_logic;
e : IN std_logic_vector(1 DOWNTO 0);
a : OUT std_logic_vector(1 DOWNTO 0)
);
END automat;

ARCHITECTURE v1 OF automat IS
-- Signale
-- Der Zustand wird einfach in ein Signal gespeichert
-- unsigned wäre z.B. auch kein Problem
SIGNAL zustand : std_logic_vector(2 DOWNTO 0);
BEGIN

-- ACHTUNG HIER FALSCH:
-- a <= "00"
-- Solche nebenläufigen Zuweisungen passieren immer gleichzeitig
-- Signalzuweisungen innerhalb eines Prozesses finden sequenteille, aber erst beim END PROCESS statt
-- Resultat wäre also, dass die nebenläufigen Zuweisungen in ARCHITECTURE mit den Zuweisungen in Process konkurrieren
-- und deshalb versuchen a auf "00" sowie dem berechneten Wert weiter unten zu treiben,
-- das führt zu "XX"

-- Wenn sich entweder RESET oder CLOCK ändern, so führe den Prozess aus
PROCESS(rst, clk)
BEGIN

-- Die höchste Priorität hat RESET, nur wenn dieses Signal nicht gesetzt ist kümmern wir uns um den Rest
IF rst = '1' THEN
report "reset signal angekommen";
-- Kehre zu Z0 zurück und verweile solange RST aktiv ist.
report "setze zustand auf 000";
zustand <= "000"; -- Vektoren haben immer doppelte Anführungsstriche, Bits einfache

ELSIF rising_edge(clk) THEN
-- Je nach Zustand macht führt eine Eingabe zu anderen Folgezuständen
-- Jedem Zustand ist aber genau eine Ausgabe zugeordnet
CASE zustand IS

WHEN "000" =>
-- Zustand Z0
IF e = "01" THEN
zustand <= "001";
--report "wechsel in zustand Z1";
ELSIF e = "11" THEN
zustand <= "010";
ELSIF e(0) = '0' THEN -- Unnötig!
zustand <= "000";
END IF;

WHEN "001" =>
-- Zustand Z1
IF e(0) = '0' THEN
zustand <= "100";
ELSIF e(0) = '1' THEN
zustand <= "010";
END IF;

WHEN "010" =>
-- Zustand Z2
IF e = "00" THEN
zustand <= "010";
ELSIF e = "01" THEN
zustand <= "011";
ELSIF e(1) = '1' THEN
zustand <= "001";
END IF;

WHEN "011" =>
-- Zustand Z3
zustand <= "000";

WHEN "100" =>
-- Zustand Z3
zustand <= "100";

-- MUSS mit angegeben werden!
WHEN OTHERS =>
zustand <= "000"; -- Zum Beispiel, ist sicherer

END CASE;

END IF;
END PROCESS;

-- Gute Lösung! Man könnte die Ausgabe auch oben direkt einfügen, wäre aber weniger schön!
-- Alternative wäre Concurrent Statement:
-- a <= "00" when z = "00"
-- else "11" when z = "01"
-- ...
-- else "00"
PROCESS(zustand)
BEGIN

CASE zustand IS
WHEN "000" =>
report "zustand wurde veraendert auf 000";
a <= "00";
report "a wurde daher auf 00 gesetzt";
WHEN "001" =>
a <= "11";
report "a wurde auf 11 gesetzt";
WHEN "010" =>
a <= "10";
report "a wurde auf 10 gesetzt";
WHEN "011" =>
a <= "01";
report "a wurde auf 01 gesetzt";
WHEN "100" =>
a <= "00";
report "a wurde auf 00 gesetzt";
WHEN OTHERS =>
report "Others aufgerufen, Null!";
null;
END CASE;

END PROCESS;

END v1;

Testumgebung (uut_testbench.vhd):

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
use ieee.numeric_std.all;

-- Wie immer beginnt die Quelldatei mit einer Entity
-- Es handelt sich sozusagen um die Hardware "außen herum" die unsere eigentliche Hardware testen soll
entity testbench is
-- Die Entity einer Testbench hat allerdings keine Eingangsports
-- Sie soll ja gerade selbständig die Hardware testen
end testbench;

architecture behav of testbench is
    -- Interne Signale, sprich die Ein- und Ausgabesignale des Bauteils
    signal rst, clk : std_logic;
    signal e, a : std_logic_vector(1 downto 0);
    
begin
    uut_0: entity work.automat port map (rst=>rst, clk=>clk, e=>e, a=>a); -- Alternative Schreibweise: rst=>rst, clk=>clk, ...

    
    -- Wir benötigen jetzt noch einen Prozess, der die Testsignale für die "unit under test" erzeugt und die Ausgabe auswertet
    process
    begin

            report "sende reset signal";
            rst <= '1';    
            wait for 20000 ns;

            assert a = "00"
                report "Trotz Reset ist die Ausgabe nicht 00, sondern: " & std_logic'image(a(1)) & std_logic'image(a(0))
                severity failure;
                
            assert a /= "00"
                report "a ist 00"
                severity note;
                
            rst <= '0';    
            wait for 10 ns;
            
            clk <= '0';
            e <= "01";
            wait for 10 ns;
            clk <= '1';
            wait for 10 ns;
            
            assert a = "11"
                report "a ist nicht 11, sondern: " & std_logic'image(a(1)) & std_logic'image(a(0))
                severity note;

            
            
            wait;    -- ansonsten läuft der Prozess immer weiter
    end process;
end behav;

Ausführen mit:

ghdl -c uut.vhd uut_testbench.vhd -r testbench