Generated code usually parses and elaborates. The remaining defects are semantic: a wrong bound, a swapped condition, a dropped case.
Bug class | Why it compiles | Test that catches it |
|---|---|---|
Off-by-one bound | Both bounds are legal indices | A sweep of every address including the range ends. |
Swapped condition | Both branches type-check | A case per branch with expected outputs. |
Dropped valid flag | The index alone still compiles | An idle-input case with no request active. |
The listing below is an illustrative example written for this lesson, never real assistant output. Its classic slip is one character: addr < 8 instead of addr <= 8, which silently drops register 8.
The testbench sweeps all 16 addresses. Nine must select exactly one line and seven must select none, so the one-character slip cannot hide.
// Illustrative example written for this lesson, not real assistant output.
module reg_hit (
input logic [3:0] addr,
output logic [8:0] hit
);
always_comb begin
hit = 9'd0;
if (addr <= 4'd8) hit[addr] = 1'b1;
end
endmodule
module tb_reg_hit;
logic [3:0] addr;
logic [8:0] hit;
int bad = 0;
reg_hit dut (.*);
initial begin
for (int a = 0; a < 16; a++) begin
addr = a[3:0]; #1;
if (a <= 8) begin
if (hit !== (9'd1 << a)) bad++;
end else begin
if (hit !== 9'd0) bad++;
end
end
if (bad == 0) $display("PASS: 9 selects hit one line and 7 out-of-range hit none");
else $display("FAIL: %0d address checks off", bad);
$finish;
end
endmoduleTest the boundaries first, since defects cluster at range ends. A full sweep beats spot checks: addresses 8 and 9 decide this module, not addresses 0 to 2.
Sweep the full input range, valid and out-of-range alike.
Check the idle and empty cases alongside the busy ones.
Lint for width and index warnings before reading the logic.
An off-by-one bound such as addr < 8 instead of addr <= 8. The boundary sweep in the testbench locates it.
The defect hides exactly at the boundary. Only a full sweep covers addresses 8 and 9 where the range ends.