Skip to content

VHDL · Chapter 10.6 · Packages and Reuse

Designing for Reuse

Reuse in VHDL rests on three mechanisms, and good design comes down to choosing the right one for each thing. Share common definitions like types, constants, and subprograms through packages. Parameterise per-instance values like widths, depths, and modes through generics. Encapsulate behaviour behind a clean entity interface so a block can be reused as a component. The skill is matching the mechanism to the need, asking whether a thing is a shared definition, a per-instance parameter, or a reusable block, and then organising packages into focused, clearly named, loosely coupled units. This closing lesson steps back from the mechanics to the methodology and turns the module's pieces into a coherent reuse strategy that scales from a single design to a whole library.

Foundation13 min readVHDLReusePackagesGenericsMethodologyDesign

1. Engineering intuition — three ways to not repeat yourself

Reuse is about writing a thing once and using it many times, and VHDL offers three distinct ways depending on what the thing is. If it is a definition that several units must agree on — a type, a constant, a helper function — share it in a package. If it is a value that varies per instance — a width, a FIFO depth, a mode — parameterise it with a generic. If it is a block of behaviour — an adder, a UART, a controller — encapsulate it behind an entity and instantiate it. Picking the right mechanism for each need is the whole skill; using the wrong one (e.g. a package constant where a generic belongs) leads to inflexible or duplicated code.

2. Formal explanation — match the mechanism to the need

three_mechanisms.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- SHARE a DEFINITION → package (types, constants, subprograms). Same for everyone.
package proj_pkg is
  subtype data_t is std_logic_vector(31 downto 0);
  type    cmd_t  is (NOP, READ, WRITE);
  function parity(v : std_logic_vector) return std_logic;
end package;
 
-- PARAMETERISE a per-INSTANCE VALUE → generic (Module 12). Different per instance.
entity fifo is
  generic ( DEPTH : positive := 16; WIDTH : positive := 8 );   -- set per instantiation
  port ( ... );
end entity;
 
-- ENCAPSULATE BEHAVIOUR → entity/component. Reuse the block by instantiation.
-- u_fifo : entity work.fifo generic map (DEPTH => 32, WIDTH => 16) port map (...);

The decision rule: package when the thing is the same everywhere (a shared type/constant/function); generic when it varies per instance (a width or depth set at instantiation); entity when it is a reusable block of behaviour. These compose — a reusable block (entity) uses shared definitions (package) and is configured per instance (generic).

3. Production usage — a small reusable library, organised

organised_reuse.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- A focused, minimally-coupled package organisation for a project:
--   types_pkg     : shared types/subtypes (data_t, addr_t, cmd_t, bus records)
--   config_pkg    : project-wide constants (widths, base addresses, depths)
--   util_pkg      : general helper functions (parity, gray<->binary, clog2)
--   <iface>_pkg   : per-interface types (e.g. amba_pkg) used by master/slave/monitor
-- Reusable BLOCKS (entities), each parameterised by generics, using the packages above:
--   fifo, sync_fifo, cdc_sync, counter, uart, ...
-- Rule: packages depend on few/none of each other; blocks depend on packages, not vice versa.

What hardware does this become? Nothing on its own — this is source organisation. But the structure is what makes a design scale: shared definitions in focused packages, per-instance values as generics, behaviour as parameterised entities, with dependencies pointing one way (blocks use packages, not the reverse). A new design reuses the same packages and blocks rather than re-deriving them, and changes propagate from one place.

4. Structural interpretation — the reuse strategy

three reuse mechanisms: packages share, generics parameterise, entities encapsulatepackages → SHAREtypes, constants,subprogramsgenerics →PARAMETERISEper-instance values (Module12)entities →ENCAPSULATEreusable blocks (Module 4)scalable design /libraryblocks use packages +generics12
The three reuse mechanisms and how they fit. Packages SHARE definitions (types, constants, subprograms) that everyone agrees on. Generics PARAMETERISE per-instance values (widths, depths, modes) so one entity serves many sizes. Entities ENCAPSULATE behaviour behind a clean interface for instantiation. They compose: a reusable block (entity) uses shared package definitions and is configured per instance with generics. Organise packages into focused, clearly-named units with dependencies pointing one way — blocks depend on packages, not the reverse — and the design scales from one project to a reusable library.

5. Why this is structural, not timing

Reuse is a source-organisation discipline — how you partition definitions, parameters, and behaviour across packages, generics, and entities — so it has no waveform; the structural map above is the right picture. Its payoff is at design time: less duplication, consistent definitions, one place to change each thing, and blocks that drop into new designs. The run-time behaviour is just that of the reused blocks themselves, which you have already seen.

6. Debugging example — the wrong mechanism (and tangled packages)

Expected: a flexible, maintainable design. Observed: a "reusable" block that cannot be resized without editing it, or a circular package dependency that will not compile, or definitions duplicated across packages. Root cause: a per-instance value (a width) was baked into a package constant instead of a generic, so it cannot vary per instance; or packages were made to depend on each other in a cycle; or the same type was declared in two packages. Fix: use a generic for per-instance values, a package for truly shared definitions, keep package dependencies acyclic and one-directional, and declare each shared thing once. Engineering takeaway: reuse problems usually mean the wrong mechanism — generic for per-instance, package for shared, entity for behaviour — or tangled package dependencies; match the mechanism and keep dependencies clean.

generic_not_constant_for_instance_value.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: width as a package constant → every instance is the same width.
-- constant WIDTH : integer := 8;   -- can't vary per instance
-- FIX: width as a GENERIC → each instance sets its own.
entity reg is generic ( WIDTH : positive := 8 ); port ( ... ); end entity;

7. Common mistakes & what to watch for

  • Per-instance values as package constants. Use generics for things that vary per instance; packages for truly shared definitions.
  • Circular package dependencies. Keep package dependencies acyclic and one-directional (blocks depend on packages).
  • Duplicating definitions across packages. Declare each shared thing once; import it where needed.
  • One giant catch-all package. Split into focused packages (types, config, util, per-interface) for clarity and dependency control.
  • Encapsulating too little or too much. An entity should be a coherent reusable block with a clean interface; match boundaries to reuse units.

8. Engineering insight & continuity

Designing for reuse is choosing the right mechanism for each thing — share definitions in packages, parameterise per-instance values with generics, encapsulate behaviour in entities — and organising them into focused, acyclically-dependent units. Get those choices right and a design stops being a pile of files and becomes a small library that scales. This completes Packages and Reuse: you can now share a design's numbers, types, and (next module) behaviour cleanly. The curriculum turns next to the third pillar of that strategy — shared behaviour — in Module 11: Functions and Procedures, the subprograms that package reusable computation, and then to Generics (Module 12) for parameterisation.