Altifigence Academy

24 / 37 · Lab

Lab: a testbench that reports its own failures

Separate DUT, stimulus, reference model and checks, then test reset, enable and wraparound at race-free observation times.

Separate implementation from the checker

A testbench supplies stimulus, calculates expected behavior and reports mismatches. It is simulation code, not the circuit to synthesize. This lab uses the modulo_tick module from the parameter lesson. Save that module as modulo_tick.sv and the following complete testbench as tb.sv.

The specification is: synchronous reset wins; disabled edges hold count and clear pulse; enabled edges increment modulo N, with pulse=1 only on wrap. Use an integer reference count rather than reading the DUT’s internal state.

SystemVerilog
`timescale 1ns/1ps
module tb #(parameter integer N = 5);
  localparam integer CW = (N <= 1) ? 1 : $clog2(N);
  logic clk = 1'b0;
  logic rst = 1'b0;
  logic enable = 1'b0;
  wire [CW-1:0] count;
  wire pulse;
  integer expected_count = 0;
  logic expected_pulse = 1'b0;
  integer checks = 0;
  modulo_tick #(.N(N)) dut (
    .clk(clk), .rst(rst), .enable(enable),
    .count(count), .pulse(pulse)
  );
  always #5 clk = ~clk;

  task automatic step(input logic reset_value, enable_value);
    begin
      @(negedge clk);
      rst = reset_value;
      enable = enable_value;
      if (reset_value) begin
        expected_count = 0;
        expected_pulse = 1'b0;
      end else begin
        expected_pulse = 1'b0;
        if (enable_value) begin
          expected_count = expected_count + 1;
          if (expected_count == N) begin
            expected_count = 0;
            expected_pulse = 1'b1;
          end
        end
      end
      @(posedge clk);
      #1;
      if (count !== CW'(expected_count) || pulse !== expected_pulse)
        $fatal(1, "Mismatch check=%0d count=%0d expected=%0d pulse=%b expected=%b",
          checks, count, expected_count, pulse, expected_pulse);
      checks = checks + 1;
    end
  endtask

  initial begin
    if (N < 1) $fatal(1, "N must be positive");
    step(1'b1, 1'b1);
    repeat (N-1) step(1'b0, 1'b1);
    step(1'b0, 1'b0);
    step(1'b0, 1'b1);
    step(1'b0, 1'b0);
    repeat (2*N+1) step(1'b0, 1'b1);
    step(1'b1, 1'b1);
    step(1'b0, 1'b0);
    $display("PASS N=%0d checks=%0d", N, checks);
    $finish;
  end
  initial begin
    #10000;
    $fatal(1, "Timeout");
  end
endmodule

Avoid a race at the active edge

The task drives inputs at a falling edge, half a clock before capture. It compares after the next rising edge’s nonblocking updates. Here #1 is an educational observation delay with a declared timescale and a ten-nanosecond clock. It is not a physical clock-to-Q, setup or hold analysis. A reusable production verification environment may use clocking blocks or a more formal event-region discipline.

At the first step, reset makes the DUT known. The checker’s initialized reference does not initialize the DUT. !== reliably treats an unexpected X/Z output as a mismatch against known expected values.

Compile and run an explicit top

In a shell with Icarus Verilog installed, run:

text
iverilog -g2012 -s tb -o sim.vvp modulo_tick.sv tb.sv
vvp sim.vvp

Use -P tb.N=1, -P tb.N=2, -P tb.N=5 and -P tb.N=8 in separate compile commands to cover the configuration matrix. Icarus’s official command-line documentation explains language-mode and top-selection flags. A simulator may support a different SystemVerilog subset, so keep its actual version in the run record.

Make the checker fail deliberately

Change the DUT’s terminal comparison so count wraps one edge early, or remove the default pulse clear. At least one scenario must terminate with a nonzero failure before the timeout. Restore the correct source afterward. A passing checker that cannot detect a relevant mutation offers weak evidence.

Record the source hash, N, exact commands, exit statuses, PASS line and mismatch from the deliberate failure. Do not label this local testbench as a DDS run. General SystemVerilog testbench delays and initial blocks are separate from the restricted single-clock product lab interface.

Your choice applies to this browser. Change it any time using the footer.