Functional Test <-> Unit (System) Test

Funktionstest
– Der Kunde beurteilt: Macht das Gerät a und b und c?
– Reines Abfragen von Anforderungen

Systemtest
– Produzenten beurteilen das Gerät: Wird a , b und c korrekt nach den heutigen Spezifikationen verarbeitet?- Wie interagieren die einzelnen Blöcke untereinander ?

Hier das Hausbeispiel als Illustration zum Unterschied unter den beiden Testes (gelber Post)
Hier ein Artikel

Boundary Scan Test

Ist die am häufigsten verwendete Testmethode in der Digitaltechnik. Das Grundprinzip ist, dass parallel zu den Ein-und Ausgängen FlipFlops zur Abbildung des aktuellen Zustands der Pins, aber auch zur Imitation von Pin-Zuständen eingebaut werden.

Die Flip-Flop Reihe, bilden ein Schieberegister, das die Zustände der IOS abbildet bzw. selbst bildet. Dieses Schieberegister ist SCAN CHAIN genannt.


Um die Daten der Scan Chain  zu erhalten, braucht es die JTAG-Schnittstelle.

Die Scan Chain kann den aktuellen Zustand der IOs über die JTAG-Schnittstelle TDO nach aussen geben.

Sie kann auch virtuell Input Werte setzen über die Schnittstelle TDI. Dies dient der Simulation, bei der ein Verhalten von Eingängen simuliert wird.

 

 

 

JTAG: Built in Testing

Von der Prüfspitze zu Testpins
Früher wurde ausschliesslich mit der „Nadelmethode“ Testnadeln an ausgewählte Stellen (Knoten) angelötet, um den Signalverlauf zu testen.
Da die Bauteile immer kleiner werden ist dies physisch fast nicht mehr möglich. An den Teststellen (Knoten) werden Leitungen an die Oberfläche fix geführt. Ist dies in einem Chip (Built in) so enstehen Pins am Chip-Ausgang, die Zugang zu den Knoten bieten.

JTAG-Standard
Der IEEE 1149.1 Standard ist von der Gruppe JTAG verfasst und definiert eine Schnittstelle für Boundary Scan Test in Chips. Detailierter Artikel JTAG

Über die JTAG-Schnittstelle, auch TAP = Test Access Port genannt kann auf die SCAN CHAIN (siehe Boundary Scan) auf die Ein-und Ausgänge des Chips zugegriffen werden.

scanChain

TAP Schnisstelle
JTAG definiert 4 Signale:
– TCK: Der Clock
– TMS: Der Testmode
– TDI: Die Datenleitung für Input (Simulation)
– TDO: Die Ausgabe des aktuellen Zustands an den Ein-und Ausgängen.

 

..

SignalTap

Über den USB-Blaster kann bei altera mit dem Programm SignalTap der Verlauf der internen Signale sichtbar gemacht werden. Der Vorteil zu einem Logic Analyser ist, dass SignalTap intern funktioniert und damit die Signale nicht zuerst nach aussen geführt werden müssen.

Die Screenshots sind von der Präsentation: de.slideshare.net/… quartus tutorial signaltap

 

Die Signale werden durch Doppelklick bei „node“ ausgewählt. Mit einem * erhält man alle Signale.
singaltap_select_signal

Der Tackt muss angegeben werden:

singaltap_set_clock
clock setzen

Nach dem Clk sind die Grundeinstellungen wichtig. Kleine FPGAs haben nicht viel Speicher und zeichnen deshalb nur wenig Signale auf…
singaltap_configurations

Am Schluss wird die Konfiguration ins FPGA geladen. (Zuvor sollte man das Projekt .sof bereits einmal hinuntergealden haben.)
singaltap_download_signaltap

Die Signale erscheinen so:
singaltap_analysis

Will man ein Signal detaillierter anschauen, so geht dies so:
singaltap_analysis_more_details

VHDL Simulation: Wait statements

Neben den Assert statements (Vergleiche) basiert die Simulation vor allem auf den richtigen Pausen.

Warten bis…
–  Zustand eintrifft   WAIT UNTIL (s_midi = „1011);  wait until (clk = ‚1‘)
.  sehr wichtig für Koordination unter parallelen Prozessen.
–  Signal wechselt     wait on s_enable_datat;
–  Eines der Signale wechselt  wait on s_note_1, s_note_2, s_note_3;
.                 (nicht 100% sicher, ob nur 1 Signale oder alle)
–  Eine gewisse Zeit verstrichen ist wait for 10 us  wait for 1 * CLK_HALFPERIOD
–  Unbestimmt wait;  Hilft Simulation bei internen timing.
.   (Falls ein parameter noch nicht frei ist, wird gewartet.)
.   Ist z. B. sinnvoll nach read(file) ;

waits dürfen nur in processen eingebaut werden

Einfluss auf den Programmfluss
–  Wait-Statements sind sequentiell und stoppen den laufenden Prozess für diese Zeit
–  Stoppt der Prozess, werden die davor zugewiesenen Signale zugewiesen
– Ein wait aktualisert auch den Zeitstempel. Ohne waits in einem Process, kann nie die abgelaufene Zeit zum Simulieren übergeben (und das Programm beendet) werden. Der Prozess hängt in einem endlos loop.
– Ein (unbestimmtes) wait am Ende eines loop regelt selbständig, wie lang das Programm warten kann, bis wieder oben begonnen werden kann. (So entfiel z.B. die Fehlermeldung: „Can’t open file. Already used.“ Sobal die Datei wieder frei war, machte die Simulation weiter.

Testbench: Debuggen per Konsole

Assert Statment
Führen einen Vergleich aus. Stimmt der Vergleich, so passiert nichts. Nur wenn der Vergleich falsch ist, dann wird der Report ausgegeben.

ASSERT (s_midi = "010110") REPORT "Midi-value wrong" SEVERITY NOTE;

Es gibt vier Levels von severity reports: NOTE, WARNING, ERROR, FAILURE.
Das letzte Level (failure) bricht die Simulation ab.

Ist das Testing textbasiert, so steht oft auf der rechten Seite der eingelesene Wert, den das Signal haben soll (aus Textfile):

ASSERT (s_midi = note_1) REPORT "Note 1 falsch" SEVERITY WARNING;

 

Asserts als Passed-Points
Will man nur wissen, ob die Simulation an einer gewissen Stelle durchgekommen ist, wird Assert false  (= immer ausgeben) für die Kommandoausgabe gebraucht.

ASSERT false REPORT "Read in finished" SEVERITY NOTE;

Textbasierte Testbench  Passed-Point
Falls die textio-Bibliothek eingebunden und  variable line_out: Line; definiert wurde, kann man mit der Funktion write()  Text auf die Konsole schreiben.

write(line_out, string'("Read in finished."));
writeline(OUTPUT, line_out);

Der Text muss (leider) zuerst in den Zeilenbuffer Line gespeichert werden, und dann per OUTPUT  der Konsole übergeben. Die Definition dieser Varialblen steht in der Datei std_logic_textio.vhd.

std_logic_textio.vhd

Die Vollversion:

use STD.textio.all;
library IEEE;
use IEEE.std_logic_1164.all;

package STD_LOGIC_TEXTIO is
--synopsys synthesis_off
	-- Read and Write procedures for STD_ULOGIC and STD_ULOGIC_VECTOR
	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC);
	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC; GOOD: out BOOLEAN);
	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR);
	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR; GOOD: out BOOLEAN);
	procedure WRITE(L:inout LINE; VALUE:in STD_ULOGIC;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);
	procedure WRITE(L:inout LINE; VALUE:in STD_ULOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);

	-- Read and Write procedures for STD_LOGIC_VECTOR
	procedure READ(L:inout LINE; VALUE:out STD_LOGIC_VECTOR);
	procedure READ(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN);
	procedure WRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);

	--
	-- Read and Write procedures for Hex and Octal values.
	-- The values appear in the file as a series of characters
	-- between 0-F (Hex), or 0-7 (Octal) respectively.
	--

	-- Hex
	procedure HREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR);
	procedure HREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR; GOOD: out BOOLEAN);
	procedure HWRITE(L:inout LINE; VALUE:in STD_ULOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);
	procedure HREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR);
	procedure HREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN);
	procedure HWRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);

	-- Octal
	procedure OREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR);
	procedure OREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR; GOOD: out BOOLEAN);
	procedure OWRITE(L:inout LINE; VALUE:in STD_ULOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);
	procedure OREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR);
	procedure OREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN);
	procedure OWRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0);

	
--synopsys synthesis_on
end STD_LOGIC_TEXTIO;

package body STD_LOGIC_TEXTIO is
--synopsys synthesis_off

	-- Type and constant definitions used to map STD_ULOGIC values 
	-- into/from character values.

	type MVL9plus is ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-', ERROR);
	type char_indexed_by_MVL9 is array (STD_ULOGIC) of character;
	type MVL9_indexed_by_char is array (character) of STD_ULOGIC;
	type MVL9plus_indexed_by_char is array (character) of MVL9plus;

	constant MVL9_to_char: char_indexed_by_MVL9 := "UX01ZWLH-";
	constant char_to_MVL9: MVL9_indexed_by_char := 
		('U' => 'U', 'X' => 'X', '0' => '0', '1' => '1', 'Z' => 'Z',
		 'W' => 'W', 'L' => 'L', 'H' => 'H', '-' => '-', others => 'U');
	constant char_to_MVL9plus: MVL9plus_indexed_by_char := 
		('U' => 'U', 'X' => 'X', '0' => '0', '1' => '1', 'Z' => 'Z',
		 'W' => 'W', 'L' => 'L', 'H' => 'H', '-' => '-', others => ERROR);


	-- Overloaded procedures.

	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC; GOOD:out BOOLEAN) is
		variable c: character;
		variable readOk: BOOLEAN;
	begin
		loop					-- skip white space
			read(l,c,readOk);		-- but also exit on a bad read
			exit when ((readOk = FALSE) or ((c /= ' ') and (c /= CR) and (c /= HT)));
		end loop;

		if (readOk = FALSE) then
			good := FALSE;
		else
		    if (char_to_MVL9plus(c) = ERROR) then
			value := 'U';
			good := FALSE;
		     else
			value := char_to_MVL9(c);
			good := TRUE;
		    end if;
		end if;
	end READ;

	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR; GOOD:out BOOLEAN) is
		variable m:  STD_ULOGIC;
		variable c:  character;
		variable s:  string(1 to value'length-1);
		variable mv: STD_ULOGIC_VECTOR(0 to value'length-1);
		constant allU: STD_ULOGIC_VECTOR(0 to value'length-1)
		 				:= (others => 'U');
                variable readOk: BOOLEAN;

	begin
		loop					-- skip white space
			read(l,c,readOk);
			exit when ((readOk = FALSE) or ((c /= ' ') and (c /= CR) and (c /= HT)));
		end loop;
 
-- Bail out if there was a bad read
                if (readOk = FALSE) then
                        good := FALSE;
			return;
                end if;

		if (char_to_MVL9plus(c) = ERROR) then
			value := allU;
			good := FALSE;
			return;
		end if;

		read(l, s, readOk);
-- Bail out if there was a bad read
                if (readOk = FALSE) then
                        good := FALSE;
                        return;
                end if;

	    	for i in 1 to value'length-1 loop
			if (char_to_MVL9plus(s(i)) = ERROR) then
				value := allU;
				good := FALSE;
			    	return;
			end if;
		end loop;

		mv(0) := char_to_MVL9(c);
	    	for i in 1 to value'length-1 loop
			mv(i) := char_to_MVL9(s(i));
	    	end loop;
		value := mv;
		good := TRUE;
	end READ;
	
	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC) is
		variable c: character;
	begin
		loop					-- skip white space
			read(l,c);
			exit when ((c /= ' ') and (c /= CR) and (c /= HT));
		end loop;

		if (char_to_MVL9plus(c) = ERROR) then
			value := 'U';
			assert FALSE report "READ(STD_ULOGIC) Error: Character '" &
					c & "' read, expected STD_ULOGIC literal.";
		else
			value := char_to_MVL9(c);
		end if;
	end READ;

	procedure READ(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR) is
		variable m: STD_ULOGIC;
		variable c: character;
		variable s: string(1 to value'length-1);
		variable mv: STD_ULOGIC_VECTOR(0 to value'length-1);
		constant allU: STD_ULOGIC_VECTOR(0 to value'length-1)
				 := (others => 'U');
	begin
		loop					 -- skip white space
			read(l,c);
			exit when ((c /= ' ') and (c /= CR) and (c /= HT));
		end loop;

		if (char_to_MVL9plus(c) = ERROR) then
			value := allU;
			assert FALSE report
				"READ(STD_ULOGIC_VECTOR) Error: Character '" & 
					c & "' read, expected STD_ULOGIC literal.";
			return;
		end if;

		read(l, s);
	    	for i in 1 to value'length-1 loop
			if (char_to_MVL9plus(s(i)) = ERROR) then
			    value := allU;
			    assert FALSE report 
				"READ(STD_ULOGIC_VECTOR) Error: Character '" &
					s(i) & "' read, expected STD_ULOGIC literal.";
			    return;
			end if;
		end loop;

		mv(0) := char_to_MVL9(c);
	    	for i in 1 to value'length-1 loop
			mv(i) := char_to_MVL9(s(i));
	    	end loop;
		value := mv;
	end READ;

	procedure WRITE(L:inout LINE; VALUE:in STD_ULOGIC;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
	begin
		write(l, MVL9_to_char(value), justified, field);
	end WRITE;
	

	procedure WRITE(L:inout LINE; VALUE:in STD_ULOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
		variable s: string(1 to value'length);
		variable m: STD_ULOGIC_VECTOR(1 to value'length) := value;
	begin
	    	for i in 1 to value'length loop
			s(i) := MVL9_to_char(m(i));
		end loop;
		write(l, s, justified, field);
	end WRITE;

	-- Read and Write procedures for STD_LOGIC_VECTOR
	procedure READ(L:inout LINE; VALUE:out STD_LOGIC_VECTOR) is
		variable tmp: STD_ULOGIC_VECTOR(VALUE'length-1 downto 0);
	begin
		READ(L, tmp);
		VALUE := STD_LOGIC_VECTOR(tmp);
	end READ;

	procedure READ(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN) is
		variable tmp: STD_ULOGIC_VECTOR(VALUE'length-1 downto 0);
	begin
		READ(L, tmp, GOOD);
		VALUE := STD_LOGIC_VECTOR(tmp);
	end READ;

	procedure WRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
	begin
		WRITE(L, STD_ULOGIC_VECTOR(VALUE), JUSTIFIED, FIELD);
	end WRITE;


	--
	-- Hex Read and Write procedures.
	--

	--
	-- Hex, and Octal Read and Write procedures for BIT_VECTOR
	--  (these procedures are not exported, they are only used
	--   by the STD_ULOGIC hex/octal reads and writes below.
	--
	--

	procedure Char2QuadBits(C: Character; 
				RESULT: out std_ulogic_vector(3 downto 0);
				GOOD: out Boolean;
				ISSUE_ERROR: in Boolean) is
	begin
		case c is
			when '0' => result :=  x"0"; good := TRUE;
			when '1' => result :=  x"1"; good := TRUE;
			when '2' => result :=  x"2"; good := TRUE;
			when '3' => result :=  x"3"; good := TRUE;
			when '4' => result :=  x"4"; good := TRUE;
			when '5' => result :=  x"5"; good := TRUE;
			when '6' => result :=  x"6"; good := TRUE;
			when '7' => result :=  x"7"; good := TRUE;
			when '8' => result :=  x"8"; good := TRUE;
			when '9' => result :=  x"9"; good := TRUE;
			when 'A' => result :=  x"A"; good := TRUE;
			when 'B' => result :=  x"B"; good := TRUE;
			when 'C' => result :=  x"C"; good := TRUE;
			when 'D' => result :=  x"D"; good := TRUE;
			when 'E' => result :=  x"E"; good := TRUE;
			when 'F' => result :=  x"F"; good := TRUE;
			when 'Z' => result(0) :=  'Z'; result(1) :=  'Z'; result(2) :=  'Z'; result(3) :=  'Z'; good := TRUE;
			when 'X' => result(0) :=  'X'; result(1) :=  'X'; result(2) :=  'X'; result(3) :=  'X'; good := TRUE;
 
			when 'a' => result :=  x"A"; good := TRUE;
			when 'b' => result :=  x"B"; good := TRUE;
			when 'c' => result :=  x"C"; good := TRUE;
			when 'd' => result :=  x"D"; good := TRUE;
			when 'e' => result :=  x"E"; good := TRUE;
			when 'f' => result :=  x"F"; good := TRUE;
			when others =>
			   if ISSUE_ERROR then 
				   assert FALSE report
					"HREAD Error: Read a '" & c &
					   "', expected a Hex character (0-F).";
			   end if;
			   good := FALSE;
		end case;
	end;

	procedure HREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR)  is
		variable ok: boolean;
		variable c:  character;
		constant ne: integer := value'length/4;
		variable bv: std_ulogic_vector(0 to value'length-1);
		variable s:  string(1 to ne-1);
	begin
		if value'length mod 4 /= 0 then
			assert FALSE report 
				"HREAD Error: Trying to read vector " &
				   "with an odd (non multiple of 4) length";
			return;
		end if;

		loop					-- skip white space
			read(l,c);
			exit when ((c /= ' ') and (c /= CR) and (c /= HT));
		end loop;

		Char2QuadBits(c, bv(0 to 3), ok, TRUE);
		if not ok then 
			return;
		end if;

		read(L, s, ok);
		if not ok then
			assert FALSE 
				report "HREAD Error: Failed to read the STRING";
			return;
		end if;

		for i in 1 to ne-1 loop
			Char2QuadBits(s(i), bv(4*i to 4*i+3), ok, TRUE);
			if not ok then
				return;
			end if;
		end loop;
		value := bv;
	end HREAD; 

	procedure HREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR;GOOD: out BOOLEAN) is
		variable ok: boolean;
		variable c:  character;
		constant ne: integer := value'length/4;
		variable bv: std_ulogic_vector(0 to value'length-1);
		variable s:  string(1 to ne-1);
	begin
		if value'length mod 4 /= 0 then
			good := FALSE;
			return;
		end if;

		loop					-- skip white space
			read(l,c);
			exit when ((c /= ' ') and (c /= CR) and (c /= HT));
		end loop;

		Char2QuadBits(c, bv(0 to 3), ok, FALSE);
		if not ok then 
			good := FALSE;
			return;
		end if;

		read(L, s, ok);
		if not ok then
			good := FALSE;
			return;
		end if;

		for i in 1 to ne-1 loop
			Char2QuadBits(s(i), bv(4*i to 4*i+3), ok, FALSE);
			if not ok then
				good := FALSE;
				return;
			end if;
		end loop;
		good := TRUE;
		value := bv;
	end HREAD; 

	procedure HWRITE(L:inout LINE; VALUE:in std_ulogic_vector;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
		variable quad: std_ulogic_vector(0 to 3);
		constant ne:   integer := value'length/4;
		variable bv:   std_ulogic_vector(0 to value'length-1) := value;
		variable s:    string(1 to ne);
	begin
		if value'length mod 4 /= 0 then
			assert FALSE report 
				"HWRITE Error: Trying to read vector " &
				   "with an odd (non multiple of 4) length";
			return;
		end if;

		for i in 0 to ne-1 loop
			quad := To_X01Z(bv(4*i to 4*i+3));
			case quad is
				when x"0" => s(i+1) := '0';
				when x"1" => s(i+1) := '1';
				when x"2" => s(i+1) := '2';
				when x"3" => s(i+1) := '3';
				when x"4" => s(i+1) := '4';
				when x"5" => s(i+1) := '5';
				when x"6" => s(i+1) := '6';
				when x"7" => s(i+1) := '7';
				when x"8" => s(i+1) := '8';
				when x"9" => s(i+1) := '9';
				when x"A" => s(i+1) := 'A';
				when x"B" => s(i+1) := 'B';
				when x"C" => s(i+1) := 'C';
				when x"D" => s(i+1) := 'D';
				when x"E" => s(i+1) := 'E';
				when x"F" => s(i+1) := 'F';
				when others =>  
					if (quad = "ZZZZ") then
					   s(i+1) := 'Z';
					else
					   s(i+1) := 'X';
					end if;
			end case;
		end loop;
		write(L, s, JUSTIFIED, FIELD);
	end HWRITE; 

	procedure Char2TriBits(C: Character; 
				RESULT: out bit_vector(2 downto 0);
				GOOD: out Boolean;
				ISSUE_ERROR: in Boolean) is
	begin
		case c is
			when '0' => result :=  o"0"; good := TRUE;
			when '1' => result :=  o"1"; good := TRUE;
			when '2' => result :=  o"2"; good := TRUE;
			when '3' => result :=  o"3"; good := TRUE;
			when '4' => result :=  o"4"; good := TRUE;
			when '5' => result :=  o"5"; good := TRUE;
			when '6' => result :=  o"6"; good := TRUE;
			when '7' => result :=  o"7"; good := TRUE;
			when others =>
			   if ISSUE_ERROR then 
				   assert FALSE report
					"OREAD Error: Read a '" & c &
					"', expected an Octal character (0-7).";
			   end if;
			   good := FALSE;
		end case;
	end;

	procedure OREAD(L:inout LINE; VALUE:out BIT_VECTOR)  is
		variable c: character;
		variable ok: boolean;
		constant ne: integer := value'length/3;
		variable bv: bit_vector(0 to value'length-1);
		variable s: string(1 to ne-1);
	begin
		if value'length mod 3 /= 0 then
			assert FALSE report 
				"OREAD Error: Trying to read vector " &
				   "with an odd (non multiple of 3) length";
			return;
		end if;

		loop					-- skip white space
			read(l,c);
			exit when ((c /= ' ') and (c /= CR) and (c /= HT));
		end loop;

		Char2TriBits(c, bv(0 to 2), ok, TRUE);
		if not ok then 
			return;
		end if;

		read(L, s, ok);
		if not ok then
			assert FALSE 
				report "OREAD Error: Failed to read the STRING";
			return;
		end if;

		for i in 1 to ne-1 loop
			Char2TriBits(s(i), bv(3*i to 3*i+2), ok, TRUE);
			if not ok then
				return;
			end if;
		end loop;
		value := bv;
	end OREAD; 

	procedure OREAD(L:inout LINE; VALUE:out BIT_VECTOR;GOOD: out BOOLEAN) is
		variable ok: boolean;
		variable c:  character;
		constant ne: integer := value'length/3;
		variable bv: bit_vector(0 to value'length-1);
		variable s:  string(1 to ne-1);
	begin
		if value'length mod 3 /= 0 then
			good := FALSE;
			return;
		end if;

		loop					-- skip white space
			read(l,c);
			exit when ((c /= ' ') and (c /= CR) and (c /= HT));
		end loop;

		Char2TriBits(c, bv(0 to 2), ok, FALSE);
		if not ok then 
			good := FALSE;
			return;
		end if;

		read(L, s, ok);
		if not ok then
			good := FALSE;
			return;
		end if;

		for i in 1 to ne-1 loop
			Char2TriBits(s(i), bv(3*i to 3*i+2), ok, FALSE);
			if not ok then
				good := FALSE;
				return;
			end if;
		end loop;
		good := TRUE;
		value := bv;
	end OREAD; 


	procedure OWRITE(L:inout LINE; VALUE:in std_ulogic_vector;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
		variable tri: std_ulogic_vector(0 to 2);
		constant ne:  integer := value'length/3;
		variable bv:  std_ulogic_vector(0 to value'length-1) := value;
		variable s:   string(1 to ne);
	begin
		if value'length mod 3 /= 0 then
			assert FALSE report 
				"OWRITE Error: Trying to read vector " &
				   "with an odd (non multiple of 3) length";
			return;
		end if;

		for i in 0 to ne-1 loop
			tri := To_X01Z(bv(3*i to 3*i+2));
			case tri is
				when o"0" => s(i+1) := '0';
				when o"1" => s(i+1) := '1';
				when o"2" => s(i+1) := '2';
				when o"3" => s(i+1) := '3';
				when o"4" => s(i+1) := '4';
				when o"5" => s(i+1) := '5';
				when o"6" => s(i+1) := '6';
				when o"7" => s(i+1) := '7';
				when others =>  
					if (tri = "ZZZ") then
					   s(i+1) := 'Z';
					else
					   s(i+1) := 'X';
					end if;
			end case;
		end loop;
		write(L, s, JUSTIFIED, FIELD);
	end OWRITE; 

	-- Hex Read and Write procedures for STD_LOGIC_VECTOR
	procedure HREAD(L:inout LINE; VALUE:out BIT_VECTOR;GOOD:out BOOLEAN) is
		variable tmp: std_ulogic_vector(VALUE'length-1 downto 0);
	begin
		HREAD(L, tmp, GOOD);
		VALUE := To_BitVector(tmp);
	end HREAD;
	
	procedure HREAD(L:inout LINE; VALUE:out BIT_VECTOR) is
		variable tmp: std_ulogic_vector(VALUE'length-1 downto 0);
	begin
		HREAD(L, tmp);
		VALUE := To_BitVector(tmp);
	end HREAD;

	-- Hex Read and Write procedures for STD_LOGIC_VECTOR

	procedure HREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR) is
		variable tmp: STD_ULOGIC_VECTOR(VALUE'length-1 downto 0);
	begin
		HREAD(L, tmp);
		VALUE := STD_LOGIC_VECTOR(tmp);
	end HREAD;

	procedure HREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN) is
		variable tmp: STD_ULOGIC_VECTOR(VALUE'length-1 downto 0);
	begin
		HREAD(L, tmp, GOOD);
		VALUE := STD_LOGIC_VECTOR(tmp);
	end HREAD;

	procedure HWRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
	begin
		HWRITE(L, STD_ULOGIC_VECTOR(VALUE), JUSTIFIED, FIELD);
	end HWRITE;


	-- Octal Read and Write procedures for STD_ULOGIC_VECTOR
	procedure OREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR;GOOD:out BOOLEAN) is
		variable tmp: bit_vector(VALUE'length-1 downto 0);
	begin
		OREAD(L, tmp, GOOD);
		VALUE := To_X01(tmp);
	end OREAD;
	
	procedure OREAD(L:inout LINE; VALUE:out STD_ULOGIC_VECTOR) is
		variable tmp: bit_vector(VALUE'length-1 downto 0);
	begin
		OREAD(L, tmp);
		VALUE := To_X01(tmp);
	end OREAD;

	-- Octal Read and Write procedures for STD_LOGIC_VECTOR

	procedure OREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR) is
		variable tmp: STD_ULOGIC_VECTOR(VALUE'length-1 downto 0);
	begin
		OREAD(L, tmp);
		VALUE := STD_LOGIC_VECTOR(tmp);
	end OREAD;

	procedure OREAD(L:inout LINE; VALUE:out STD_LOGIC_VECTOR; GOOD: out BOOLEAN) is
		variable tmp: STD_ULOGIC_VECTOR(VALUE'length-1 downto 0);
	begin
		OREAD(L, tmp, GOOD);
		VALUE := STD_LOGIC_VECTOR(tmp);
	end OREAD;

	procedure OWRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
			JUSTIFIED:in SIDE := RIGHT; FIELD:in WIDTH := 0) is
	begin
		OWRITE(L, STD_ULOGIC_VECTOR(VALUE), JUSTIFIED, FIELD);
	end OWRITE;


--synopsys synthesis_on
end STD_LOGIC_TEXTIO;

Sie muss mit vcim 2002 kompiliert werden.

Simulationsprogramm starten (altera)

Vorbereitungen
– testbench ist in vhdl geschrieben
– compile_<projektname>.do ist in shell  und wird von der Konsole (des Simulationsprogrammes) her aufgerufen

Starten der Testbench
1. Neues Projekt anlegen
.  File/New/Project:
.  Ablegen der Simulation
2. Neue Bibliothek ein
File/New/Bibliothek.
Der Ordner wird Work genannt
3. Starten des Kompilierens und des Projekts per Konsole
4. Signale auswählen
.    Als erstes erscheinen nur die Fenster default und object (das sind die Signale).
.   Im Fenster objects auf ein Signal klicken,
.   CTRL A, alle Siganle auswählen
.   Rechte Maus: „Add waves
Ab jetzt sieht man (nach neuem Kompilieren)
.   die Signale im Wave-Fenster.
5. Speichern der Waves:
.    File/Save Format:   speichern in wave_<projektname>.do

Tipp:
In einem Projekt können mehrere testbenches bestehen. Jede erhält einen Namen: tb_midi_interface, tb_top_level, tb_uart.
Zu jeder Testbench gehört ihre eigene Kompilierungsdatei: compile_midi_interface.do, compile_top_level.do, compile_uart.do
und zu jeder Testbench gehören ihre eigene signale: wave_midi_interface.do, wave_top_level.do, wave_uart.do.

..

Unit Testing with Catch

Es gibt CATCH, das Testing für C++ ermöglicht. Es gibt einen Blogeintrag, der den Einsatz von CATCH auf hohem Niveau beschreibt. Zum Starten ist das Tutorial sehr gut.

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"

unsigned int Factorial( unsigned int number ) {
    return number <= 1 ? number : Factorial(number-1)*number;
}

TEST_CASE( "Factorials are computed", "[factorial]" ) {
    REQUIRE( Factorial(1) == 1 );
    REQUIRE( Factorial(2) == 2 );
    REQUIRE( Factorial(3) == 6 );
    REQUIRE( Factorial(10) == 3628800 );
}

Catch innerhalb eines Files einbauen und ausführen

1. Catch als Header in File einbinden

#define CATCH_CONFIG_RUNNER  
#include "catch.hpp"

2. Funktion, die getetst werden will

double calculate(string calculation) {
    double result = 0.0;
    ts.set(calculation);
    result = expression();
    return result;
}

3. Testfälle aufstellen

TEST_CASE( "r", "[digit]" ) {
    REQUIRE(calculate("4") == 4);
    REQUIRE(calculate("6") == 6);
    REQUIRE(calculate("4+2") == 6);
    REQUIRE(calculate("4+2+2") == 8);
    REQUIRE(calculate("4+2+2+4+2+2+1") == 17);
    REQUIRE(calculate("4-2") == 2);
    REQUIRE(calculate("8-2") == 6);
    REQUIRE(calculate("8-2-2") == 4);
    REQUIRE(calculate("3*2") == 6);
    REQUIRE(calculate("3*3") == 9);
    REQUIRE(calculate("3*2*3") == 18);
    REQUIRE(calculate("3+2*4") == 11);
    REQUIRE(calculate("3*2+4") == 10);
    REQUIRE(calculate("4 + 2") == 6);
    REQUIRE(calculate("10 + 2") == 12);
    REQUIRE(calculate("10 + 123") == 133);
    REQUIRE(calculate("-4 + 5") == 1);
    REQUIRE(calculate("-100 + 5") == -95);
    REQUIRE(calculate("(4 + 5)*2") == 18);
    REQUIRE(calculate("3+(-4 * 5)") == -17);
    REQUIRE(calculate("1.5 * 2") == 3);
}

Am Ende des Files folgend die Testfälle.

4. Datei kompilieren
5. Ausführen über Konsole und Argumente mitgeben
Gibt man keine Argumente mit, werden alle Tests durchgelauften.

Struktur der Testfälle

REQUIRE(calculate("6") == 6);

Nach REQUIRE schreibt man die Funktion, die aufgerufen werden soll und gibt ihr die notwendigen Werte mit.
Nach dem == folgt das zu erwartende Ergebnis.