VHDL · Chapter 1.9 · Foundation
Compilation, Elaboration, and the Build Flow
VHDL does not go straight from text to hardware. It passes through three distinct stages, analysis or compile, then elaboration, and then simulation or synthesis, and most confusing VHDL errors come from not knowing which stage you are in. This lesson builds the mental model that separates serious HDL engineers from beginners: what each stage actually does, why compile order is mandatory in VHDL, how an entity is bound to an architecture during elaboration, and how to read cannot-find-unit and unresolved-binding errors as precise signals about which stage failed and why. Once you can name the stage, the tool messages stop being mysterious and start pointing straight at the fix.
Foundation16 min readVHDLCompilationElaborationBuild FlowCompile OrderDebugging
1. Intuition — three stages, not one
Coming from software, you expect "compile, then run." VHDL has a stage in the middle, and skipping over it is why the tools seem to throw errors at surprising times. The journey from source to a running model is:
- Analysis (compile) — each design unit is checked on its own and filed into a library. Think "proofread each part and put it on the shelf."
- Elaboration — the tool picks a top unit and builds the design: it binds every entity to an architecture, expands the hierarchy, resolves generics, and creates the signals. Think "assemble the parts into one machine."
- Simulation / synthesis — only now does the elaborated machine run (simulator) or get mapped to gates (synthesiser).
2. The build flow — and what each stage catches
The stages run in order, and each one catches a different class of error. Knowing which stage rejected your code tells you where to look:
There is no waveform on this page on purpose: compilation and elaboration happen before any signal has a value. The build flow itself is the artifact that teaches the concept.
3. Analysis files units into a library
Analysis takes one design unit at a time, checks it, and stores the compiled result in
a library (work by default). It verifies syntax, type correctness, and that every
name it references is already visible — which means every unit it depends on must
already be analysed. That requirement is the source of VHDL's compile-order rules:
- a package body needs its package analysed first,
- an architecture needs its entity analysed first,
- anything that
uses a package needs that package analysed first, - a design that instantiates another entity needs that entity analysed first.
VHDL does not hunt around your files to satisfy these for you — you (or your script / Makefile) must present units in dependency order.
4. A small project, in dependency order
Consider a counter that uses a shared constants package, wrapped by a top, exercised by a testbench. The arrows below are "depends on / must be compiled after":
A compile script simply lists them in that order:
analyze const_pkg.vhd # 1 — package first (nothing depends earlier)
analyze counter.vhd # 2,3 — entity then its architecture
analyze top.vhd # 4 — instantiates counter (needs it analysed)
analyze tb.vhd # 5 — drives top
elaborate tb # build the design rooted at the testbench
simulate # run itlibrary ieee;
use ieee.std_logic_1164.all;
package const_pkg is
constant WIDTH : integer := 4;
end package const_pkg;library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.const_pkg.all; -- needs const_pkg already analysed
entity counter is
port (
clk : in std_logic;
q : out std_logic_vector(WIDTH-1 downto 0)
);
end entity counter;
architecture rtl of counter is -- needs the entity above
signal q_i : unsigned(WIDTH-1 downto 0) := (others => '0');
begin
process (clk)
begin
if rising_edge(clk) then
q_i <= q_i + 1;
end if;
end process;
q <= std_logic_vector(q_i);
end architecture rtl;5. Elaboration binds and assembles
Once the units are on the shelf, elaboration takes a top unit and builds the actual design tree. The key act is binding: for every entity in the hierarchy, the tool selects an architecture to use (by default the most recently analysed one, unless a configuration says otherwise). It also expands each instance, fixes every generic to a concrete value, and creates the signal objects the simulator will drive.
This is why an entity can compile perfectly yet fail at elaboration: analysis only checks the entity exists and is well-formed; binding it to an architecture happens later. If no architecture for that entity is in the library, analysis was happy but elaboration cannot assemble the design — and you get an unbound / unresolved binding error.
6. Compile-time vs elaboration-time errors
| You see… | Stage | Usual cause |
|---|---|---|
"identifier std_logic not declared" | analysis | missing use ieee.std_logic_1164.all; |
"unit const_pkg not found" / "cannot find" | analysis | package not analysed yet, or wrong library / order |
| type / width mismatch in an assignment | analysis | the expression and target types disagree |
"cannot find entity/architecture for counter" | elaboration | entity analysed but its architecture was not |
"instance u_counter is unbound" | elaboration | no matching component/entity binding |
| generic / port-map association error | elaboration | hierarchy wiring or generic value is inconsistent |
| assertion fired / wrong waveform | simulation | the design is wrong, not the build |
The pattern: "not declared / not found / type" → analysis. "cannot find unit / unbound / generic / port map" → elaboration. "it ran but misbehaved" → simulation.
7. What goes wrong when compile order is wrong
The same correct source files fail loudly if presented in the wrong order — the code is fine, the order is not:
- Architecture before entity.
analyze counter_archbefore the entity → "entitycounternot found." The architecture cannot be checked without its entity on the shelf. - Using a package before analysing it.
analyze counter.vhdbeforeconst_pkg.vhd→ "const_pkgnot found" at theuseline. Fix the order, not the code. - Missing package body. A package that declares a function but whose body was never analysed: the declarations compile, but elaboration fails to resolve the function — a stage-2 failure for a stage-1-looking file.
- Stale library. You edited
const_pkg.vhdbut only re-analysedcounter.vhd; the library still holds the old package. Symptoms look impossible until you re-analyse the dependency.
The debugging habit that follows: when you read "cannot find unit X", do not stare
at the line — ask "was X analysed into this library, before this unit, and is it
still current?" When you read "unbound" or "no architecture", ask "did the
architecture (or component binding) actually get compiled?" The error names its
stage; the stage names the fix.
8. Common mistakes & what to watch for
- Assuming the tool finds dependencies for you. It does not — order is your job (or your Makefile's).
- Reading every error as a syntax error. An elaboration error means the units are fine but the design will not assemble — a different fix entirely.
- Forgetting to re-analyse a changed dependency. Edit a package, re-analyse it and everything downstream, or the library stays stale.
- Ignoring which library a unit landed in.
workis default, but multi-library projects can analyse a unit into the wrong shelf and then "cannot find" it.
9. Summary & next step
VHDL reaches hardware in three ordered stages: analysis compiles each unit and shelves it in a library, elaboration binds entities to architectures and assembles a chosen top into a real design, and simulation/synthesis runs or maps it. Compile order is mandatory because analysis only succeeds when every referenced unit is already on the shelf — package before users, entity before architecture, dependency before dependent. Read an error by its stage and the cause is usually obvious: "not found / type" is analysis, "unbound / cannot bind" is elaboration, "misbehaved" is simulation.
With the build model in place, the foundations turn to the two faces of that final stage — simulation versus synthesis — and how the same source is read differently by each, which is where the rest of the track's RTL discipline begins.