Skip to content

Verilog · Chapter 9.5 · Design & Testbench Creation

Testbench Creation Techniques in Verilog — Clock, Stimulus, Checking, Termination

You have learned to build a clean design module, the device under test. This lesson teaches you to prove it works by building a testbench, the simulation-only harness that wraps that design, brings it to life, and judges its behaviour. A testbench has a fixed set of jobs. It generates a clock, applies reset, drives stimulus into the inputs, observes the outputs, checks them against what was expected, and then terminates cleanly. You will learn the technique for each job, plus the one rule beginners get wrong on the harness side, which signals should be a reg and which should be a wire. This is where earlier lessons converge, from modules and instances to port mapping and the system tasks that control a run, into the harness that turns I wrote it into I proved it.

Foundation22 min readVerilogTestbenchVerificationDUTSelf-CheckingSimulation

Chapter 9 · Section 9.5 · Design & Testbench Creation

1. The Engineering Problem

You followed 9.4 and built a clean alu design module — synthesizable, single-purpose, well-interfaced. Is it correct? You have no idea. A design module sitting in a file does nothing: it has no clock ticking, no values on its inputs, no one watching its outputs. It is a chip with no power, no signal generator, and no oscilloscope attached.

A junior's instinct is to "look at the waveform and see if it seems right." That does not scale and it does not hold: eyeballing a few cycles proves nothing about the hundreds of cases that matter, and "seems right" has shipped countless bugs to silicon. The real question is:

How do you bring a design module to life, exercise it across the cases that matter, and have the design itself report whether it passed or failed — automatically?

The answer is a testbench: a simulation-only module that surrounds the DUT, generates its clock, drives its inputs, watches its outputs, and checks them against expected values, ending with a clear PASS or FAIL. It is the test rig that turns a dormant design into a running, judged experiment. This page builds that rig piece by piece — clock, reset, stimulus, observation, checking, termination — using the toolkit Chapter 8 and 9.1–9.4 already gave you.

2. Mental Model — The Testbench Is a Virtual Test Bench

Visual A — the six jobs of a testbench

What every testbench does

data flow
What every testbench doesClockgenerate the heartbeatResetknown starting stateStimulusdrive DUT inputsObserve$display / $monitorCheckactual vs expected → errorsTerminate$finish + watchdog
The six jobs map onto the simulation lifecycle from 8.5: clock + reset are Initialize, stimulus is the driving phase, observe + check are verification, and terminate is the deterministic end. Every testbench is an arrangement of these six.

3. The Anatomy of a Testbench

Here is the standard testbench skeleton wrapped around a DUT, with each region labelled. (The alu is the DUT from 9.4; its internals are not shown — the testbench only touches its interface.)

alu_tb.v — testbench skeleton
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps
 
module alu_tb;                       // 1. module with NO ports
 
    // 2. driver signals: reg for DUT inputs, wire for DUT outputs
    reg  [15:0] a, b;
    reg  [2:0]  op;
    wire [15:0] y;
    wire        zero;
 
    // 3. instantiate the DUT (named port mapping — 9.3)
    alu u_dut (
        .a(a), .b(b), .op(op), .y(y), .zero(zero)
    );
 
    // 4. clock generation (combinational DUTs may omit this)
    //    ... see §4
 
    // 5. stimulus + reset + checking, in an initial block
    initial begin
        // apply reset, drive inputs, check outputs, finish
        // ... see §5–§8
    end
 
    // 6. watchdog timeout — bound the run (§8, Chapter 8.5)
 
endmodule

Six regions, every testbench: a port-less module, driver signals (the reg/wire split of §9), the DUT instance, clock generation, the stimulus-and-check body, and a watchdog. The shape is as invariant as the design-module skeleton in 9.4 — learn it once and every testbench is a variation on it.

4. Clock Generation

A synchronous DUT does nothing without a clock. The testbench generates one — a free-running square wave — with a single line:

clock-gen.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reg clk = 0;
always #5 clk = ~clk;        // toggle every 5 time units → 10-unit period → 100 MHz at 1ns

The always #5 clk = ~clk flips the clock every 5 time units, producing a 10-unit period. This is sim-only (the #5 delay has no hardware meaning) and lives only in the testbench. Two cautions:

  • It runs forever. A free-running clock keeps the simulation's event queue alive indefinitely — which is exactly why a testbench must call $finish and carry a watchdog (Chapter 8.5). The clock will not stop on its own.
  • Combinational DUTs need no clock. A purely combinational block (an adder, a mux) has no clock port; its testbench drives inputs and waits for the outputs to settle, no clock generator required.

5. Reset Application

A sequential DUT starts in an unknown state until reset puts it into a known one. The testbench asserts reset at the very start, holds it briefly, then releases it before stimulus begins:

reset-apply.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
reg rst_n;
initial begin
    rst_n = 0;          // assert reset (active-low) at t=0
    #20;                // hold across a couple of clock edges
    rst_n = 1;          // release — DUT is now in a known state
    // ... stimulus follows ...
end

Reset is applied first, before any meaningful stimulus, so the DUT begins every test from a defined state. Forgetting it is a classic bug: stimulus applied to an unreset sequential DUT operates on x state and produces meaningless results (§11). The DUT's reset behaviour is its own design (Chapter 14); the testbench's job is simply to drive the reset signal at the right time.

6. Stimulus Application

Stimulus is the sequence of input values the testbench drives into the DUT to exercise it. In a directed testbench, the engineer writes out specific cases by hand:

directed-stimulus.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    rst_n = 0; #20; rst_n = 1;     // reset first
 
    // directed cases — drive inputs, wait, let outputs settle
    a = 16'd10; b = 16'd5; op = 3'b000; #10;   // add
    a = 16'd10; b = 16'd5; op = 3'b001; #10;   // subtract
    a = 16'd12; b = 16'd4; op = 3'b010; #10;   // and
    // ... more cases ...
 
    $finish;
end

Each case sets the inputs, waits (#10) for the DUT to respond, and moves on. Directed stimulus targets specific, known-interesting cases — boundary values, each operation, corner cases. (When you need thousands of varied cases, constrained-random stimulus uses $random/$urandom from Chapter 8.2 to generate them — that is a verification technique previewed here and drilled later; directed testing is where you start.)

7. Observation and Self-Checking

Driving stimulus is half the job; the testbench must also judge the result. There are two levels, and only one is acceptable for real work.

Observation prints what happened — useful, but it makes a human the checker:

observation-only.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("[t=%0t] a=%0d b=%0d op=%b -> y=%0d", $time, a, b, op, y);
// A person must read the log and decide if y is right. Doesn't scale.

Self-checking makes the testbench the checker — it computes the expected value and compares automatically, counting failures:

self-checking.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer errors = 0;
 
task check(input [15:0] exp);
    if (y !== exp) begin
        $display("[t=%0t] FAIL: a=%0d b=%0d op=%b -> y=%0d expected=%0d",
                 $time, a, b, op, y, exp);
        errors = errors + 1;
    end
endtask
 
// usage in stimulus:
a = 16'd10; b = 16'd5; op = 3'b000; #10; check(16'd15);   // add → expect 15

A self-checking testbench encodes the specification as executable comparisons — it fails loudly and automatically on any mismatch and needs no human to read the log. This is the standard for all real verification: a testbench that only prints (and relies on someone noticing) will pass a broken DUT the day no one is watching (§11, DebugLab 3). (The $display mechanics and the ===/!== X-aware comparison are Chapter 8.1 / Chapter 5; here the point is the testbench checks itself.)

Visual B — observation-only vs self-checking

Observation-only versus self-checking testbenchObservation-only$display → human reads logpasses bugs silentlyno one is watchingSelf-checkingcompute expected → comparedeterministicPASS/FAILfails loudly, automatically12
Two levels of testbench. Observation-only prints the DUT's outputs and relies on a human to spot wrong values — it does not scale and silently passes bugs no one happens to read. Self-checking computes the expected value, compares automatically, counts errors, and reports a deterministic PASS/FAIL. Real verification is always self-checking.

8. Termination and the Watchdog

A testbench must end deterministically with a verdict — and protect itself against a hung DUT. Both come straight from Chapter 8.5.

termination.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// at the end of the stimulus sequence:
initial begin
    // ... reset, stimulus, checks ...
    if (errors == 0) $display("TEST PASSED");
    else             $display("TEST FAILED (%0d errors)", errors);
    $finish;                          // deterministic end with a verdict
end
 
// independent watchdog — bound the run so a hung DUT can't run forever
initial begin
    #100000;
    $display("TEST FAILED — TIMEOUT");
    $finish;
end

The stimulus block prints a parseable verdict and $finishes; an independent watchdog block bounds the worst case so a deadlocked DUT fails loudly instead of running to the farm's wall-clock limit. Every testbench carries both. (The full discipline — $finish vs $stop, the watchdog race, exit semantics — is Chapter 8.5; here it is the testbench's closing region.)

9. The reg/wire Rule — Which Side Drives

The one rule beginners get wrong on the testbench side, and the complement to 9.3's port-direction rule:

  • Signals connected to DUT inputs must be reg. The testbench drives them from procedural (initial/always) blocks, and only a reg (a procedural variable) can be assigned there.
  • Signals connected to DUT outputs must be wire. The DUT drives them through its instance, and an instance output must connect to a net (the 9.3 rule, seen from the testbench).
the reg/wire split in the testbench
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   testbench DRIVES → DUT input    ⇒  declare as  reg
   DUT DRIVES → testbench observes  ⇒  declare as  wire

The mnemonic: the testbench writes its regs and reads the DUT's wires. Get it backwards — an output declared reg, or an input declared wire — and the tool rejects it (DebugLab 1). The logic is the same direction rule as 9.3, now applied from inside the harness: whoever drives a signal determines its type, and for DUT inputs that driver is the testbench.

Visual C — testbench drives reg inputs, reads wire outputs

Testbench reg inputs and wire outputsTestbenchdrives inputs, readsoutputsreg a, b, opTB drives → DUT inputsDUTcomputeswire y, zeroDUT drives → TB reads12
The signal types follow the drive direction. The testbench drives the DUT's inputs from procedural blocks, so those signals are reg. The DUT drives its outputs through the instance, so those signals are wire — the testbench only reads them. This is 9.3's port-direction rule seen from inside the harness.

10. Worked Examples

Three testbench fragments — clock, a reset-plus-stimulus sequence, and a self-checking skeleton.

10.1 Example 1 — clock generation

tb-clock.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module dut_tb;
    reg clk = 0;
    always #5 clk = ~clk;        // 10-unit period, free-running
    // ... DUT instance and stimulus use clk ...
endmodule

One line makes the heartbeat. Remember it runs forever — the testbench must $finish and carry a watchdog, or the simulation never ends.

10.2 Example 2 — reset then directed stimulus

tb-reset-stimulus.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    // 1. reset into a known state
    rst_n = 0;  en = 0;  d = 0;
    #20;  rst_n = 1;
 
    // 2. directed stimulus, synchronized to the clock
    @(posedge clk); en = 1; d = 8'hA5;
    @(posedge clk); d = 8'h3C;
    @(posedge clk); en = 0;
 
    // 3. end
    #20; $finish;
end

Reset first, then a short directed sequence driving the DUT's inputs across clock edges, then a clean $finish. This is the everyday shape of a directed stimulus block.

10.3 Example 3 — a self-checking skeleton

tb-self-checking.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer errors = 0;
 
task expect_eq(input [15:0] got, input [15:0] exp, input [127:0] name);
    if (got !== exp) begin
        $display("FAIL %0s: got=%0d expected=%0d at t=%0t", name, got, exp, $time);
        errors = errors + 1;
    end
endtask
 
initial begin
    rst_n = 0; #20; rst_n = 1;
 
    a = 16'd10; b = 16'd5; op = 3'b000; #10; expect_eq(y, 16'd15, "add");
    a = 16'd10; b = 16'd5; op = 3'b001; #10; expect_eq(y, 16'd5,  "sub");
 
    if (errors == 0) $display("TEST PASSED");
    else             $display("TEST FAILED (%0d errors)", errors);
    $finish;
end

The testbench encodes the spec as expect_eq checks, counts failures, and reports a deterministic verdict — no human in the loop. This is the standard structure all six worked together; 9.6 assembles a complete DUT-plus-testbench pair end to end.

11. Industry Perspective

  • Self-checking is mandatory. No serious team accepts an observation-only ("eyeball the waveform") testbench. A testbench that does not encode the expected result and fail automatically provides no regression protection — it passes a broken DUT the moment no one reads the log.
  • Testbenches run unattended, in bulk. Thousands of testbenches run nightly on compute farms with no human watching, which is exactly why deterministic $finish, watchdog timeouts, and a parseable PASS/FAIL verdict (Chapter 8.5) are non-negotiable — the harness must report its own result.
  • Directed gives way to constrained-random + coverage. Directed testbenches (this page) are the foundation; at scale, verification moves to constrained-random stimulus ($urandom) plus functional coverage to reach cases no human would enumerate. The directed skeleton here is the base those techniques build on.
  • The testbench is half the engineering. In modern projects, verification code often exceeds design code in volume and effort. The testbench is not an afterthought to the DUT — it is a co-equal deliverable, built in the same loop (the Chapter 9 overview's thesis).

12. Common Mistakes

  1. Wrong signal types — a DUT input declared wire (the testbench can't drive it procedurally) or a DUT output declared reg (an instance output must drive a net). The reg/wire rule of §9 (DebugLab 1).
  2. No clock — a synchronous DUT with no clock generator never advances; the simulation runs but nothing happens (DebugLab 2).
  3. No $finish — a free-running clock keeps the run alive forever (Chapter 8.5 / §8).
  4. No self-checking — only $display, relying on a human to notice wrong values; passes a broken DUT silently (DebugLab 3).
  5. Forgetting reset — driving stimulus into an unreset sequential DUT, which operates on x state and yields meaningless results (§5).

13. Debugging Lab

Three testbench debug post-mortems

14. Interview Q&A

15. Exercises

Exercise 1 — Classify the testbench signals

A DUT has ports input clk, input rst_n, input [7:0] d, input en, output [7:0] q, output valid. In the testbench, state which signals must be reg and which must be wire, and explain the rule in one sentence.

Exercise 2 — Write the missing pieces

Given a testbench skeleton with a DUT instance already in place, write: (a) a free-running clock generator with a 10-unit period, (b) a reset sequence that asserts active-low reset at t=0, holds it 20 units, and releases it, and (c) a $finish after the stimulus. Code only these three fragments.

Exercise 3 — Make it self-checking

This fragment is observation-only:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = 8'd7; b = 8'd9; #10;
$display("sum = %0d", sum);

Convert it to self-checking: compute the expected value, compare against sum, increment an errors counter on mismatch, and (separately) print a final PASS/FAIL. Describe what the final verdict block looks like.

Exercise 4 — Find the testbench bugs

List every testbench bug below (reference §4–§9 / §12) and give a one-line fix for each.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module dut_tb;
    reg        clk;
    reg  [7:0] d;
    reg  [7:0] q;          // q is a DUT output
    dut u_dut ( .clk(clk), .d(d), .q(q) );
    initial begin
        clk = 0;           // never toggled
        d = 8'h10; #10;
        $display("q=%0d", q);
        // no $finish
    end
endmodule

16. Summary

A testbench is the simulation-only harness that proves a design module (the DUT) works — it brings the dormant DUT to life, exercises it, and judges its behaviour automatically.

The core ideas:

  • Six jobs: generate the clock, apply reset, drive stimulus, observe outputs, check them against expected values, and terminate with a verdict (plus a watchdog). Every testbench arranges these six.
  • A testbench is a port-less, simulation-only module that reaches down into the DUT and freely uses initial, # delays, $display, $finish — everything a design module must not.
  • The reg/wire rule: DUT-input signals are reg (the testbench drives them); DUT-output signals are wire (the DUT drives them). The testbench writes its regs and reads the DUT's wires.
  • Self-checking, not observation-only: the testbench computes the expected result and compares automatically, failing loudly — printing alone passes broken DUTs silently.
  • Clock generation + $finish + watchdog (Chapter 8.5) make the run advance, end deterministically, and survive a hung DUT.

The discipline this page instils:

  • Always self-check — encode the spec as comparisons; a testbench that only prints is no testbench.
  • Reset first, clock always, finish always — known state, a heartbeat, and a deterministic end.
  • Type signals by who drives themreg for DUT inputs, wire for DUT outputs.

You can now build the harness that turns a design module into a verified one — the full convergence of Chapters 8 and 9.1–9.4. The final step is to see it all assembled: Chapter 9.6 Practical Examples walks a complete, working DUT-plus-testbench pair end to end — design module and self-checking testbench together — closing Chapter 9 and the foundation arc before the curriculum turns to the RTL core (Chapter 10 Operators onward).