Verilog Testbench Writing – A step by step guide

Testbenches are modules written to test a design or a part of a design by giving it different inputs and observing the output using a simulator. The testbench intends to facilitate the simulation tools by giving the required input in required order and taking the output of the simulation and checking it with the expected output.

Testbenches save a lot of design time by allowing the designer to check whether each unit functions as expected without implementing them in actual hardware. If the testbench is sufficiently advanced, each and every signal in a design can be simulated and checked using the testbench. But the complexity of the testbench increases with the complexity of the method of input required by the unit – not with the complexity of the functionality of the design. A good testbench can cover a wide range of inputs reducing the chances of the design failing in the real implementation.

Different languages and techniques such as verilog, system verilog, direct programming interface-c (DPI_C) are used for writing testbenches. For simplicity, the step by step guide uses verilog for the testbench development.

Step by step guide.

The objective of a testbench is to give the input to the unit under test (UUT) as required, take its outputs and match them with the expected output. The testbench can be non-synthesizable and need not have any synergy with a hardware design.

  1. Create the testbench file as a non-synthesizable top module and instantiate the UUT in it.
  2. Declare the registers and wires required for feeding the UUT with inputs and taking its outputs.
  3. Write an ‘initial begin’ block to indicate what has to be done at the begining of the simulation. This block is sequentially executed and usually contains the reset and starting the clock ticking.
  4. Then use always blocks to indicate the activitites that should happen continuously such as ticking the clock, feeding input and checking the output, etc.
  5. Input feeding can be done in different ways. An often used method is to read the inputs from the file and feeding them to the uut. The expected outptut can also be stored in a file and read out as output comes from the uut.

eg: Assume a module, ‘comparer’ used to compare the 8-bit inputs and output the largest value. Please note that the test cases for which the unit is tested are not shown in the example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
module design1_tb()

reg clk;
reg reset;
reg [7:0] input1;
reg [7:0] input2;
reg out1;

comparer #(
    //Parameters to be passed (if there's any)
)
inst(
    .clk(clk),
    .reset(reset),
    .input1(input1),
    .input2(input2),
    .decision(out1)
);

initial begin
    clk = 0; //initial value
    reset = 1;
    #10 reset = 0; //release reset
    end

    always
        #5 clk != clk; //tick the clock every 5ns (clk period = 10ns)

    always@(posedge clk) begin
        //Arrangement for feeding the testcases go here...

        //output comparison
        if(input1 > input2) begin
            if(out1 == input1) begin
                $display("compare success");
            end
        else begin
            $display("comparison failed! Output : %x Expected %x", out1, input1);
            $stop
        end
    end
    else begin
        if(out1 == input2) begin
            $display("compare success");
        end
        else begin
            $display("comparison failed! Output : %x Expected %x", out1, input2);
            $stop
        end
    end
end

endmodule

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.