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).