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

Tags:

Fpga

Vhdl