Skip to content

VHDL · Chapter 16.2 · Synthesis and RTL Implementation

The Synthesizable Subset of VHDL

VHDL is a large language built for both simulation and hardware, and only part of it maps to gates. The synthesizable subset is what a synthesizer can turn into a circuit: entities and architectures, clocked and combinational processes, if and case, bounded for loops, signals and variables, the standard types and operators, rising-edge detection, generics and generate, and bounded pure subprograms. Everything else is simulation-only and is either ignored or rejected by synthesis: timed waits and after-delays, file I/O and textio, access and protected types, real, initial values used as reset, and unbounded loops or recursion. Knowing the boundary is what keeps RTL buildable and keeps simulation and hardware in agreement. This lesson catalogs what is in the subset, what is not, and how to code within it.

Foundation14 min readVHDLSynthesisSynthesizable SubsetRTLSimulationCoding Style

1. Engineering intuition — write only what can be a circuit

A synthesizer can only build what corresponds to gates, flip-flops, and wires that exist before the circuit runs. So the test for any construct is simple: could this be a fixed piece of hardware? A clocked process — yes, flip-flops. A bounded loop — yes, it unrolls into fixed logic. But "wait 5 ns," "read a file," "allocate a node," "compute in real" have no gate equivalent; they only make sense to a simulator. The synthesizable subset is exactly the constructs that pass this test, and staying inside it is not a limitation so much as a discipline: write hardware-describing code, keep the simulation-only constructs in testbenches, and your RTL builds — and matches simulation.

2. Formal explanation — the subset, in and out

synthesizable_subset.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- SYNTHESIZABLE (maps to hardware):
--   entities / architectures / components / generics / generate
--   processes: clocked (rising_edge/falling_edge) and combinational (process(all))
--   if / elsif / else, case/when (with full coverage), BOUNDED for-loops
--   signals, variables; types: std_logic, std_logic_vector, unsigned, signed,
--     RANGED integer, enumerations, records, constrained/bounded arrays
--   operators: and/or/not/xor, +, -, *, comparisons, &, slices, aggregates
--   conversions (numeric_std), rising_edge/falling_edge
--   BOUNDED, PURE functions/procedures (inline to logic)
 
-- NOT SYNTHESIZABLE (simulation-only — ignored or rejected by synthesis):
--   wait for <time>, signal <= x after <time>          (timing/delays)
--   file / textio, report-as-IO                        (host I/O)
--   access types (pointers), protected types, 'new'    (dynamic memory)
--   real / math_real                                   (non-hardware types)
--   initial values used AS reset                       (HW powers up unknown)
--   unbounded loops / unbounded recursion              (no fixed hardware)
--   division/modulo by a NON-constant                  (tool-dependent / costly)

The synthesizable subset covers structural/behavioral RTL: entities, processes, control flow with bounded loops, the standard types and operators, edge functions, generics/generate, and bounded pure subprograms. Outside it: timed wait/after, file I/O, access/protected, real, initial-as-reset, unbounded loops/recursion, and non-constant division — all simulation-only.

3. Production usage — keeping RTL inside the subset

inside_the_subset.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- SYNTHESIZABLE RTL: clocked process, bounded loop, standard types/ops, reset (not initial value).
process (clk) begin
  if rising_edge(clk) then
    if rst = '1' then acc <= (others => '0');         -- RESET, not an initializer
    else
      acc <= (others => '0');
      for i in data'range loop                        -- BOUNDED loop (data'range fixed) → unrolls
        if data(i) = '1' then acc <= acc + 1; end if;
      end loop;
    end if;
  end if;
end process;
 
-- KEEP simulation-only constructs in the TESTBENCH, never in RTL:
--   wait for 10 ns;           -- TB timing
--   readline(f, l);           -- TB file I/O
--   shared variable sb : scoreboard_t;   -- TB protected type

What hardware does this become? The process above is fully synthesizable — a registered accumulator with a reset and an unrolled population-count loop — because every construct maps to fixed logic. The discipline is a clean split: RTL uses only subset constructs (reset every register, bounded loops, standard types); the testbench is where wait, textio, access/protected, and real live. This is the same boundary that prevents sim/synth mismatches (15.5): stay in the subset and the simulator and synthesizer read your code the same way.

4. Structural interpretation — the subset boundary

synthesizable constructs versus simulation-only constructs separated by the subset boundarymapstono mapSYNTHESIZABLEprocesses, if/case, boundedloops, std types/ops,generics/generate→ gates / FF / mem /arithfixed hardwareSIMULATION ONLYwait/after, file I/O,access/protected, real,unbounded loops→ testbenches /modelsno hardware12
The synthesizable subset is the boundary between code that maps to hardware and code that only simulates. On the synthesizable side: entities and processes, if/case, bounded for-loops, the standard types and operators, rising_edge, generics and generate, and bounded pure subprograms — all of which become gates, flip-flops, memory, or arithmetic. On the simulation-only side: timed waits and after-delays, file I/O and textio, access and protected types, real, initial-values-as-reset, and unbounded loops or recursion — which synthesis ignores or rejects. RTL must stay inside the subset; simulation-only constructs belong in testbenches. This is a construct-classification structure, captured by a diagram rather than a waveform.

5. Why this is structural, not timing

The synthesizable subset is a classification of constructs — which language features become hardware and which do not — so the boundary diagram above is the right picture, not a waveform. It is a static property of the code, not a behavior over time; the synthesizable constructs' run-time behavior was covered in their own modules (combinational, sequential, memory), and the simulation-only ones have meaning only in a testbench timeline. Knowing the subset is structural knowledge: where each feature falls, so your RTL stays buildable.

6. Debugging example — a simulation-only construct in RTL

Expected: the RTL synthesizes cleanly and matches simulation. Observed: synthesis rejects the code (unsupported construct), or silently ignores part of it so the hardware differs from simulation — an after delay that vanishes, an initial value that "resets" in sim but not in silicon, a wait for or real the tool drops. Root cause: a simulation-only construct leaked into RTL — timed wait/after, file I/O, access/protected, real, an initial value used as reset, or an unbounded loop — none of which is in the synthesizable subset. Fix: keep RTL strictly inside the subset — use a reset instead of an initializer, bounded loops, no timed delays/file I/O/real — and move all simulation-only constructs into the testbench. Engineering takeaway: if a construct has no gate equivalent (timed waits, file I/O, dynamic memory, real, initial-as-reset, unbounded loops), it does not belong in RTL — keep it in the testbench so synthesis and simulation agree.

reset_not_initial.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: relying on an initial value as reset + an 'after' delay — both sim-only, ignored by synth.
-- signal cnt : unsigned(7 downto 0) := (others=>'0');   q <= d after 2 ns;
-- FIX: explicit reset; no delay in RTL.
if rst='1' then cnt <= (others=>'0'); else cnt <= cnt + 1; end if;   q <= d;

7. Common mistakes & what to watch for

  • Initial value as reset. Hardware powers up unknown; reset registers explicitly rather than relying on signal initializers.
  • Timed wait/after in RTL. Delays are simulation-only and ignored by synthesis; sequence with clocks, not time.
  • Unbounded loops/recursion. Synthesizable loops must have static bounds (they unroll); unbounded forms are rejected.
  • File I/O, access/protected, real in RTL. All simulation-only; keep them in testbenches/models.
  • Non-constant division/modulo. Often unsupported or very costly; restructure (constant divisors, shifts, or dedicated dividers).

8. Engineering insight & continuity

The synthesizable subset is the set of constructs that become hardware — processes, if/case, bounded loops, the standard types and operators, rising_edge, generics/generate, and bounded pure subprograms — while timed waits, file I/O, dynamic memory, real, initial-as-reset, and unbounded loops are simulation-only. Coding inside the subset is what makes RTL buildable and keeps it matching simulation. Within the subset, the next question is what specific hardware each pattern produces — the inference rules: the next lesson, Inferring Flip-Flops, Latches, and Logic, covers exactly which RTL patterns become which primitives.