GHDL and VHDL Simulation
GHDL is the most common open-source simulator for learning VHDL. It follows the VHDL model closely: analyze design units, elaborate the top-level testbench, then run the simulation. That structure teaches dependencies and makes failures easier to locate.
Learning Objectives
By the end of this lesson, you should be able to:
- run the GHDL analyze, elaborate, and run steps;
- compile packages before dependent VHDL files;
- generate VCD waveforms for GTKWave;
- write VHDL assertions that make simulations self-checking;
- debug common GHDL errors from their first useful message.
The Three-Step GHDL Flow
ghdl -a counter4.vhd
ghdl -a counter4_tb.vhd
ghdl -e counter4_tb
ghdl -r counter4_tb
ghdl -a: analyze a VHDL source file. This can fail on syntax errors, missing packages, and type errors.ghdl -e: elaborate a chosen top entity. This can fail when an entity, architecture, or design unit is unresolved.ghdl -r: run the elaborated simulation. This can fail through assertions, runtime stops, or tests that never finish.
Some GHDL backends can elaborate automatically during -r, but beginners should learn the explicit three-step model. It mirrors how VHDL tools understand projects.
Compile Order Matters
If uart_tx.vhd uses work.uart_pkg.all, the package must be analyzed first:
ghdl -a rtl/uart_pkg.vhd
ghdl -a rtl/baud_tick.vhd
ghdl -a rtl/uart_tx.vhd
ghdl -a tb/uart_tx_tb.vhd
ghdl -e uart_tx_tb
ghdl -r uart_tx_tb
When GHDL says it cannot find a package, entity, or architecture, check the file order before rewriting the code.
Generating A Waveform
ghdl -r counter4_tb --vcd=counter4.vcd
gtkwave counter4.vcd
VCD files are widely supported and easy for students to inspect. For large simulations, other formats may be more efficient, but VCD is a good starting point.
Self-Checking Assertions
assert count = "0001"
report "counter did not increment after enable"
severity error;
Use severity error when the test should fail. Use severity note for an intentional final pass message:
assert false report "PASS counter test complete" severity note;
The final note is not a failure in GHDL. It is a clear marker that the test reached the intended end.
Minimal VHDL Testbench Pattern
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity counter4_tb is
end entity;
architecture tb of counter4_tb is
constant CLK_PERIOD : time := 10 ns;
signal clk : std_logic := '0';
signal rst : std_logic := '1';
signal en : std_logic := '0';
signal count : std_logic_vector(3 downto 0);
begin
clk <= not clk after CLK_PERIOD / 2;
dut : entity work.counter4
port map (
clk => clk,
rst => rst,
en => en,
count => count
);
process
begin
wait until rising_edge(clk);
rst <= '0';
en <= '1';
wait until rising_edge(clk);
wait for 1 ns;
assert unsigned(count) = 1
report "expected count 1"
severity error;
assert false report "PASS minimal test" severity note;
wait;
end process;
end architecture;
The wait for 1 ns gives clocked signal assignments time to update before the assertion samples count.
Simulation Timeline
title "GHDL analyze-run-debug loop"
time start=0 end=60 unit=ns divisions=6
CLK: square label="clk" low=0 high=1 duty=50 cycles=6 unit=logic color=#2563eb
RST: pulse label="rst" low=0 high=1 at=0 width=10 unit=logic color=#dc2626
EN: step label="en" low=0 high=1 at=12 unit=logic color=#7c3aed
COUNT: sawtooth label="count checked" min=0 max=3 cycles=1 unit=count color=#16a34a
marker RELEASE at=10 label="rst off"
marker ASSERT at=25 label="assert"
This waveform is illustrative. Your generated VCD should be inspected when an assertion fails or when you need to explain timing.
Worked Example: Fixing A Failed Increment
Suppose the test reports:
counter4_tb.vhd:42:13:@26ns:(assertion error): expected count 1
Debug in this order:
- Open the waveform around 10 ns to 30 ns.
- Confirm
clkhas a rising edge. - Confirm
rstis low before the edge. - Confirm
enis high before the edge. - Confirm the RTL uses
elsif en = '1' then count_r <= count_r + 1;. - Confirm the test waits one small delay after the edge before checking.
This process separates stimulus mistakes from RTL mistakes.
Practical Checks
Before trusting a GHDL result:
- analyze packages first;
- analyze RTL before testbenches that instantiate it;
- elaborate the testbench, not the synthesizable design entity;
- make the testbench stop intentionally;
- make assertion errors fail the run;
- inspect warnings about unbound components or missing units;
- regenerate the VCD after every code change.
Common Failure Symptoms
unit ... not found in library work: file not analyzed or wrong compile order.cannot find entity or configuration: elaborating the wrong top name.- Output is
U: missing reset, uninitialized driver, or never-driven signal. - Assertion one cycle early: sampling in the same delta cycle as the edge.
- Simulation never ends: testbench has no stop condition.
- VCD does not change: old waveform or test did not rerun.
Common Mistakes
- Running the design entity instead of the testbench entity.
- Forgetting to compile a package before using it.
- Treating waveforms as proof without assertions.
- Using nonstandard arithmetic packages instead of
numeric_std. - Ignoring the first GHDL error and chasing later messages.
- Mixing generated simulator files with source files.
Summary
GHDL gives VHDL students a transparent simulation workflow: analyze, elaborate, run, and inspect. Use numeric_std, compile dependencies in order, make assertions decide pass or fail, and use GTKWave to understand timing when a check fails.
Next: Exercise: Find and Fix a Timing Failure.