1. Schritt (b: System Services Testbench)
Für das gerade geschriebene Modul muss noch die Testbench erstellt werden. Dabei soll überprüft werden, dass:
- während aktivem Reset nichts passiert
- der Zeitstempel nach Reset inkrementiert
- die Zeitbasis im spezifizierten Zeit Intervall taktet
- die Bits der Zeitbasis synchron ausgegeben werden
Die Simulation erfolgt dann im Schritt 1c.
Source-Code
Der Ausgangscode für diesen Schritt kann hier eingesehen werden:
Im weiteren Verlauf wird wo nötig der Ausgangscode und die notwendigen Änderungen erklärt.
Testprinzip
Nachdem wir uns versichert haben, dass der Zeitstempel nach Reset inkrementiert, verlassen wir uns auf ihn.
Eine Messfunktion ermittelt den Zeitstempel zum nächsten Auftreten eines Bits der Zeitbasis.
Dann muss nur noch überprüft werden, ob der Zeitstempel mit der Zeitbasis korreliert.
Zusätzliche Bibliotheken
Damit die Konvertierungen von std_logic_vector nach integer funktionieren müssen ganz oben noch use-Klauseln hinzugefügt werden:
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all ;
Die Messfunktion
Es wird mit Time-out auf das nächste Auftreten des Bits in der Zeitbasis gewartet und der zugehörige Zeitstempel zurückgegeben.
--! get timestamp at next time base event
procedure MeasureTimeStamp(constant maxCycles : in integer;
constant baseIdx : in integer; signal timeStamp : out integer) is
begin
timeStamp <= 0; -- initial state
for i in 1 to maxCycles loop -- timeout condition
wait until rising_edge(Ext_Clk); -- measure in clock ticks
if (TimeBase(baseIdx) = '1') then
timeStamp <= iTimeStampLo;
wait for 10 ps; -- Let variable value settle
return; -- got result
end if;
end loop;
end MeasureTimeStamp;
Im Gegensatz zur Synthese kann man in der Testbench natürlich aktiv auf Signale warten, wie hier auf die nächste steigende Taktflanke.
Wir könnten natürlich auch auf das entsprechende Bit in der Zeitbasis warten. Dann könnten wir aber keinen Time-Out realisieren.
Wir nehmen hier also implizit an, dass der Takt regelmäßig kommt, und dass die Zeitbasis von diesem Takt erzeugt wird. Da wir das Modul selbst geschrieben haben, ist diese Annahme aber zulässig.
Die Überprüfungsfunktion
Die Überprüfungsfunktion bekommt das Bit auf das überprüft werden soll und den erwarteten Wert des Zeitstempels. Dieser wird mit dem gemessenen Zeitstempel überprüft.
--! check time stamp
procedure CheckTimeStamp(constant maxCycles : in integer;
constant baseIdx : in integer; constant expected : in integer;
signal timeStamp : inout integer; signal TestErr : out std_logic) is
begin
MeasureTimeStamp(maxCycles, baseIdx, timeStamp);
if (timeStamp = expected) then
tb_report(" Waiting for TimeBase(" & integer'Image(baseIdx) &
"), expected TimeStamp: " & integer'Image(expected) & ": passed.");
else
TestErr <= '1';
tb_report(" Waiting for TimeBase(" & integer'Image(baseIdx) &
"), expected TimeStamp: " & integer'Image(expected) &
", found: " & integer'Image(timeStamp) &
": Failed!");
end if;
end CheckTimeStamp;
Hilfssignale
Um den Code etwas einfacher zu halten werden in der Testbench Integer-Signale verwendet. Diese werden aus den std_logic_vector Signalen erzeugt.
Da der Zeitstempel 55 Bit enthält und Integer Werte 32 wird dieser in MSBs und LSBs zerlegt.
signal TestStepErr : std_logic := '0'; -- Flag for error reporting
signal iTimeStampHi : integer; -- MSBs of time stamp
signal iTimeStampLo : integer; -- LSBs of time stamp
signal iTimeBase : integer; -- Time base as integer
signal iTimeStMsr : integer; -- LSBs of time stamp from measurement
Die Hilfssignale müssen jetzt noch berechnet werden. Das geschieht im Ausführungsteil außerhalb aller Prozesse:
iTimeStampLo <= to_integer(unsigned(TimeStamp(30 downto 0)));
iTimeStampHi <= to_integer(unsigned(TimeStamp(54 downto 31)));
iTimeBase <= to_integer(unsigned(TimeBase));
Da iTimeStampHi während unseres Tests nie verändert wird (schließlich haben wir nicht vor 2^32 Zyklen zu simulieren) wird dieses Signal aber wenigstens während der Simulation überprüft:
--! Check for test bench time out
p_checktime : process
begin
wait for 100 ns;
wait until iTimeStampHi /= 0;
-- this must not happen!
tb_report ("Timestamp error!");
assert false severity failure;
end process;
Wenn alles planmäßig läuft bleibt dieser Prozess beim Warten auf iTimeStampHi hängen und die Fehlermeldung wird nie erzeugt.
Die Testdurchführung
Im Stimulationsprozess werden nacheinander die einzelnen Tests durchgeführt.
Im ersten Schritt wird für ein paar Takte überprüft, ob während des aktivierten Resets die Ausgangssignale inaktiv bleiben:
TestCase <= 1;
tb_report("Test Case 1: Reset condition");
WaitCycles(10);
Reset <= '1';
WaitCycles(2);
for i in 0 to 99 loop
WaitCycles(1);
if (iTimeStampLo /= 0) or (iTimeBase /= 0) then
tb_report(" Expected TimeBase = 0 and TimeStamp = 0" &
"; Found TimeBase: " & integer'Image(iTimeBase) &
", TimeStamp: " & integer'Image(iTimeStampLo) &
": Failed!");
TestErr <= '1';
TestStepErr <= '1';
exit;
end if;
end loop;
WaitCycles(1);
if (TestStepErr = '0') then
tb_report(" Expected TimeBase = 0 and TimeStamp = 0: passed.");
end if;
TestStepErr <= '0';
Reset <= '0';
WaitCycles(1);
Überprüfung des Zeitstempels
Direkt nach dem Reset wird getestet ob der Zeitstempel losläuft. Das nur für sehr kurze Zeit, damit das erste Ereignis der Zeitbasis noch nicht aktiviert wurde.
TestCase <= 2;
tb_report("Test Case 2: Check TimeStamp counting");
for i in 0 to 7 loop
WaitCycles(1);
if (TimeStamp /= i) then
tb_report(" Expected TimeStamp = " & integer'Image(i) &
"; Found: " & integer'Image(iTimeStampLo) &
": Failed!");
TestErr <= '1';
TestStepErr <= '1';
exit;
end if;
end loop;
WaitCycles(1);
if (TestStepErr = '0') then
tb_report(" TimeStamp is counting correctly: passed.");
end if;
TestStepErr <= '0';
Überprüfung der Zeitbasis
Die Zeitbasis wird überprüft indem die Zeitstempel zur Zeit der Ereignisse überprüft werden. Da die Ereignisse zur gleichen Zeit wie das Inkrement-Signal kommen sollen, erwarten wir sie immer wenn ((Zeitstempel mod 10) == 9) gilt.
TestCase <= 3;
tb_report("Test Case 3: Check TimeBase events");
CheckTimeStamp( 20, 1, 9, iTimeStMsr, TestErr);
CheckTimeStamp( 20, 1, 19, iTimeStMsr, TestErr);
CheckTimeStamp( 200, 2, 99, iTimeStMsr, TestErr);
CheckTimeStamp( 200, 2, 199, iTimeStMsr, TestErr);
CheckTimeStamp( 2000, 3, 999, iTimeStMsr, TestErr);
CheckTimeStamp( 2000, 3, 1999, iTimeStMsr, TestErr);
CheckTimeStamp( 20000, 4, 9999, iTimeStMsr, TestErr);
CheckTimeStamp( 20000, 4, 19999, iTimeStMsr, TestErr);
CheckTimeStamp( 200000, 5, 99999, iTimeStMsr, TestErr);
CheckTimeStamp( 200000, 5, 199999, iTimeStMsr, TestErr);
CheckTimeStamp( 2000000, 6, 999999, iTimeStMsr, TestErr);
CheckTimeStamp( 2000000, 6, 1999999, iTimeStMsr, TestErr);
CheckTimeStamp( 200000, 5, 2099999, iTimeStMsr, TestErr);
CheckTimeStamp( 20000, 4, 2109999, iTimeStMsr, TestErr);
CheckTimeStamp( 2000, 3, 2110999, iTimeStMsr, TestErr);
CheckTimeStamp( 200, 2, 2111099, iTimeStMsr, TestErr);
CheckTimeStamp( 20, 1, 2111109, iTimeStMsr, TestErr);
Die obersten Bits der Zeitbasis werden aus Zeitgründen nicht simuliert.
Weiter zum 1. Schritt (c)