Loading header...

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:

  1. Open the waveform around 10 ns to 30 ns.
  2. Confirm clk has a rising edge.
  3. Confirm rst is low before the edge.
  4. Confirm en is high before the edge.
  5. Confirm the RTL uses elsif en = '1' then count_r <= count_r + 1;.
  6. 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.

Further Reading

Mind Map

mindmap root((GHDL Flow)) Core analyze elaborate run inspect VCD Order package first RTL next testbench last top is TB Checks assertions PASS note severity error stop test Debug first error missing work unit U output delta timing Applications VHDL labs regression tests CI scripts waveform debug Mistakes wrong entity old VCD no numeric_std waveform only