Skip to content

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

generic_clause.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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

two_instances.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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

one generic reg entity elaborated into an 8-bit and a 32-bit instancegeneric map 8generic map32entity reggeneric WIDTH sizes ports +registerinstance WIDTH=88-bit registerinstance WIDTH=3232-bit register12
A generic parameterizes an entity so one source produces many configurations. The reg entity has a WIDTH generic that sizes its ports and internal register. Two instantiations set WIDTH to 8 and 32 through their generic maps, elaborating into an 8-bit register and a 32-bit register from the same source — no duplication. Generics are resolved at elaboration (build time), so each instance becomes a fixed-width circuit; the generic is not runtime hardware. This is a parameterization structure — one definition, many sized instances — captured by a diagram rather than a waveform.

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.

generic_is_build_time.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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.