Video 2 of 4 · ~12 minutes
Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF
Regression suites at Intel/NVIDIA/Apple run thousands of self-checking testbenches overnight. CI/CD pipelines for RTL exist. A failing testbench emits a precise error log that developers can triage in minutes. Waveform inspection is reserved for root-causing; it is never how tests report status.
Every lab from here forward uses a self-checking testbench. Your make sim either prints “0 failed” or it doesn't. Labs are graded on the self-check output. Video 3 organizes this pattern with tasks; Video 4 scales it with external test vectors.
“I'll run the simulation, open GTKWave, check that the outputs look right. If I add more tests later I'll just re-check by eye.”
Manual inspection does not scale, does not regress, and does not communicate. A self-checking testbench codifies expected behavior, compares it against actual behavior, and emits a structured PASS/FAIL report. When a student hands you a repo and says “it works,” you run make sim and trust the exit code, not their screenshot.
=== N passed, 0 failed ===”, it isn't done. No exceptions.
integer tests_run = 0, tests_failed = 0;
task check_eq(input [31:0] actual, input [31:0] expected, input [256*8-1:0] label);
begin
tests_run = tests_run + 1;
if (actual !== expected) begin // use !== to handle X/Z correctly
tests_failed = tests_failed + 1;
$display("FAIL: %0s actual=%h expected=%h", label, actual, expected);
end else begin
$display("PASS: %0s = %h", label, actual);
end
end
endtask
initial begin
// ... drive stimulus ...
check_eq(sum, 5'd8, "5+3");
check_eq(sum, 5'd16, "7+9");
// ... more ...
$display("=== %0d passed, %0d failed ===", tests_run - tests_failed, tests_failed);
$finish;
end
!== (case-inequality) — not != — to correctly compare signals that may contain X or Z. (2) Track tests_run and tests_failed as integer counters. (3) Always print a summary line at the end so CI/CD can grep for it.
== vs ===reg [3:0] signal = 4'b10x1;
if (signal == 4'b1011) $display("A fires?"); // result: X — if-condition is ambiguous
if (signal === 4'b1011) $display("B fires?"); // result: 0 — cleanly false, B does not fire
if (signal === 4'b10x1) $display("C fires?"); // result: 1 — matches including X, C fires
== returns X when either operand contains X. In an if, ambiguous results often get treated as false, but the behavior is fragile. === treats X as a literal value and always returns a clean 0 or 1. In testbenches, always use === and !==.
$ make sim
PASS: reset → count=0
PASS: 1 cycle → count=1
FAIL: 5 cycles → count=5 actual=xxxx expected=5
PASS: rollover → count=0
FAIL: enable test actual=0 expected=1
=== 3 passed, 2 failed ===
Two failures. What are the likely root causes?
~5 minutes
▸ COMMANDS
cd labs/week2_day06/ex2_self_check/
# before: prints values
# after: PASS/FAIL + summary
diff tb_adder_before.v tb_adder_after.v
make sim
▸ EXPECTED STDOUT
PASS: 5+3 = 8
PASS: 7+9 = 16
PASS: 15+1 = 16
PASS: 0+0 = 0
PASS: F+F = 1E
=== 5 passed, 0 failed ===
▸ KEY OBSERVATION
The Makefile's make sim can now grep for “0 failed” to determine pass/fail automatically — enabling scripting, CI, and honest grading.
Same answer as before — testbenches don't synthesize. But now the difference matters:
# Your DUT (adder.v) synthesizes:
$ yosys -p "synth_ice40 -top adder" ...
SB_LUT4: 4 SB_CARRY: 4
# Your testbench (tb_adder.v) does not:
$ yosys -p "synth_ice40 -top tb_adder" ...
ERROR: unsynthesizable constructs
# make stat runs ONLY on the DUT.
# make sim runs ONLY the testbench.
# They never mix.
make sim is where verification lives. make stat is where synthesis lives. Self-checking testbenches are the contract between them.
Ask AI: “Refactor this testbench to be self-checking with PASS/FAIL output and a summary line. Use !== for comparisons.”
TASK
Ask AI to self-check an existing testbench.
BEFORE
Predict: a check task, !== compare, counter-based summary.
AFTER
Strong AI uses !==. Weak AI uses != and handles X poorly.
TAKEAWAY
Test your AI by giving it code with an X and checking it handles the compare correctly.
① Manual waveform inspection is for debugging, not verification.
② Every testbench must print PASS/FAIL + a summary line.
③ Use !== (case-inequality), never !=, for output checks.
④ Grep "0 failed" from your Makefile for honest CI.
🔗 Transfer
Video 3 of 4 · ~8 minutes
▸ WHY THIS MATTERS NEXT
You already met task in this video. Video 3 goes further: using tasks to clean up stimulus-and-check patterns so that each test case becomes a single readable line. This is how professional verification engineers structure their testbenches.