Menu

Verilog Testbench Basics: How to Verify a Module

How to write a Verilog testbench - clock generation, reset sequence, stimulus, observation, and the standard skeleton that drives every simulation you'll run.

This page includes runnable editors - edit, run, and see output instantly.

What a Testbench Actually Is

A testbench is just a Verilog module. The difference from a synthesizable module is what's inside:

  • It has no ports - it's the top of the simulation hierarchy.
  • It instantiates the design under test.
  • It drives the DUT's inputs from initial and always blocks.
  • It observes the DUT's outputs with $display, $monitor, or by dumping a VCD.
  • It terminates the simulation with $finish.

That's the entire job. There's no special "testbench" keyword - the way you tell something's a testbench is by reading what it does.

The Standard Skeleton

That's the entire pattern. Five sections, easy to copy-paste, easy to adapt.

For combinational DUTs (like the adder), you don't need a clock. For sequential DUTs, you do - and that's the next layer up.

Clock Generation for Sequential DUTs

When the DUT has a clk input, the testbench has to produce one. The standard one-liner:

reg clk = 0;
always #5 clk = ~clk;

That toggles clk every 5 time units. Each toggle is half the clock period, so the full period is 10 units (one cycle = high for 5 + low for 5). If your timescale is 1ns / 1ps (default in most simulators), that's a 100 MHz clock.

Pick the half-period based on what you want to model. For a 10 MHz clock at the same timescale, use #50. For a 200 MHz clock, #2.5 (or change the timescale).

Reset Sequence

Synchronous designs need a reset that gets held high for a few cycles after time 0, then released. The conventional shape:

reg reset = 1;     // start asserted

initial begin
    // Hold reset for a few clocks.
    #20 reset = 0;
end

That keeps reset high until time 20, then drops it. By the time reset falls, the clock has done a couple of cycles, every flip-flop has captured the reset value, and the design is in a known state.

For active-low reset (reset_n instead of reset), invert the initial value:

reg reset_n = 0;   // active-low: 0 means asserted

initial begin
    #20 reset_n = 1;
end

A Sequential Testbench in Full

Combining clock, reset, and stimulus:

That's a complete testbench for a 4-bit counter. Press Run and you'll see the counter come out of reset, count up while enabled, pause when enable drops, resume when it rises, then stop.

Three things to notice about the structure:

  1. Three distinct loci of activity: the clock generator (one always), the stimulus (one initial), and the monitor (another always). Each has one job.
  2. The # delays in the stimulus block are measured in time units - not clock cycles. If your clock period is 10 units, then #10 is exactly one cycle. Some testbenches use @(posedge clk) instead, which advances one cycle regardless of period.
  3. The monitor uses $display from an always @(posedge clk) block. That prints every cycle. For more sophisticated output, switch to $monitor (covered next).

Cycle-Based Stimulus

Sometimes "wait N cycles" reads better than "wait N time units":

initial begin
    @(posedge clk);     // wait for next clock edge
    reset = 0;

    repeat (5) @(posedge clk);   // wait 5 more cycles
    enable = 1;

    repeat (8) @(posedge clk);
    enable = 0;

    repeat (10) @(posedge clk);
    $finish;
end

@(posedge clk) blocks until the next rising clock edge. repeat (N) @(posedge clk); waits N cycles. This style is independent of the clock period - if you change the clock frequency, the stimulus still does the same thing in terms of cycles.

For early testbenches # delays are simpler. For production testbenches that may run at multiple clock speeds, cycle-based is the safer style.

Self-Checking Tests

The testbench so far prints what happened, leaving you to read the output. A self-checking testbench instead checks the output and reports pass/fail:

initial begin
    #1;
    a = 10; b = 20;
    #1;
    if (sum !== 30) begin
        $display("FAIL: 10 + 20 = %0d (expected 30)", sum);
        $finish;
    end
    a = 250; b = 5;
    #1;
    if (sum !== 255) begin
        $display("FAIL: 250 + 5 = %0d (expected 255)", sum);
        $finish;
    end

    $display("PASS");
    $finish;
end

Use !== (the case-inequality operator) for safety - it doesn't return x when one operand has unknown bits. The pattern: drive inputs, wait for things to settle, compare against expected, report pass or fail.

Self-checking is invaluable in regression suites: thousands of tests can run unattended, and only failures need attention.

What Comes Next

You now have the testbench shape that's enough for any module. The next docs cover the tools that go inside the testbench: Display and Monitor for richer text output, Dumpfile and VCD for graphical waveform debugging, and Timescale and Delays for controlling exactly how simulation time relates to wall-clock time.

Frequently Asked Questions

What is a testbench in Verilog?

A testbench is a Verilog module - typically with no ports - whose only purpose is to exercise a design under test (DUT). It instantiates the DUT, generates a clock, drives stimulus through initial and always blocks, observes outputs with $display/$monitor, optionally dumps a VCD waveform, and ends the simulation with $finish.

How do I generate a clock in a Verilog testbench?

The standard pattern is a single line: always #5 clk = ~clk; (with reg clk = 0; declared earlier). That toggles clk every 5 simulation time units, giving a 10-unit period (a 100 MHz clock if your timescale is in nanoseconds). The #5 is the half-period - half the time clk is high, half it's low.

What is a DUT in Verilog?

DUT stands for Design Under Test - the module the testbench is exercising. Convention is to name the DUT instance dut or u_dut in the testbench: my_module dut(.clk(clk), .reset(reset), .in(in), .out(out));. The name is just a label; what matters is that it's instantiated, its ports are connected to testbench signals, and the testbench drives those signals.

How long should a Verilog simulation run?

Long enough to exercise everything you want to verify, then $finish. Most testbenches set an explicit time limit (#1000 $finish) so the simulation can't hang waiting for an event that never comes. Inside that window, drive your stimulus, let the DUT settle, and ideally include some self-checking if statements that print FAIL if the output doesn't match expectations.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED