How to write scalable VHDL code
There is an alternative to passing generics through all levels of the hierarchy:
declare the relevant quantities in a package, and "use" that package in every unit that needs it.
You can do a little better than a constant data_width
.
package bus_types is
constant DATA_WIDTH: natural := 8;
subtype DATA_BUS is std_logic_vector(DATA_WIDTH - 1 downto 0);
end package bus_types;
Now, any unit that uses the bus_types package use work.bus_types.all;
can simply say
port
(
address : in ADDRESS_BUS,
data_in : in DATA_BUS,
data_out : out DATA_BUS
);
which is simpler and better documents the intent.
There are use cases for generics, and use cases for this. One is not universally better than the other.
Multiple memories, which may be different sizes, are best handled with a generic for memory size. Then the generic specialises the memory block for each usage.
But for a data bus width : the same width is likely to be used throughout the whole design. And then a package like this gets the job done with a lot less plumbing than a generic on every component ... especially if you find yourself passing generics through hierarchy levels, without using them, just to get to a lower level.
More on packages, may supply the additional info requested:
https://stackoverflow.com/questions/35234086/vhdl-standard-layout-syntax-for-header-file/35234901#35234901 https://stackoverflow.com/questions/31383481/vhdl-how-to-define-port-map-of-a-component-with-a-package-in-its-entity/31383565#31383565 https://stackoverflow.com/questions/16144135/avoid-duplicating-code-in-vhdl/16144861#16144861
And libraries https://stackoverflow.com/questions/13414682/how-to-to-create-include-files-in-vhdl/13415746#13415746 https://stackoverflow.com/questions/44222287/vhdl-library-doesnt-work/44226184#44226184
Declare data_width
as generic
in top module. Map this generic to corresponding generics in the submodules. For eg:
entity Top is
generic (data_width: integer := 32);
port (...);
end entity;
architecture Structure of Top is
component CompA
generic (data_width: integer := 8);
port (...);
end component;
begin
u1: CompA generic map (data_width => data_width)
port map (...);
u2: CompA generic map (data_width => data_width/2)
port map (...);
end Structure;
I can give default values to all generics in top modules and sub modules for individual synthesis/simulation. However it can be overridden while instantiating and generic mapping. For instance, here u1
's data_width
becomes 32, and u2
's data_width
becomes 16. Both were derived from Top
's data_width
.
The entity with a generic
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity MyGeneric is
generic
(
DATA_WIDTH: natural := 4 -- Default value of 4.
);
port
(
clock: in std_logic;
reset: in std_logic;
data : out std_logic_vector(DATA_WIDTH - 1 downto 0)
);
end entity;
architecture V1 of MyGeneric is
signal internal_data: std_logic_vector(DATA_WIDTH - 1 downto 0) := (others => '1');
begin
process(clock, reset)
begin
if reset then
data <= internal_data;
elsif rising_edge(clock) then
data <= std_logic_vector(to_unsigned(8, DATA_WIDTH));
end if;
end process;
end architecture;
Test bench
library ieee;
use ieee.std_logic_1164.all;
entity TestBench is
end entity;
architecture V1 of TestBench is
constant DATA_WIDTH: natural := 8;
signal clock: std_logic;
signal reset: std_logic;
signal data : std_logic_vector(DATA_WIDTH - 1 downto 0);
component MyGeneric is
generic
(
DATA_WIDTH: natural := 4 -- Default value of 4.
);
port
(
clock: in std_logic;
reset: in std_logic;
data : out std_logic_vector(DATA_WIDTH - 1 downto 0)
);
end component;
begin
MG: MyGeneric
generic map
(
DATA_WIDTH => DATA_WIDTH
)
port map
(
clock => clock,
reset => reset,
data => data
);
process
begin
clock <= '0';
reset <= '1';
wait for 10 ns;
clock <= '0';
reset <= '0';
wait for 10 ns;
clock <= '1';
wait for 10 ns;
clock <= '0';
wait for 10 ns;
wait;
end process;
end architecture;
Try it out on EDA Playground