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:
- Instantiates the Device Under Test (DUT)
- Generates stimuli (input signals)
- 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
| Instruction | Usage |
|---|---|
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
| Level | Effect |
|---|---|
note | Informational message |
warning | Warning, simulation continues |
error | Error, simulation may continue |
failure | Immediate simulation stop |
Best Practices
- Name assertions clearly: clear message to identify the failure
- Verify after stabilization: wait with
wait forbeforeassert - Test edge cases: min/max values, state transitions
- Separate stimuli and verification: separate processes for clarity
- Explicit end: finish with
report "... success"+wait;