Leveraging Constants and Generic Maps in VHDL for Flexible Module Design
Creating reusable modules is a cornerstone of efficient VHDL design, yet minor variations often demand duplicate code. Generics and generic maps solve this by allowing you to configure a module’s parameters at compile‑time, keeping your code DRY and maintainable.
Constants help avoid hard‑coding the same value repeatedly. They’re ideal for defining signal vector widths or other compile‑time parameters, and can even be mapped to generic constants. Unlike signals or variables, a constant’s value is immutable after elaboration, ensuring consistency across the design.
This article is part of the Basic VHDL Tutorials series.
In our previous tutorial we built a 4‑input multiplexer with an 8‑bit bus width. If you now need a similar MUX with a different width, copying and pasting the entire module isn’t the only option.
Instead, declare a constant that holds the desired bus width, then pass that constant to the module via a generic. The only place you need to change the width is the constant declaration in your testbench.
Below is the syntax for declaring a constant in VHDL:
constant <constant_name> : <type> := <value>;
Constants can appear alongside signals in the declarative part of a file, or with variables inside a process.
To expose a constant to a module, use the generic keyword in the entity declaration:
entity <entity_name> is
generic(
<entity_constant_name> : <type> [:= default_value];
...
);
port(
<entity_signal_name> : in|out|inout <type>;
...
);
end entity;
Instantiate the generic module elsewhere using a generic map and port map:
<label> : entity <library_name>.<entity_name>(<architecture_name>)
generic map(
<entity_constant_name> => <value_or_constant>, ...
)
port map(
<entity_signal_name> => <local_signal_name>, ...
);
Exercise
In this video tutorial we’ll walk through creating and instantiating a generic 4‑input MUX module in VHDL.
Testbench – Generic MUX
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T16_GenericMapTb is
end entity;
architecture sim of T16_GenericMapTb is
constant DataWidth : integer := 8;
signal Sig1 : signed(DataWidth-1 downto 0) := x"AA";
signal Sig2 : signed(DataWidth-1 downto 0) := x"BB";
signal Sig3 : signed(DataWidth-1 downto 0) := x"CC";
signal Sig4 : signed(DataWidth-1 downto 0) := x"DD";
signal Sel : signed(1 downto 0) := (others => '0');
signal Output : signed(DataWidth-1 downto 0);
begin
i_Mux1 : entity work.T16_GenericMux(rtl)
generic map(DataWidth => DataWidth)
port map(
Sel => Sel,
Sig1 => Sig1,
Sig2 => Sig2,
Sig3 => Sig3,
Sig4 => Sig4,
Output => Output
);
process is
begin
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= Sel + 1;
wait for 10 ns;
Sel <= "UU";
wait;
end process;
end architecture;
Generic MUX Module
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T16_GenericMux is
generic(DataWidth : integer);
port(
-- Inputs
Sig1 : in signed(DataWidth-1 downto 0);
Sig2 : in signed(DataWidth-1 downto 0);
Sig3 : in signed(DataWidth-1 downto 0);
Sig4 : in signed(DataWidth-1 downto 0);
Sel : in signed(1 downto 0);
-- Output
Output : out signed(DataWidth-1 downto 0)
);
end entity;
architecture rtl of T16_GenericMux is
begin
process(Sel, Sig1, Sig2, Sig3, Sig4) is
begin
case Sel is
when "00" => Output <= Sig1;
when "01" => Output <= Sig2;
when "10" => Output <= Sig3;
when "11" => Output <= Sig4;
when others => Output <= (others => 'X');
end case;
end process;
end architecture;
ModelSim waveform after running the simulation (zoomed to the timeline):

Analysis
By defining DataWidth once in the testbench, we can adapt the same MUX for any bus width without touching the module code. The waveform confirms identical functionality to the hard‑coded version.

Takeaway
- Constants eliminate repetitive hard‑coded values, boosting readability and reducing errors.
- Generics make modules highly configurable, enhancing reuse across a design.
Proceed to the next tutorial »
VHDL
- Using Impure Functions in VHDL: Enhancing FSM Readability and Maintainability
- Mastering VHDL Functions: A Practical Guide to Efficient Design
- Using Procedures in VHDL: Simplify Your Design with Reusable Code
- Mastering VHDL Port Map Instantiation: A Practical Guide
- Mastering the Case-When Statement in VHDL: Efficient Multiplexer Design
- Mastering Signed and Unsigned Types in VHDL: A Practical Guide
- Mastering VHDL Wait Statements: Wait On, Wait Until, and Combined Usage
- Mastering While Loops in VHDL: Dynamic Iteration Control
- Mastering For‑Loops in VHDL: A Practical Guide
- Mastering Loop and Exit Constructs in VHDL: A Practical Guide