Topic 6 · Verification Methodology

Testbench Anatomy

Video 1 of 4 · ~12 minutes

Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF

AnatomySelf-CheckingTasksFile-Driven

🌍 Where This Lives

Where it shows up

At Intel, Apple, NVIDIA, AMD — more than half the engineers who touch silicon never design any of it. They build the harness that pokes the chip a million times an hour, for two years, before it ever sees a fab. Headcount runs 2:1 over the designers. So do the senior salaries. The thing being checked is not the chip — it's the engineer's claim that the chip works.

When it goes wrong

The Pentium FDIV bug shipped because the harness asked four billion questions and missed a small region where the chip would lie. Intel paid $475M. Ariane 5 exploded 37 seconds into its maiden flight because no one had ever fed the maiden-flight profile into the inertial unit and looked at what came back. The bench tests passed. The bench tests asked the wrong questions.

Career tag: “Can you write a testbench?” is table stakes for any RTL position. Writing good testbenches is what distinguishes senior from junior.

⚠️ A Testbench Is a Module. Just a Weird One.

❌ Wrong Model

“A testbench is a different kind of thing — some special file with $display and timing. Different syntax than my design.”

✓ Right Model

A testbench is just a module with no ports. It instantiates your Design Under Test (DUT) as a sub-module, drives the DUT's inputs, reads the DUT's outputs, and compares them to expected values. Same Verilog language you've been writing all week — plus a handful of simulation-only constructs (initial, $display, #delay) that tools like Yosys ignore.

The receipt: A testbench file typically starts module tb_foo; — no parentheses, no port list. It lives in the top of your simulation hierarchy, orchestrating the test from outside.

🧬 How the Testbench Wraps the DUT

Testbench wraps DUT
Three boundaries, one module: stimulus on the left drives the DUT's inputs (so those signals are reg in the tb); the checker on the right reads the DUT's outputs (those are wire). The whole assembly is a single Verilog module — tb_my_design — sitting at the top of the simulation hierarchy with nothing above it.

🪞 The Biggest Issue: Designer = Verifier

Designer-verifier blindspot
Logic flaws are the same on both sides. If you misread the spec when writing the RTL, you will almost always misread it the same way when writing the testbench. Your “PASS” report just confirms that your bug is internally consistent. Industry's 2:1 verifier:designer ratio is not bureaucracy — it's because independent eyes are the only reliable cure.

👁️ I Do — The Testbench Template

`timescale 1ns/1ps         // simulation resolution
module tb_adder;            // no ports — top of sim hierarchy

    // 1. Declare DUT signals
    reg  [3:0] a, b;        // inputs to DUT → reg (driven by tb)
    wire [4:0] sum;         // outputs from DUT → wire (read by tb)

    // 2. Instantiate the DUT
    adder dut (.i_a(a), .i_b(b), .o_sum(sum));

    // 3. Clock generator (if sequential) — always block with period
    reg clk = 0;
    always #5 clk = ~clk;   // 10 ns period = 100 MHz

    // 4. Waveform dump (for GTKWave)
    initial begin
        $dumpfile("tb_adder.vcd");
        $dumpvars(0, tb_adder);
    end

    // 5. Stimulus and checks
    initial begin
        a = 0; b = 0;
        #10; a = 4'd5; b = 4'd3;
        #10; $display("5+3 = %d", sum);
        #10; $finish;
    end
endmodule
Five standard parts: (1) signal declarations, (2) DUT instantiation, (3) clock generator, (4) waveform dump, (5) stimulus/checks. Color key: a · b · sum · clk · $dump* — same colors in the next slide's block diagram.

🧱 Same Template — as an RTL Block Diagram

Testbench template — five-part RTL block diagram
One-to-one mapping: each numbered region in the diagram corresponds to a numbered section of the code. The colored wires — a, b, clk, sum — match the highlighted identifiers in the template.

🤝 We Do — The reg/wire Rule

In a testbench, why must DUT inputs be declared as reg in the testbench?

Answer: The testbench drives those signals procedurally (inside initial). Topic 2 rule: anything assigned in an always/initial block must be declared reg. DUT outputs are driven by the DUT, so they must be wire on the testbench side.
Common bug: Declaring a DUT input as wire in the testbench. The simulator will refuse to let you assign to it. Easy fix, confusing error message.

🧪 You Do — Predict the Simulation

At what simulation time does the third $display fire?

initial begin
    #5   $display("first at t=%0t", $time);
    #10  $display("second at t=%0t", $time);
    #20  $display("third at t=%0t", $time);
    $finish;
end
Cumulative delay timeline — #5, #10, #20 → t=5, t=15, t=35
Answer: t=35. Delays are cumulative. #5 at start → t=5 → first display. #10 → t=15 → second. #20 → t=35 → third. Then $finish.
▶ LIVE DEMO

Build a Testbench from Scratch

~5 minutes

▸ COMMANDS

cd lecture_examples/week2_day06/d06_s1_ex1/
# Start from empty tb_adder.v
# Build: decls → instantiate → clock → dump → stim
iverilog -g2012 -o sim.vvp tb_adder.v adder.v
vvp sim.vvp
gtkwave tb_adder.vcd &

▸ EXPECTED STDOUT

VCD info: dumpfile
  tb_adder.vcd opened
a=0 b=0 -> sum=0
a=1 b=2 -> sum=3
a=200 b=100 -> sum=300
a=255 b=1 -> sum=256 (carry expected)
tb_adder.v:31: $finish called

▸ GTKWAVE

Signals a · b · sum. Watch the values change at the delays you scheduled. If you forgot $dumpvars your waveform will be empty — the most common testbench failure mode.

🔧 What Did the Tool Build?

Testbenches don't synthesize — they're simulation-only:

$ yosys -p "read_verilog tb_adder.v; synth_ice40 -top tb_adder" -q
ERROR: Module `\tb_adder' is not synthesizable.
  - contains initial block (simulation only)
  - contains $display (simulation only)
  - contains timing control (#10) (simulation only)
Yosys rejects testbench; accepts DUT
Expected. The three highlighted constructs are sim-only and never synthesize. The DUT (right side of the diagram) compiles fine; the testbench (left) is rejected by design.

🤖 Check the Machine

Ask AI: “Here's my 4-bit counter. Write a self-contained testbench that exercises reset, counts to 15, and verifies rollover.”

TASK

Ask AI for a full testbench for your counter.

BEFORE

Predict: 5-part structure with reset + 16 clock cycles + rollover check.

AFTER

Good AI: complete 5 parts. Weak AI misses $dumpfile or uses wrong reg/wire.

TAKEAWAY

AI is actually good at testbench boilerplate. Verify the dumpvars line exists.

Key Takeaways

 A testbench is just a Verilog module with no ports.

 Five parts: decls, DUT instance, clock, dumpfile, stimulus.

 DUT inputs → reg in tb. DUT outputs → wire in tb.

 Testbenches don't synthesize — that's the whole point.

 Same person writing RTL and testbench = same logic flaw on both sides.

Verification is bigger than design. Today you start becoming that engineer.

🔗 Transfer

Self-Checking Testbenches

Video 2 of 4 · ~12 minutes

▸ WHY THIS MATTERS NEXT

Printing values and eyeballing waveforms doesn't scale. Video 2 introduces self-checking testbenches — the PASS/FAIL pattern you've been reading all week, from the inside. By the end, every testbench you write will check itself and tell you exactly what broke.