Testbenches and Simulation

Writing VHDL testbenches to verify your circuit behavior before synthesis.

What is a Testbench?

A testbench (TB) is a VHDL simulation file that:

  1. Instantiates the Device Under Test (DUT)
  2. Generates stimuli (input signals)
  3. Observes and verifies outputs

A testbench has no ports — it is a closed environment.

entity tb_mux4to1 is
  -- No ports!
end entity tb_mux4to1;

Typical Testbench Structure

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity tb_mux4to1 is
end entity tb_mux4to1;
 
architecture tb of tb_mux4to1 is
 
  -- Simulation constants
  constant c_CLK_PERIOD : time := 10 ns;  -- 100 MHz
 
  -- Signals to connect to the DUT
  signal w_d0, w_d1, w_d2, w_d3 : std_logic := '0';
  signal w_sel : std_logic_vector(1 downto 0) := "00";
  signal w_y   : std_logic;
 
begin
 
  -- DUT instantiation
  DUT : entity work.mux4to1
    port map (
      i_d0  => w_d0,
      i_d1  => w_d1,
      i_d2  => w_d2,
      i_d3  => w_d3,
      i_sel => w_sel,
      o_y   => w_y
    );
 
  -- Stimuli process
  p_stimuli : process
  begin
    -- Test 1: select d0
    w_d0 <= '1'; w_d1 <= '0'; w_d2 <= '0'; w_d3 <= '0';
    w_sel <= "00";
    wait for 20 ns;
    assert w_y = '1' report "FAIL: sel=00 should output d0=1" severity error;
 
    -- Test 2: select d1
    w_sel <= "01";
    wait for 20 ns;
    assert w_y = '0' report "FAIL: sel=01 should output d1=0" severity error;
 
    -- Test 3: select d3
    w_d3 <= '1';
    w_sel <= "11";
    wait for 20 ns;
    assert w_y = '1' report "FAIL: sel=11 should output d3=1" severity error;
 
    report "All tests passed!" severity note;
    wait;  -- end simulation
  end process p_stimuli;
 
end architecture tb;

Clock Generation

-- Clock generation process
p_clk : process
begin
  i_clk <= '0';
  wait for c_CLK_PERIOD / 2;
  i_clk <= '1';
  wait for c_CLK_PERIOD / 2;
end process p_clk;
 
-- Concurrent alternative
i_clk <= not i_clk after c_CLK_PERIOD / 2;

Complete Testbench for Sequential Circuit

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity tb_counter is
end entity tb_counter;
 
architecture tb of tb_counter is
 
  constant c_CLK_PERIOD : time := 10 ns;
 
  signal i_clk : std_logic := '0';
  signal i_rst : std_logic := '1';
  signal o_cnt : std_logic_vector(3 downto 0);
 
begin
 
  -- Clock generation
  i_clk <= not i_clk after c_CLK_PERIOD / 2;
 
  -- DUT
  DUT : entity work.counter_4bit
    port map (
      i_clk => i_clk,
      i_rst => i_rst,
      o_cnt => o_cnt
    );
 
  -- Stimuli
  p_test : process
  begin
    -- Reset active for 3 cycles
    i_rst <= '1';
    wait for 3 * c_CLK_PERIOD;
    assert unsigned(o_cnt) = 0 report "Reset failed" severity error;
 
    -- Start counting
    i_rst <= '0';
    wait for 5 * c_CLK_PERIOD;
    assert unsigned(o_cnt) = 5 report "Counting incorrect" severity error;
 
    -- Wait for overflow (16 total cycles = back to 0)
    wait for 11 * c_CLK_PERIOD;
    assert unsigned(o_cnt) = 0 report "Overflow incorrect" severity error;
 
    report "Testbench completed successfully" severity note;
    wait;
  end process p_test;
 
end architecture tb;

Useful Simulation Instructions

InstructionUsage
wait for 10 ns;Wait a fixed duration
wait until rising_edge(i_clk);Wait for a clock edge
wait until i_valid = '1';Wait for a condition
wait;Stop simulation
assert condition report "msg" severity level;Verification

Severity Levels

LevelEffect
noteInformational message
warningWarning, simulation continues
errorError, simulation may continue
failureImmediate simulation stop

Best Practices

  1. Name assertions clearly: clear message to identify the failure
  2. Verify after stabilization: wait with wait for before assert
  3. Test edge cases: min/max values, state transitions
  4. Separate stimuli and verification: separate processes for clarity
  5. Explicit end: finish with report "... success" + wait;