Difficulty: Medium · Puzzle 3 of 6 · Topic: Verifying CDC & Reset Puzzles
The DUT wrongly raises valid while in reset. The checker only runs 'if (!rst_n)'. In run 1 the testbench never drives rst_n (it stays X); in run 2 it drives rst_n=0. What are checks and fails in each run (edges at 5, 15, 25, 35, 45), and how do you make the checker fail loudly in run 1?
`timescale 1ns/1ns
module tb;
parameter DRIVE = 0; // 0: the testbench never drives rst_n
reg clk = 0;
reg rst_n; // 4-state: X until somebody drives it
reg valid;
integer checks = 0, fails = 0;
always #5 clk = ~clk; // posedges at 5, 15, 25, 35, 45
// DUT with a reset bug: it raises valid while in reset
always @(posedge clk) if (!rst_n) valid <= 1'b1; else valid <= 1'b0;
// reset checker: valid must be 0 whenever reset is active
always @(posedge clk)
if (!rst_n) begin
checks = checks + 1;
if (valid !== 1'b0) fails = fails + 1;
end
initial begin
if (DRIVE) rst_n = 0;
#46 $display("DRIVE=%0d checks=%0d fails=%0d", DRIVE, checks, fails);
$finish;
end
endmoduleWhat does if (X) do in Verilog?
1) Run 1: rst_n stays X. The condition !rst_n is X, and an if on an X condition counts as false in Verilog/SV, so the checker body never executes: checks=0 and fails=0. The DUT uses the same test, so it takes the else branch and valid stays 0, hiding the bug as well.
2) Run 2: rst_n = 0 at every edge. The checker runs at all five edges (checks=5). valid is X at the first edge (not 0, so a fail) and 1 afterwards (fail): fails=5.
3) So the same checker passes vacuously when the stimulus is wrong and fails properly when the reset is driven.
4) Fix: assert that reset is a known value once the test has started (!$isunknown(rst_n)), test with 'if (rst_n !== 1'b1)' so that X cannot slip through, and count how often the check ran.Answer: Run 1: checks=0 fails=0 (the checker never ran: a vacuous pass). Run 2: checks=5 fails=5. Add a check that rst_n is never X after time 0 and write reset tests with === or !==, never with a bare if on a possibly-X signal.
Checked by running the code in Icarus Verilog 13; the printed output supports the answer above. Icarus, rst_n never driven (X): the checker body never runs. Icarus, rst_n driven to 0: the same checker runs at every edge and flags the bug.
A silent pass is not a pass. X on an if condition behaves like 0, so a checker, a DUT and a coverage condition may all skip their body together. Always count how many times a check actually ran (or use a cover) and check that the reset itself is known.
Would the same thing happen in a 2-state simulator? Model answer: no: there is no X, so rst_n would be 0 or 1 from the start; the vacuous pass is a 4-state effect, which is one reason to run at least some bench configurations in a 4-state tool.