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)