Lab 2A – Introduction to Testbenches
Introduction
A testbench is an important component in digital design. When developing a new or evolving design, an organization is typically divided into two functional units:
| Team | Focus |
|---|---|
| Design Team | Creating the actual product design |
| Test/Verification Team | Verifying the design meets specifications and testing during manufacturing |
In this lab, you will be introduced to the verification process.
Why Testbenches Matter
Both teams start with the same specification. In a simple design block, a complete specification can be based on an equation or truth table. By complete, we mean that the values of all output signals are defined for all possible combinations of input signals.
For simple designs, this process is fairly straightforward:
- Apply all possible inputs to the design
- Observe the outputs
- Compare outputs to expected values
While we could check outputs by hand, this is tedious and error-prone. A better solution is to use a self-checking testbench that automatically compares each output signal with an expected value.
Benefits of Self-Checking Testbenches
-
Independent Verification - The verification team works from the specification, not the design code. Two independent views must match, reducing functional design errors.
-
Regression Testing - As the design evolves (minimizing power, increasing speed), the team can quickly verify changes haven’t broken functionality by reusing the testbench.
Design/Module Concepts
Verilog/SystemVerilog can be used for both design and verification. However, not all language constructs translate into hardware. The testbench is an abstract design that:
- Applies stimuli to inputs of a real design
- Compares outputs to expected values
- Is NOT part of the hardware implementation
File Naming Convention
We use a consistent naming style:
| File Type | Naming Pattern | Example |
|---|---|---|
| Design Module | <module_name>.sv | and_3_inputs.sv |
| Testbench | tb_<module_name>.sv | tb_and_3_inputs.sv |
| Test Vectors | tb_<module_name>.txt | tb_and_3_inputs.txt |
Example: Three-Input AND Gate
The Design Under Test
module and_3_inputs (output logic f, input logic a, b, c);
// this is the design to test
assign f = a & b & c;
endmodule
This design synthesizes into a three-input AND gate that can be loaded into FPGA hardware.
Truth Table Specification
| data_in[2:0] | expected_out[0:0] |
|---|---|
| 3’b000 | 1’b0 |
| 3’b001 | 1’b0 |
| 3’b010 | 1’b0 |
| 3’b011 | 1’b0 |
| 3’b100 | 1’b0 |
| 3’b101 | 1’b0 |
| 3’b110 | 1’b0 |
| 3’b111 | 1’b1 |
Test Vector File (tb_and_3_inputs.txt)
000_0
001_0
010_0
011_0
100_0
101_0
110_0
111_1
Note: Verilog allows underscores to separate data patterns for readability. The file has 8 rows (2³) and 4 columns.
Building the Testbench
Step 1: Module Declaration
module tb_and_3_inputs (); // top level testbench wrapper
// no inputs/outputs at top level for simulation
...
endmodule
Step 2: Declare Parameters and Signals
parameter ROWS=8, INPUTS=3, OUTPUTS=1;
logic [INPUTS-1:0] data_in;
logic [OUTPUTS-1:0] expected_out;
logic [OUTPUTS-1:0] sim_out; // simulated output
logic [INPUTS+OUTPUTS-1:0] test_vectors [0:ROWS-1];
integer i; // loop index
integer mm_count; // mismatch count
Using parameters makes the code reusable - just edit one line for different designs!
Step 3: Instantiate Unit Under Test
// instantiate the unit under test (instance name uut)
and_3_inputs uut (
.f(sim_out),
.a(data_in[2]),
.b(data_in[1]),
.c(data_in[0])
);
Key Concept: Instantiation is a parallel operation. Think of the
uutas physical hardware - change the inputs and the outputs change immediately.
Step 4: The Initial Block
The initial block executes statements sequentially, starting at simulation time zero:
initial begin
$dumpfile("tb_and_3_inputs.vcd");
$dumpvars();
mm_count = 0;
// read test vectors from file
$readmemb("tb_and_3_inputs.txt", test_vectors);
// loop over all rows
for (i=0; i<ROWS; i=i+1) begin
// apply test vector
{data_in, expected_out} = test_vectors[i];
#10; // wait 10 ns for outputs to settle
// compare output to expected
if (sim_out !== expected_out) begin
$display("Mismatch--index: %d; input: %b, expected: %b, received: %b",
i, data_in, expected_out, sim_out);
mm_count = mm_count + 1;
end
#10; // symmetry delay
end
// report results
if (mm_count == 0)
$display("Simulation complete - no mismatches!!!");
else
$display("Simulation complete - %d mismatches!!!", mm_count);
$finish;
end
Key Functions Explained
| Function | Purpose |
|---|---|
$dumpfile() | Creates VCD file for waveform viewing |
$dumpvars() | Records all signals to VCD file |
$readmemb() | Reads binary data from text file |
$display() | Prints formatted message (like C’s printf) |
$finish | Ends simulation cleanly |
Timescale Directive
`timescale 1ns / 1ps
This defines:
- Time units: 1 ns
- Precision: 1 ps
So #10 means delay 10 nanoseconds.
Complete Testbench Template
`timescale 1ns / 1ps
module tb_and_3_inputs ();
parameter ROWS=8, INPUTS=3, OUTPUTS=1;
logic [INPUTS-1:0] data_in;
logic [OUTPUTS-1:0] expected_out;
logic [OUTPUTS-1:0] sim_out;
logic [INPUTS+OUTPUTS-1:0] test_vectors [0:ROWS-1];
integer i;
integer mm_count;
// instantiate the unit under test
and_3_inputs uut (
.f(sim_out),
.a(data_in[2]),
.b(data_in[1]),
.c(data_in[0])
);
initial begin
$dumpfile("tb_and_3_inputs.vcd");
$dumpvars();
mm_count = 0;
$readmemb("tb_and_3_inputs.txt", test_vectors);
for (i=0; i<ROWS; i=i+1) begin
{data_in, expected_out} = test_vectors[i];
#10;
if (sim_out !== expected_out) begin
$display("Mismatch--index: %d; input: %b, expected: %b, received: %b",
i, data_in, expected_out, sim_out);
mm_count = mm_count + 1;
end
#10;
end
if (mm_count == 0)
$display("Simulation complete - no mismatches!!!");
else
$display("Simulation complete - %d mismatches!!!", mm_count);
$finish;
end
endmodule
Lab Exercise: Hamming Encoder Testbench
Design Specification
Create a testbench for the Hamming(7,4) encoder:
module hamming7_4_encode(output logic [7:1] e, input logic [4:1] d);
...
endmodule
Truth Table
| d[4:1] | e[7:1] |
|---|---|
| 4’b0000 | 7’b0000000 |
| 4’b0001 | 7’b0000111 |
| 4’b0010 | 7’b0011001 |
| 4’b0011 | 7’b0011110 |
| 4’b0100 | 7’b0101010 |
| 4’b0101 | 7’b0101101 |
| 4’b0110 | 7’b0110011 |
| 4’b0111 | 7’b0110100 |
| 4’b1000 | 7’b1001011 |
| 4’b1001 | 7’b1001100 |
| 4’b1010 | 7’b1010010 |
| 4’b1011 | 7’b1010101 |
| 4’b1100 | 7’b1100001 |
| 4’b1101 | 7’b1100110 |
| 4’b1110 | 7’b1111000 |
| 4’b1111 | 7’b1111111 |
Steps to Complete
- Open
tb_template.svin one window - Create new file:
tb_hamming7_4_encode.sv - Copy the template and modify:
- Change
ROWS=16, INPUTS=4, OUTPUTS=7 - Update module instantiation
- Update file names
- Change
- Create
tb_hamming7_4_encode.txtwith truth table data - Test syntax by running
tb_hamming7_4_encode.m_sim
Expected Output (Before Design Complete)
Since the Hamming encoder design is empty, you should see:
Mismatch--loop index i: 0; input: 0000, expected: 0000000, received: xxxxxxx
Mismatch--loop index i: 1; input: 0001, expected: 0000111, received: xxxxxxx
...
Mismatch--loop index i: 15; input: 1111, expected: 1111111, received: xxxxxxx
Simulation complete - 16 mismatches!!!
Viewing the Waveform
The VCD file contains signal data useful for debugging.
Using WaveTrace (VS Code Extension)
Your lab instructor will demonstrate using WaveTrace, which provides waveform viewing directly in VS Code.
Alternative: EPWave (Online)
- Download VCD file - Right-click on
tb_hamming7_4_encode.vcdand select Download (do NOT open it directly) - Open EPWave - Go to https://www.edaplayground.com/w/home
- Upload file - Select Upload and choose your VCD file
- Select signals - Click “Get Signals” and select
.uutto view design signals - View waveforms - Use “Append All” to add signals to the viewer
Tip: Change display format from hex to binary using the Radix dropdown.
Important: Simulation vs Hardware
⚠️ Critical Rule: If your design does not pass simulation, don’t waste time synthesizing and loading it into hardware!
Why This Matters
- Mismatches in simulation will NOT magically correct in hardware
- Hardware debugging has limited visibility into the FPGA
- Simulation provides complete visibility of any signal at any time
- Self-checking testbenches point you directly to problem patterns
The Golden Rule
A passing simulation almost guarantees that your design will operate correctly in hardware.
Synthesis
There is no synthesis for this lab. You are creating the testbench that will be used to verify the Hamming encoder design in the next lab.
Deliverables
- Completed
tb_hamming7_4_encode.svtestbench - Completed
tb_hamming7_4_encode.txttest vector file - Screenshot showing simulation results (16 mismatches expected)