Simulation and Testbenches
Simulation is where you ask the design questions before hardware makes them expensive. A testbench is not synthesized into the FPGA; it is a verification environment that drives inputs, checks outputs, and helps you debug.
Learning Objectives
You will be able to:
- explain the difference between design RTL and testbench code;
- write a clock/reset testbench structure;
- create self-checking tests;
- use waveforms as a debugging tool instead of a substitute for checks.
Design Code vs Testbench Code
| Design RTL | Testbench |
|---|---|
| Synthesizes to FPGA hardware | Runs only in simulator |
| Must use synthesizable constructs | Can use delays, loops, file I/O, tasks |
| Describes actual registers and logic | Drives stimulus and checks behavior |
| Must meet timing | Must expose bugs clearly |
Minimal Clocked Testbench
`timescale 1ns/1ps
module tb_counter8;
reg clk = 0;
reg rst = 1;
reg en = 0;
wire [7:0] count;
counter8 dut (.clk(clk), .rst(rst), .en(en), .count(count));
always #5 clk = ~clk; // 100 MHz illustrative clock
initial begin
repeat (2) @(posedge clk);
rst <= 0;
en <= 1;
repeat (4) @(posedge clk);
if (count !== 8'd4) begin
$display("FAIL count=%0d", count);
$finish;
end
$display("PASS counter8");
$finish;
end
endmodule
What to Check
Good testbenches check behavior, not just that the simulator ran.
- Reset state.
- First valid output after enable.
- Boundary conditions.
- Overflow or wrap behavior.
- Illegal or unused states.
- Back-to-back transactions.
- Stalls, pauses, and recovery.
Waveforms Help You See Timing
title "Illustrative simulation sequence"
time start=0 end=80 unit=ns divisions=8
CLK: square label="clk" low=0 high=1 duty=50 cycles=8 unit=logic color=#2563eb
RST: pulse label="rst" low=0 high=1 at=0 width=20 unit=logic color=#dc2626
EN: step label="en" low=0 high=1 at=25 unit=logic color=#7c3aed
COUNT: sawtooth label="count rises after reset" min=0 max=4 cycles=1 unit=count color=#16a34a
marker RELEASE at=20 label="reset released"
marker CHECK at=70 label="check expected count"
This is an explanatory waveform. Your simulator waveform will reflect the actual RTL and testbench.
Self-Checking Beats Manual Inspection
Manual waveform inspection is useful while debugging. It is not enough for regression testing. A self-checking testbench should print PASS only when expected behavior is proven.
Worked Example: Counter Reset Check
Requirement: when rst is high at a clock edge, count becomes zero.
Test idea:
- Let the counter run to a nonzero value.
- Assert reset.
- Wait for a clock edge.
- Check
count == 0.
This catches designs where reset is missing, inverted, asynchronous in the wrong way, or connected to the wrong register.
Common Mistakes
- Treating “no simulator crash” as success.
- Looking at waveforms once and never automating checks.
- Forgetting reset behavior in tests.
- Driving inputs exactly on the active clock edge, creating artificial races.
- Testing only the happy path.
Summary
Simulation gives fast feedback. A useful testbench creates a realistic clock/reset sequence, drives meaningful stimulus, checks expected behavior automatically, and uses waveforms to debug failures. Hardware testing comes later; it should not be the first time your design is exercised.
Next: Verilog Simulation: Verilator, Icarus Verilog, and GTKWave.
Further Reading
- Verilator user guide
- GHDL quick start and VCD wave output
- cocotb documentation
- Vendor simulator tutorials