VHDL · Chapter 12.1 · Generics
Generics — Parameterizing Entities
A package constant is one shared value for the whole design, while a generic is a parameter that each instance sets for itself. Declared in a generic clause before the ports, a generic is an elaboration-time constant, fixed when the design is built rather than a runtime signal, so it can size things the language must know statically such as vector widths, counter limits, memory depths, and mode selects. The payoff is reuse without duplication, since you can write one register entity with a width generic and instantiate it as 8 bits here and 32 bits there from the same source. This module opener introduces the generic clause and defaults, how generics differ from package constants and from ports, and the model of a generic as configuration fixed at elaboration into concrete hardware.
Foundation14 min readVHDLGenericsParameterizationReuseElaborationEntities
1. Engineering intuition — one design, many sizes
You rarely want a register that is only 8 bits, or a FIFO that is only 16 deep. You want a register and a
FIFO whose size you choose where you use them. A generic gives you exactly that: a knob on the entity that
each instantiation turns to its own value. The entity is written once in terms of the knob — WIDTH, DEPTH,
MODE — and every instance bakes in its chosen setting at build time. This is how a design becomes a reusable
component rather than a one-off: the same source produces an 8-bit and a 32-bit register, a 16- and a 1024-deep
memory, with no copy-paste and no divergence.
2. Formal explanation — the generic clause
library ieee; use ieee.std_logic_1164.all;
-- The generic(...) clause comes BEFORE port(...). Generics are compile-time constants.
entity reg is
generic ( WIDTH : positive := 8 ); -- a parameter, with a default
port ( clk : in std_logic;
d : in std_logic_vector(WIDTH-1 downto 0); -- ports sized BY the generic
q : out std_logic_vector(WIDTH-1 downto 0) );
end entity;
architecture rtl of reg is
begin
process (clk) begin
if rising_edge(clk) then
q <= d; -- WIDTH bits, fixed at elaboration
end if;
end process;
end architecture;A generic is declared in the generic (...) clause (before port), optionally with a default. It is an
elaboration-time constant: visible throughout the entity and architecture, usable to size ports and signals,
and fixed when the instance is built. It is not a runtime input — it cannot change while the circuit runs.
3. Production usage — two instances, two configurations
-- ONE entity, TWO instances with different WIDTH → two differently-sized registers from one source.
u_byte : entity work.reg
generic map ( WIDTH => 8 ) -- this instance is 8 bits
port map ( clk => clk, d => d8, q => q8 );
u_word : entity work.reg
generic map ( WIDTH => 32 ) -- this instance is 32 bits
port map ( clk => clk, d => d32, q => q32 );
-- Omitting generic map uses the default (WIDTH => 8 here).
u_dflt : entity work.reg port map ( clk => clk, d => d8, q => q8 );What hardware does this become? Two separate registers — an 8-bit flop bank and a 32-bit flop bank —
elaborated from the one reg entity. The generic is resolved at build time, so by the time gates exist each
instance is a fixed-width circuit; there is no runtime "width" hardware. This is the essence of generics: they
configure how much logic is generated, then disappear into concrete, sized hardware.
4. Structural interpretation — one entity, many instances
5. Why this is structural, not timing
A generic is configuration fixed at elaboration, not a signal that moves over time, so the parameterization structure above is the right picture rather than a waveform. Its run-time behaviour is simply that of the concrete sized circuit it produces: an 8-bit instance behaves like an 8-bit register, a 32-bit instance like a 32-bit one — ordinary behaviour you have already seen, just at the chosen size. The whole effect of the generic happens before simulation starts; nothing about it varies during operation.
6. Debugging example — treating a generic like a runtime input
Expected: a configurable, correctly-sized component. Observed: an attempt to change a generic while the
circuit runs (it cannot), or a width mismatch / unconnected generic at instantiation, or a generic with no value.
Root cause: confusing a generic (compile-time, per-instance configuration) with a port (runtime data),
or instantiating without supplying a generic that has no default. Fix: use a generic only for build-time
configuration (widths, depths, modes) and a port for runtime data; give generics a default or set every one in
the generic map. Engineering takeaway: generics are fixed at elaboration — for values that change while the
design runs use a port; for per-instance build-time configuration use a generic, and always supply or default it.
-- BUG: trying to drive a "width" as runtime data through a port.
-- port ( width : in integer; d : in std_logic_vector ... ) -- can't size a vector by a runtime port
-- FIX: WIDTH is a generic, fixed at build time; data flows through ports.
generic ( WIDTH : positive := 8 ); port ( d : in std_logic_vector(WIDTH-1 downto 0); ... );7. Common mistakes & what to watch for
- Confusing generics with ports. Generics are compile-time configuration (widths, modes); ports carry runtime data. Sizing needs a generic.
- No default and no value at instantiation. A generic without a default must be set in every
generic map, or elaboration fails. - Expecting a generic to change at runtime. It is fixed at elaboration; for runtime variation use a port and logic.
- Putting per-instance values in a package constant. A package constant is one value design-wide; use a generic for values that differ per instance.
- Unconstrained sizes without thought. Generic-sized ports/signals must resolve to concrete widths at elaboration; make sure every generic is determined.
8. Engineering insight & continuity
A generic turns an entity into a reusable, configurable component: a compile-time, per-instance constant declared before the ports, used to size and configure the design, and resolved at elaboration into concrete hardware. It is the difference between writing a 32-bit register and writing the register, instantiated at whatever width each use needs — distinct from package constants (design-wide) and ports (runtime data). Opening Module 12, the most important application comes next: Width-Generic Design — building blocks whose data-path width is a generic, the single most common and powerful use of parameterization in RTL.