Why Checking Has to Be Automatic
Sections 6.1 to 6.3 built the infrastructure. This section is about the checker, and it starts with why there has to be one. A 16-bit adder has four billion input pairs. At one second per waveform, full coverage takes 136 years. The numbers are not there to discourage; they are there to make one thing plain: for anything past a toy, checking by eye is not slow, it is impossible.
The input space
| Input width | Combinations | At ten a second, by hand |
|---|---|---|
| 8 bits | 256 | 26 seconds |
| 12 bits | 4096 | 7 minutes |
| 16 bits | 65 536 | under 2 hours |
| 24 bits | 16 million | 19 days |
| 32 bits | 4 billion | 13 years |
A simulator with a self-checking testbench does the 16-bit case in seconds and the 32-bit case in minutes. It is not cleverer than you; it does not tire and it does not skip. And where exhaustive testing is out of reach, the discipline still holds: thousands of well-chosen inputs checked by code beat a dozen looked at by eye.
Regression
You have a working ALU. You fix a bug in the shifter, re-run your tests, all pass. Two weeks later somebody finds the adder is broken, because the shifter fix shared a resource with the carry chain, and you did not re-test the adder. That is a regression: a change that fixes one thing and breaks another unnoticed. Self-checking testbenches make it practical to re-run everything on every change, in seconds, so that the break is flagged on the commit that caused it rather than a fortnight later.
What a bug costs
| Found in | Relative cost |
|---|---|
| simulation | 1 |
| after synthesis | 10 |
| on an FPGA prototype | 100 |
| in silicon | 1000 and up |
The figures are approximate and the order of magnitude is real. Thorough simulation is not perfectionism; it is finding defects at the moment they cost the least, when the fix is a text edit.
By eye and by code
Two testbenches for one 8-bit adder. The first prints:
`timescale 1ns / 1ps
module tb_adder_manual();
reg [7:0] a, b;
wire [8:0] sum;
adder_8bit dut (.a(a), .b(b), .sum(sum));
initial begin
a = 8'd10; b = 8'd20; #10;
$display("10 + 20 = %0d", sum);
a = 8'd200; b = 8'd100; #10;
$display("200 + 100 = %0d", sum);
a = 8'd255; b = 8'd1; #10;
$display("255 + 1 = %0d", sum);
$finish;
end
endmodule
You read three numbers and think about each. Maybe you misread one. Maybe you check two and move on.
The second checks:
`timescale 1ns / 1ps
module tb_adder_selfcheck();
reg [7:0] a, b;
wire [8:0] sum;
integer error_count, test_count;
integer i, j;
adder_8bit dut (.a(a), .b(b), .sum(sum));
initial begin
error_count = 0;
test_count = 0;
for (i = 0; i < 256; i = i + 1) begin
for (j = 0; j < 256; j = j + 1) begin
a = i[7:0];
b = j[7:0];
#10;
test_count = test_count + 1;
if (sum !== (i + j)) begin
$error("a=%0d b=%0d expected=%0d got=%0d", i, j, i + j, sum);
error_count = error_count + 1;
end
end
end
if (error_count == 0)
$display("PASSED: all %0d tests.", test_count);
else
$display("FAILED: %0d of %0d tests.", error_count, test_count);
$finish;
end
endmodule
All 65 536 pairs, in a fraction of a second, with the failing inputs named. One line to read.
The mistake to avoid at this point is writing a self-checking testbench and then only checking the same three or four inputs you would have looked at anyway. The automation only pays when you use it to check far more than you could by hand.
A printing testbench needs a person in the loop to give a verdict, and the person can be tired, or hurried, or wrong. A self-checking one puts the judgement in code, where it is repeatable and reviewable and can run at midnight. From here on every testbench in the course checks itself, and the next article is about the tools Verilog gives you to report what it finds.