How to Write a Self Checking Testbench in Verilog
A self checking testbench in Verilog uses
initial blocks to apply inputs and always blocks or if statements to compare outputs against expected values. It automatically reports mismatches using $display or $error, so you don't need to manually check waveforms.Syntax
A self checking testbench typically includes these parts:
regandwiredeclarations for inputs and outputs.initialblock to apply stimulus (test inputs).alwaysblock orinitialblock withifstatements to check output correctness.- Use of
$displayor$errorto report mismatches. $finishto end simulation after tests.
verilog
module testbench(); reg clk, reset; reg [3:0] in; wire [3:0] out; // Instantiate the design under test (DUT) my_module dut(.clk(clk), .reset(reset), .in(in), .out(out)); initial begin // Apply inputs in = 4'b0000; reset = 1; #10 reset = 0; #10 in = 4'b0011; // ... more stimulus end always @(posedge clk) begin // Check outputs if (out !== expected_value) begin $display("Error: output mismatch at time %0t", $time); end end initial begin // Clock generation clk = 0; forever #5 clk = ~clk; end initial begin #100 $finish; end endmodule
Example
This example shows a self checking testbench for a simple 2-input AND gate module. It applies inputs, checks outputs, and reports errors automatically.
verilog
module and_gate(input a, input b, output y); assign y = a & b; endmodule module testbench(); reg a, b; wire y; and_gate dut(.a(a), .b(b), .y(y)); integer errors = 0; initial begin // Test all input combinations a = 0; b = 0; #10 check_output(0); a = 0; b = 1; #10 check_output(0); a = 1; b = 0; #10 check_output(0); a = 1; b = 1; #10 check_output(1); if (errors == 0) $display("All tests passed."); else $display("%0d errors found.", errors); $finish; end task check_output(input expected); if (y !== expected) begin $display("Error at time %0t: a=%b b=%b y=%b expected=%b", $time, a, b, y, expected); errors = errors + 1; end endtask endmodule
Output
All tests passed.
Common Pitfalls
- Not using non-blocking assignments (
<=) in sequential logic can cause timing issues. - Forgetting to check all input combinations leads to incomplete verification.
- Using
==instead of===can miss unknown (X) or high-impedance (Z) states. - Not ending simulation with
$finishcauses infinite runs. - Not reporting errors clearly makes debugging harder.
verilog
/* Wrong: Using == instead of === */ if (out == expected) begin // This may miss X or Z states end /* Right: Use === for exact comparison */ if (out === expected) begin // Correctly detects unknowns end
Quick Reference
Tips for writing self checking testbenches:
- Use
initialblocks to apply test inputs. - Use
alwaysorinitialblocks withifstatements to verify outputs. - Use
$displayor$errorto report mismatches. - Use
===for comparisons to catch unknowns. - End simulation with
$finish.
Key Takeaways
Use automated checks with if statements and $display to catch errors without manual waveform inspection.
Compare outputs with === to detect unknown or invalid states.
Apply all relevant input combinations to fully verify the design.
End simulation cleanly with $finish to avoid infinite runs.
Report errors clearly to simplify debugging.