digiwleeaLearn
Open the lab →

Testbenches: simulating a design

Driving inputs and checking outputs

5 min read

A VHDL testbench is a separate, non-synthesisable design that instantiates the circuit under test, applies a sequence of input stimuli over time, and uses assert statements to check the outputs, the textual equivalent of sweeping inputs and reading the waveform in a simulator.

You can now describe combinational logic with concurrent assignments and sequential logic with a clocked process. But how do you know your description is correct before committing it to a chip? The same way you check a circuit in the lab: drive the inputs and watch the outputs. In VHDL, the harness that does this is a testbench.

What a testbench is

A testbench is itself a VHDL design, but a special one: it has an entity with no ports (nothing connects to the outside, it is a closed experiment) and it is not synthesised, it only runs in a simulator. Inside, it does three jobs: instantiate the design under test (your entity), drive that design's inputs with a planned sequence of values over time (the stimulus), and check the outputs against what you expect.
An analogy: the design is a component on a test bench in an electronics lab; the testbench is the signal generator wired to its inputs plus the meter watching its outputs. The component does not know it is being tested, exactly as your circuit on the canvas does not change when you sweep its inputs to read the truth table. The testbench is the lab bench, not part of the product.

Stimulus and assert

A testbench uses two tools you have not met yet. The wait for statement advances simulated time (wait for 10 ns;), letting you set an input, wait, then check, which is how a static description gets a sense of "before and after". The assert statement checks a condition and complains if it fails: assert F = '1' report "AND of 1,1 should be 1" severity error;. If the output is wrong, the simulator prints your message, the textual version of a probe reading the wrong value.
library ieee;
use ieee.std_logic_1164.all;

entity and_gate_tb is
end entity;                         -- testbench has no ports

architecture sim of and_gate_tb is
  signal A, B, F : std_logic := '0';
begin
  -- instantiate the design under test
  dut : entity work.and_gate
    port map (A => A, B => B, F => F);

  -- drive inputs over time and check outputs
  process
  begin
    A <= '0'; B <= '0';
    wait for 10 ns;
    assert F = '0' report "0 and 0 should be 0" severity error;

    A <= '1'; B <= '1';
    wait for 10 ns;
    assert F = '1' report "1 and 1 should be 1" severity error;

    wait;                           -- stop: no more stimulus
  end process;
end architecture;
A small testbench for the AND gate: it instantiates the design as dut (device under test), drives A/B through a couple of input combinations, waits, and asserts the expected output each time. Note the empty port list and that it is simulation-only, never synthesised.
The port map (A => A, ...) line wires the testbench's signals to the design's ports, the textual form of dropping the part on the canvas and connecting wires to its pins. The final bare wait; (no for) parks the process forever so the simulation stops after the last check instead of looping.

Reading the result as a waveform

Run a testbench and the simulator produces a waveform: each signal drawn as a line over time, going high and low as your stimulus drives it, exactly the timing diagram you get from the lab's Waveform view. For combinational logic you are really just sweeping the truth table over time; for a clocked design you watch the output change on each rising edge, the same way you pulse a clock and read a flip-flop in the lab. The asserts turn that visual check into an automatic pass/fail.
Do not try to synthesise a testbench, and do not put wait for or fixed time delays in the design you intend to build. wait for 10 ns and bare wait are simulation-only: they model the passage of time on the bench, but a real chip has no such delays to map onto. Keep stimulus and timing in the testbench; keep the synthesisable entity free of them.
Why this matters: in real hardware design you verify in simulation before you ever program a chip, because a bug found in a waveform costs minutes while a bug found in fabricated silicon costs a respin. The testbench is where that verification lives, and it is the same discipline you have used all along: drive the inputs, read the output, confirm it matches the truth table. You have been writing testbenches by hand on the canvas this whole time.
Check yourself
Why does a testbench entity have an empty port list, while the design it tests (like and_gate) has ports?

Frequently asked

What is a testbench in VHDL?

A testbench is a VHDL design that verifies another design in simulation. It instantiates the design under test, drives its inputs with a planned sequence of values over time, and checks the outputs with assert statements. Its entity has no ports and it is never synthesised onto a chip.

What does the assert statement do in a VHDL testbench?

assert checks a condition and, if it is false, prints a message with a severity level (note, warning, error, failure). For example 'assert F = '1' report "should be 1" severity error;' flags it when the output does not match the expected value, turning a manual waveform check into an automatic pass/fail.

Why is a testbench not synthesisable?

A testbench uses simulation-only constructs like 'wait for 10 ns' to model the passage of time and to sequence stimulus. A real chip has no such delays to implement, so the testbench runs only in a simulator; only the design under test is synthesised onto hardware.

How does a testbench relate to a waveform?

Running a testbench produces a waveform: each signal drawn over time as the stimulus drives it. It is the same timing diagram you read in a lab simulator, sweeping a combinational truth table over time or watching a clocked output change on each rising edge, with assert statements automating the pass/fail check.

Every lesson here builds toward one thing: a working CPU, from the transistor up.

Open the free lab →
Found this useful? Share itShare on X