# Testbenches

*Driving inputs and checking outputs*

A Verilog testbench is a module with no ports that instantiates the design under test, drives its inputs over simulated time inside an initial block, and observes the outputs with $display or $monitor (ending on $finish), so a simulator can verify the design's logic before it is built.

Group: HDL
URL: https://digiwleea.wleeaf.dev/learn/verilog-testbench/

You can write combinational and sequential Verilog. How do you know it is **right**? You simulate it: build a **testbench**, a module whose only job is to feed the design inputs and watch its outputs. It is the same idea as sweeping a [truth table](https://digiwleea.wleeaf.dev/learn/truth-tables/) or driving a [waveform](https://digiwleea.wleeaf.dev/learn/timing/) on the canvas, written as code so it runs automatically.

## A module that tests another module

A testbench is a module with **no ports** (nothing connects to the outside, it is the top of the simulation). Inside, it declares signals to drive the **design under test** (the DUT), instantiates the DUT wiring those signals to its ports, then uses an **`initial` block** to apply a sequence of inputs over time. Here is a testbench for the [XOR gate](https://digiwleea.wleeaf.dev/learn/verilog-combinational/) you wrote:

```verilog
module xor_gate_tb;
  reg  A, B;
  wire F;

  xor_gate dut(.A(A), .B(B), .F(F));

  initial begin
    A = 0; B = 0; #10;
    A = 0; B = 1; #10;
    A = 1; B = 0; #10;
    A = 1; B = 1; #10;
    $finish;
  end

  initial
    $monitor("A=%b B=%b F=%b", A, B, F);
endmodule
```

_A testbench for xor_gate. It drives A and B through all four input combinations (one every 10 time units), prints each with $monitor, and ends with $finish. Inputs are reg (the testbench drives them); the output F is a wire (the DUT drives it)._

Read it top to bottom. `reg A, B;` are the inputs the testbench **drives** (assigned procedurally, so they must be [reg](https://digiwleea.wleeaf.dev/learn/verilog-values/)); `wire F;` is the output the DUT drives. The line `xor_gate dut(.A(A), .B(B), .F(F));` creates one copy of the gate named `dut` and connects each of its ports to a testbench signal by name. The `initial` block runs once at time zero: set the inputs, wait `#10` time units, change them, and so on, then `$finish` stops the simulator.

## Seeing the results

`$monitor` is a **system task** (the `$` marks a simulator command, not hardware) that reprints its line every time one of the listed signals changes, so you get a running log: `A=0 B=0 F=0`, `A=0 B=1 F=1`, and so on, exactly the XOR truth table. `$display` prints once when reached; `$finish` ends the run. To check automatically instead of eyeballing, you add an assertion: `if (F !== expected) $display("FAIL ...");`. A testbench that self-checks is a regression test for your hardware.

## Format specifiers: printing values your way

`$display` and `$monitor` take a **format string** first, then one argument for each `%` code in it, exactly like C's `printf`. The string is plain text with `%` codes punched into it, and each code says how to render the next value. A typical line reads `$display("t=%0t a=%b sum=%d", $time, a, sum);`, which pairs `%0t` with `$time`, `%b` with `a`, and `%d` with `sum`, left to right.

- `%b` prints the value in **binary**, every bit shown (a five-bit `sum` of 8 prints as `01000`).
- `%d` prints it in **decimal** (that same `sum` prints as `8`); `%d` right-justifies to the value's width, and `%0d` trims the leading pad spaces.
- `%h` prints it in **hexadecimal** (`sum` of 8 prints as `08`, two hex digits for a five-bit bus).
- `%0t` prints the current **simulation time** from the `$time` system function; the `0` asks for minimum width, so you see `10` and not a padded `        10`.
- Any text that is not a `%` code is printed **literally** (the `t=`, the `a=`, and the spaces); `%s` prints a string argument and `\n` starts a new line.

`$time` returns the current simulated time as an integer (its unit is set by the design's `timescale`), which is why testbench logs usually lead with `%0t`: you can see exactly *when* each line printed. Here is a compact testbench for a four-bit [adder](https://digiwleea.wleeaf.dev/learn/adder8/) `add4` (inputs `a`, `b`, output `sum`) that drives three input pairs and prints each result:

```verilog
module add4_tb;
  reg  [3:0] a, b;
  wire [4:0] sum;

  add4 dut(.a(a), .b(b), .sum(sum));

  initial begin
    a = 4'd3; b = 4'd5; #10;   // 3 + 5
    a = 4'd9; b = 4'd7; #10;   // 9 + 7
    a = 4'hF; b = 4'h2; #10;   // 15 + 2
    $finish;
  end

  initial
    $monitor("t=%0t  a=%0d b=%0d  sum=%0d  (bin %b, hex %h)",
             $time, a, b, sum, sum, sum);
endmodule
```

_A multi-bit testbench. The initial block drives a and b through three pairs, one every 10 time units; the $monitor line reprints whenever a listed signal changes. sum is [4:0] (five bits) so it holds the carry-out of 15 + 2._

Because `$monitor` reprints **every time one of the listed signals changes**, that single statement logs the entire run for you:

```verilog
t=0  a=3 b=5  sum=8  (bin 01000, hex 08)
t=10  a=9 b=7  sum=16  (bin 10000, hex 10)
t=20  a=15 b=2  sum=17  (bin 10001, hex 11)
```

_The console output the $monitor above produces (printed text, not hardware). Each line lands the instant its inputs change, then $finish stops the run at t=30. Read the sum column down: 8, 16, 17, exactly 3+5, 9+7, and 15+2._

Contrast that with `$display`, which prints its line **once**, wherever execution reaches it. So you reach for `$display` for one-shot messages (a header row, a final `PASS`/`FAIL` verdict) and for `$monitor` when you want a continuous trace that follows the signals on its own. Only one `$monitor` is active at a time, and it watches only the signals named in its argument list.

> **WARN:** `initial` blocks and `#` delays are **simulation only**, they are not synthesisable and never become gates. That is correct here: a testbench is not hardware, it is the scaffolding that exercises hardware. Never put `#10` delays or `initial` stimulus in a module you intend to synthesise; keep testbench code and design code in separate files.

> **KEY:** Why this matters: in real HDL work you write as much testbench as design. Every block, a gate, an [adder](https://digiwleea.wleeaf.dev/learn/adder8/), a [register](https://digiwleea.wleeaf.dev/learn/register8/), the whole [CPU](https://digiwleea.wleeaf.dev/learn/cpu/), is verified by a testbench that drives known inputs and checks the outputs before a chip is ever made, because fixing a bug in simulation costs seconds and fixing it in silicon costs months. Simulation is the safety net under everything you have built.

**Q (Try it):** In the testbench, why is `A` declared `reg` but `F` declared `wire`? What would go wrong if you swapped them?

**A:** `A` is a `reg` because the **testbench drives it** procedurally inside the `initial` block, and only regs can be assigned in a procedural block. `F` is a `wire` because the **DUT drives it** (through its output port); the testbench only observes it. Swapping them breaks both: you cannot assign a `wire` (`A`) inside `initial`, and a module output port cannot connect to a `reg` externally, `F` must be a `wire` (a net) for the DUT to drive it. Drivers connect to wires; procedurally-assigned stimulus is reg.

### FAQ

**Q:** What is a testbench in Verilog?

**A:** A testbench is a module with no ports that instantiates the design under test, drives its inputs over simulated time in an initial block, and observes the outputs with $display or $monitor. It verifies a design in simulation before it becomes hardware, the coded equivalent of sweeping a truth table by hand.

**Q:** What is the difference between $display and $monitor?

**A:** $display prints its line once, when execution reaches it. $monitor prints its line every time one of the listed signals changes, giving a running log of the simulation. Both are system tasks (marked with $) that only run in simulation, not in synthesised hardware.

**Q:** Why are the DUT inputs reg and the outputs wire in a testbench?

**A:** The testbench drives the inputs procedurally inside an initial block, and only a reg can be assigned there. The design under test drives its own outputs through its ports, so those must be wires that the testbench observes. Drivers connect to wires; procedurally-assigned stimulus is reg.

**Q:** Are initial blocks and # delays synthesisable in Verilog?

**A:** No. initial blocks and # time delays are simulation-only constructs used to write testbenches; they never become gates. That is fine because a testbench is not hardware. Design code you intend to synthesise must avoid them and is kept in separate files from the testbench.

**Q:** What do %b, %d, and %h mean in a Verilog $display?

**A:** They are format specifiers that pick the base for the next value: %b prints binary (every bit), %d prints decimal, and %h prints hexadecimal. A five-bit value of 8 prints as 01000 with %b, as 8 with %d, and as 08 with %h. Text and spaces in the format string print literally, and %0d removes the leading pad spaces from a decimal field.

**Q:** What does $time print in a Verilog testbench?

**A:** $time is a system function that returns the current simulation time as an integer, so a line like $display("t=%0t ...", $time, ...) stamps each print with when it happened. The %0t code formats a time value at minimum width, and the actual unit (nanoseconds, picoseconds) is set by the design's timescale directive.
