Erläuterungen

Im Definitionsbereich der Architektur werden die verwendeten Module und die Signale der Testbench deklariert. Es werden zumindest die Ein- und Ausgangs-Signale der UUT benötigt. Zusätzlich werden in unserem Fall aber auch noch einige Signale für die Testbench selber deklariert:

architecture Behavioral of TestSimpleLed is

    component SimpleLed
        port (
            Button  : in std_logic;
            Led     : out std_logic
        );
    end component;
    
    --Inputs
    signal Button : std_logic := '0'; -- Signal is high active
    
    --Outputs
    signal Led : std_logic;
    
    --Test
    signal TestCase : natural;
    signal TestErr : std_logic := '0';

begin

Eingangssignale der UUT bekommen Initialisierungswerte. Diese werden mit := zugewiesen (Normalerweise ist der Zuweisungsoperator für Signale <=. Initialisierungswerte werden allerdings mit := zugewiesen). Dadurch haben die Eingänge der UUT beim Start der Testbench definierte Pegel und die UUT macht nicht schon ab Anfang irgendetwas undefiniertes.

Da der Button High-Aktiv ist, wird er hier mit '0' initialisiert um zu signalisieren, dass er noch nicht gedrückt ist.

Die Signale TestCase und TestErr, sind zusätzliche Signale für die Testbench um den Ablauf des Tests im Simulationsfenster verfolgen zu können. Diese werden von der Testbench verwaltet.

Im Body der Testbench wird zuerst die UUT instantiiert:

    uut: SimpleLed port map (
        Button  => Button,
        Led     => Led
    );

Dabei werden die internen Signale der UUT mit den Signalen der Testbench verbunden. In diesem Fall sind die Signalnamen identisch was bei der Testbench üblich ist.

Zum anderen wird zumindest ein Prozess benötigt, welcher die UUT stimuliert und erkennt ob das Modul korrekt funktioniert. Diesen Prozess zu erstellen ist die eigentliche Aufgabe dieses Kapitels.

    stim_proc: process
    begin 
        wait for 100 ns;
        report "=======================================================";
        report "Only end of test and errors will be reported";

Jeder Prozess in VHDL hat einen Namen und enthält Anweisungen die in einen begin/end-Kontext eingebettet sind. Die obigen Anweisungen sind typisch für eine Testbench. Sie wären so nicht synthetisierbar aber für einen Test natürlich hilfreich.

        TestCase <= 1;
        wait for 100 ns;
        if (Led /= '0') then
            TestErr <= '1';
            report "Initial state failed";
        end if;

Im ersten Testcase wird zuerst das Testcase-Signal gesetzt, damit man später in der Ausgabe erkennen kann, welcher Testcase gerade durchgeführt wird. Zur Überprüfung der UUT können nun einfach die Ausgangssignale auf bestimmte Zustände getestet werden. Durch das Flag TestErr wird hier gezeigt, wann ein Fehler aufgetreten ist. Im Konsolenfenster würde man im Fehlerfall die entsprechende Fehlermeldung erhalten.

        TestCase <= 2;
        Button <= '1';
        wait for 100 ns;
        if (Led /= '1') then
            TestErr <= '1';
            report "Button pressed, but LED is off";
        end if;

Während im ersten Schritt die Reset-Bedingung getestet wurde wird im zweiten Schritt die UUT stimuliert (Button <= '1') und wiederum getestet, ob sich die Schaltung richtig verhält. Natürlich müsste man auf die Reaktion nicht 100 ns warten. Diese Zeit ist für eine sinnvolle Darstellung gewählt.

        TestCase <= 3;
        Button <= '0';
        wait for 100 ns;
        if (Led /= '0') then
            TestErr <= '1';
            report "Button released, but LED is on";
        end if;

Im dritten Testcase wird nur noch geprüft ob die LED nach dem Loslassen des Tasters auch wieder aus geht.

        TestCase <= 0;
        wait for 100 ns;
        TestErr <= not TestErr;
        wait for 30 ns;
        TestErr <= not TestErr;
        wait for 100 ns;
        if (TestErr = '0') then
            report "Test completed successfully";
        else
            report "Test completed with error(s)";
        end if;
        report "======================================================="; 
        assert false; -- stop simulation

Am Schluss der Stimulation wird nur noch entsprechend des bisherigen Verlaufs der Simulation der Erfolg/Misserfolg ausgegeben. Diese Art der Ausgabe ist rein willkürlich gewählt.

Bei mir wird am Ende ein Puls auf TestErr ausgegeben (durch dessen zweimalige Invertierung). Dieser zeigt nach oben (Daumen hoch), falls kein Fehler während des Tests aufgetreten ist, ansonsten nach unten (Daumen runter). 

        wait;
    end process;

end Behavioral;

Dann muss nur noch der Prozess und die Testbench abgeschlossen werden.

Weiter zu den Übungen