A Simple Testbench
Drive inputs, check outputs, and make it self-checking
Before any fancy methodology, you must understand the plain idea: a testbench wraps your design, feeds it inputs, and checks the outputs. Everything else in this path is just a better-organized version of this.
The DUT we will test
Say the design is a simple adder: out = a + b. The spec is one line, but it is enough to show the whole pattern.
module adder (
input [7:0] a,
input [7:0] b,
output [8:0] out // 9 bits so the carry fits
);
assign out = a + b;
endmoduleA first testbench - just print
module adder_tb;
logic [7:0] a, b;
logic [8:0] out;
adder dut (.a(a), .b(b), .out(out));
initial begin
a = 8'd10; b = 8'd20; #1;
$display("10 + 20 = %0d", out);
a = 8'd200; b = 8'd100; #1;
$display("200 + 100 = %0d", out);
$finish;
end
endmoduleThis works, but it makes you read the numbers and judge them yourself. That does not scale, and tired eyes miss bugs.
A self-checking testbench - the real upgrade
Instead of printing, compute what the answer should be and compare. Let the computer judge, and shout only when something is wrong.
initial begin
for (int i = 0; i < 100; i++) begin
a = $random; // random value, truncated to 8 bits on assignment
b = $random;
#1; // let the result settle
if (out !== a + b) // the golden expected value
$error("FAIL: %0d + %0d gave %0d", a, b, out);
end
$display("All checks done");
$finish;
endThe line if (out !== a + b) $error(...) is the heart of all verification. You have a reference for the correct answer (here, a + b) and you compare automatically. Everything in UVM later is an elaborate, reusable way to do exactly this: generate stimulus, predict the answer, compare.
Use !== and === (the case-equality operators) when checking, not != and ==. The triple version also catches x and z values, so an uninitialized output cannot silently pass your check. This single habit catches a surprising number of real bugs.