VHDL has fairly good support for generics. VHDL-2008 will actually provide several improvements as well. Generics are a good alternative to code generation, and work well for the vast majority of designs. Generics aren’t a complete replacement for code generation in all cases.
With XST, the allowed types for generics are very wide-ranging. For example, an array of integers can be used as a generic. A record type can also be used as a generic. In VHDL-2008, types themselves will be allowed — a port to a generic fifo could be defined as a signed/unsigned/std_logic/record at instantiation!
Generics To Define Widths
The canonical use for generics is to define the widths of the input/output data ports. eg:
entity my_fifo is generic( WIDTH : integer range 1 to 72 := 33 ); port( Dout : out std_logic_vector(WIDTH-1 downto 0); Rd : in std_logic; Din : in std_logic_vector(WIDTH-1 downto 0); Wr : in std_logic; Full : out std_logic; Empty : out std_logic; Clk : in std_logic; Rst : in std_logic ); end entity my_fifo;
Which defines an entity “my_fifo” that can be defined to take data 1-72b wide, with a default of 33b. This also provides an advantage over using unconstrained vectors, as the valid range is automatically checked. Also the range for Din/Dout are automatically the same. Further, ‘low is 0, and ‘ascending is false (direction is “downto”) — both are common assumptions.
Another common use of generics is to allow for features to be added to the core, or to tell the core something about the expected inputs/outputs.
An example of this might be a DSP core that can be optimized for speed by adding pipeline stages. In such a case, there might be a set of generics:
generic( USE_PL_MATH : boolean := true; PL_STAGES : integer range 1 to 3 );
The above actually could be reduced a bit if PL_STAGES is allowed to be 0-3, but it shows the general point.
The biggest issue with using generics for feature additions is that feature-creep becomes an issue. It moves more complexity into the specific HDL file and implies that a large number of feature combinations are possible. This isn’t always true. If asserts are not in place to give an error or warning for invalid/untested cases, the user might spend a good deal of time debugging external code instead of focusing on the bad interaction of generics.
A less orthodox use of generics is to warn the user that the code doesn’t have certain features. In a commercial environment, it is desirable to have reusable code. It is undesirable to spend a lot of time adding features that no one currently needs to this code. As a result, quirky behavior could occur in fringe cases that aren’t being used.
One example might be a DSP module that currently is written to expect data every clock cycle. A “valid” might be provided for the input/output, but are only used to mark the Nth output after the first valid input as a valid output.
generic( ALWAYS_VALID_IN : boolean; );
Adding this generic with no default will force the user to set the generic — thus affirming that the behavior is acceptable. An assert statement can be used to check the generic, and return a detailed error telling the user to write in the functionality.
-- within architecture body: assert ALWAYS_VALID_IN == TRUE report "This feature is not available." severity failure;
More Advanced Generics
In the first paragraphs, I alluded to the use of an array of integers as a generic. This is actually a very useful feature. It can allow the user to pass a set of coefficients to an FIR filter in one case. It can also be used to determine which fields of a packet contain header/footer information. For an arbiter, it can determine what the priority of each input is.
entity header_extract is generic( -- int array must be define, ideally in a package. HEADER_LOC : int_array ); port( -- data array must be defined as well Header_Out : out data_array; Header_Valid : out std_logic; Din : in data_type; Last : in std_logic; Wr : in std_logic; Clk : in std_logic_vector; Rst : in std_logic ); end entity header_extract;
In the above case, Header_Out is an unconstrained vector and would get its dimensions from HEADER_LOC’range. The generic also uses an unconstrained type. This prevents the tools from complaining about using generics to define other generics.
The default direction in VHDL is “to”. This means that “HEADER_LOC => (1,2,3,4)” would infer Header_Out as an array (0 to 3) instead of (3 downto 0). This becomes important when user indexes Header_Out.
There are many creative uses for generics in VHDL, but it is important to ensure that the tools will support all of the features you plan to use. XST doesn’t currently support VHDL-2008, and thus you cannot pass functions or types to a module as generics.