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