Verilog Testbench Tutorial: Write, Run and Self-Check a Testbench

A Verilog testbench is a piece of Verilog code that exercises a design, applies inputs to it and checks the outputs. It is never synthesized into hardware; it exists only in simulation. Every design, from a 2:1 multiplexer to a processor core, is verified with a testbench before it goes to synthesis.

What a testbench contains

  • A module with no ports. The testbench is the top of the simulation, so nothing connects to it from outside.
  • The DUT instance. The design under test is instantiated and wired to testbench signals: reg for inputs you drive, wire for outputs you read.
  • Stimulus. An initial block that sets inputs over time using delays (#10).
  • Clock and reset generation for sequential designs.
  • Checking. $display, $monitor or, better, comparisons against expected values.
  • Waveform dump and end of simulation ($dumpfile, $dumpvars, $finish).

Step 1: the design under test

A 4-bit adder with carry-out:

module adder4 (
  input  [3:0] a,
  input  [3:0] b,
  input        cin,
  output [3:0] sum,
  output       cout
);
  assign {cout, sum} = a + b + cin;
endmodule

Step 2: a basic testbench

`timescale 1ns/1ps
module tb_adder4;
  reg  [3:0] a, b;
  reg        cin;
  wire [3:0] sum;
  wire       cout;

  // instantiate the DUT with named port connections
  adder4 dut (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));

  initial begin
    $dumpfile("adder4.vcd");
    $dumpvars(0, tb_adder4);
    $monitor("t=%0t a=%d b=%d cin=%b -> sum=%d cout=%b",
             $time, a, b, cin, sum, cout);

    a = 0;  b = 0;  cin = 0; #10;
    a = 5;  b = 3;  cin = 0; #10;
    a = 15; b = 1;  cin = 0; #10;
    a = 7;  b = 8;  cin = 1; #10;
    $finish;
  end
endmodule

$monitor prints a line every time any of its arguments changes. Named port connections (.a(a)) are safer than positional ones because they do not break when the DUT’s port order changes.

Step 3: make it self-checking

Reading printed values by eye does not scale. A self-checking testbench computes the expected result and reports only mismatches:

  integer errors = 0;

  task check(input [3:0] ta, tb, input tc);
    reg [4:0] expected;
    begin
      a = ta; b = tb; cin = tc; #10;
      expected = ta + tb + tc;
      if ({cout, sum} !== expected) begin
        errors = errors + 1;
        $display("FAIL a=%d b=%d cin=%b got=%d exp=%d",
                 ta, tb, tc, {cout, sum}, expected);
      end
    end
  endtask

  initial begin
    check(0, 0, 0);
    check(5, 3, 0);
    check(15, 1, 0);
    check(7, 8, 1);
    if (errors == 0) $display("PASS: all vectors matched");
    else             $display("FAIL: %0d mismatches", errors);
    $finish;
  end

Use !== (case inequality) so that X or Z on an output is reported as a failure rather than silently matching.

Step 4: exhaustive and random stimulus

A 4-bit adder has only 512 input combinations, so loop through all of them:

  integer i;
  initial begin
    for (i = 0; i < 512; i = i + 1)
      check(i[3:0], i[7:4], i[8]);
  end

For wider designs, exhaustive testing is impossible; use $random or $urandom for random vectors and keep the self-checking logic. This is the idea that SystemVerilog constrained-random verification and UVM take much further.

Testbench for a sequential design

Sequential designs need a clock and a reset. Generate the clock with a free-running always block and drive inputs on the clock edge:

  reg clk = 0;
  always #5 clk = ~clk;      // 100 MHz clock, 10 ns period

  reg rst_n;
  initial begin
    rst_n = 0;               // assert active-low reset
    repeat (2) @(posedge clk);
    rst_n = 1;               // release reset
  end

  // drive inputs just after the clock edge to avoid races
  always @(posedge clk) begin
    #1 data_in <= $random;
  end

Drive inputs with non-blocking assignments or a small delay after the edge so the DUT samples stable values. Sample DUT outputs on the opposite edge or after a delay for the same reason.

Useful system tasks

TaskUse
$displayPrint once, when executed
$monitorPrint whenever a listed signal changes
$strobePrint at the end of the current time step (after non-blocking updates)
$timeCurrent simulation time
$random / $urandomRandom stimulus
$readmemh / $readmembLoad vectors or memory contents from a file
$fopen / $fdisplayWrite results to a log file
$dumpfile / $dumpvarsRecord a waveform (VCD)
$finishEnd the simulation

Common mistakes

  • Forgetting $finish, so the simulation runs forever when a clock is present.
  • Driving a DUT input declared as wire; inputs you drive from procedural blocks must be reg.
  • Changing inputs exactly on the active clock edge, creating a race between stimulus and the DUT.
  • Using == instead of !==/===, which hides X values.
  • No reset, so flip-flops start at X and every comparison fails.
  • Checking by eye instead of self-checking; it stops working after the second design change.

From Verilog testbenches to SystemVerilog and UVM

Plain Verilog testbenches are enough for small blocks. Industry verification uses SystemVerilog classes, constrained-random stimulus, functional coverage and assertions, organised with UVM. Read Verilog vs SystemVerilog, then the UVM testbench tutorial. These skills are taught in depth in the ASIC design verification course; the design side is covered in the RTL design course.

Frequently asked questions

What is a testbench in Verilog?

A non-synthesizable Verilog module with no ports that instantiates the design, applies stimulus to its inputs and checks its outputs in simulation.

Why does a testbench have no ports?

Because it is the top level of the simulation. Nothing outside the testbench drives or reads it; it creates its own signals internally.

What is a self-checking testbench?

One that computes expected results and compares them with the DUT’s outputs automatically, reporting pass or fail, instead of relying on someone reading printed values.

How do I generate a clock in a Verilog testbench?

Declare reg clk = 0; and write always #5 clk = ~clk; for a 10 ns period. Keep the clock in a separate always block from the stimulus.

Can a testbench be synthesized?

No. It uses delays, initial blocks and system tasks that have no hardware meaning. Only the DUT is synthesized.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.

Ask the Admin Team

Drop your basic question in comments: eligibility, prerequisites, tools, fee range, and placement support.

Our team reviews and responds regularly.

Tags :
Share This :
Next batch starts 28th October 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Popular: VLSI Course Fees · Learn VLSI From Scratch · VLSI Training With Job Support · Best VLSI Training Institute · VLSI Internship 2026 · Upcoming Batches
Cities: VLSI Training Institute in Hyderabad · VLSI Training Institute in Bangalore · VLSI Training in Noida & Delhi NCR · VLSI Training in Pune
Hi! Ask me about courses, fees, batches or discounts. ×
BANGALORE
HYDERABAD