Vu que je m’attache de plus en plus à la validation (et donc aux tests unitaires), j’ai tenté de l’appliquer au Verilog. Il s’agit juste d’une modification simple du banc de tests. Revenons donc à un simple compteur.

module counter(out, clk, reset);

  parameter WIDTH = 8;
  output [WIDTH-1 : 0] out;

  input 	       clk, reset;

  reg [WIDTH-1 : 0]   out;
  wire 	       clk, reset;

  always @(posedge clk)
    out <= out + 1;

  always @reset
    if (reset)
	    assign out = 0;
    else
	    deassign out;

endmodule

Maintenant les tests,

module test;

  // Definition des modules et des fils
  reg reset = 0;

  	reg clk = 0;
  	always #1 clk = !clk;

  	wire [7:0] value;
  	counter c1 (value, clk, reset);

  	// Premier test
  	task testReset;
  		begin
  			#5;
  			reset <= 1;
  			#1;
  			assert_equal(0, value);
  			reset <= 0;
  		end
  	endtask

  	// Second test
  	task testResetProlonge;
  		begin
  			reset <= 1;
  			#10;
    		assert_equal(0, value);
  		end
  	endtask

  	// Troisieme test
  	task testCompteur;
  		begin
  			reset <= 1;
  			#1;
  			reset <= 0;
  			#5; // 1 front montant desactive le reset, 2 autres incrémentent
    		assert_equal(2, value);
  		end
  	endtask

  	// Sequence de tests
  initial begin
    	testReset;
    	testResetProlonge;
    	testCompteur;
    $finish;
  end

  // Longueur des vecteurs pour la comparaison
  parameter n = 32;

  // Comparaison
  integer i = 0;

  task assert_equal;
  		input [n - 1 : 0] needle, var;
  		begin
    		if (needle != var) begin
    			$display("#%0d : failure", i);
    			$display("	%0d != %0d (dec)", needle, var);
    			$display("	%0h != %0h (hex)", needle, var);
    			$finish;
    		end
    		else begin
    			$display("#%0d : pass", i);
    		end
    		i <= i + 1;
    	end
  	endtask

endmodule

Un peu d’explication : Il suffit déclarer et « brancher » tout d’abord les modules qui nous serviront dans les tests, puis d’écrire nos tests au fur et à mesure sous forme de « task ». Enfin, on rajoute le test dans la séquence dans le bloc « initial ».

A l’exécution, on obtient,

$ iverilog -o design counter_test.v counter.v && vvp design
#0 : pass
#1 : pass
#2 : failure
    2 != 1 (dec)
    2 != 1 (hex)
$ iverilog -o design counter_test.v counter.v && vvp design
#0 : pass
#1 : pass
#2 : pass

Deux problèmes surgissent alors :

  • Les tests ne sont pas indépendants. Il faut impérativement faire un reset du module entre chaque test.
  • La comparaison dans l’assertion : il faut donner à assert_equal le nombre de bits sur lequel il doit faire la comparaison.

Cette méthode pour intégrer les tests unitaires me semble la plus judicieuse et la plus simple. Les assertions existent déjà en Verilog mais elles sont d’un autre type, pour un autre usage (voir asic-world).