Writing Testbenches

Clock generation, reset, stimulus tasks, $monitor and the habits that keep testbenches free of races.

A testbench is Verilog that never becomes hardware. It can use everything the language offers — delays, loops over time, file I/O, printing — to drive the design under test (DUT) and observe it. Most verification time in industry is spent writing testbenches, not designs.

The parts of a testbench

always #5 clk = ~clk; generates a 100 MHz clock (period 10 ns).

The task add drives din and en at the falling edge, half a period away from the rising edge the design uses.

$monitor prints a line every time any of its arguments changes.

repeat (2) @(posedge clk); waits two clock edges.

Waveforms

@(posedge clk) din = 5; // same instant the DUT samples @(negedge clk) din = 5; // half a period before the next rising edge

Changing inputs exactly at the active edge creates a race between the testbench and the design. Drive them away from the edge.

initial begin...stimulus... end // no $finish, clock never stops initial begin...stimulus... $finish; end

With a free-running clock the simulation never ends by itself.

How do you generate a 50 MHz clock in a testbench with `timescale 1ns/1ps?

Period 20 ns, so toggle every 10 ns: initial clk = 0; always #10 clk = ~clk;

What is the difference between $display and $monitor?

$display prints once when executed. $monitor registers its arguments and prints a line every time any of them changes (only one $monitor is active at a time).