VHDL coding tips and tricks: GENERIC's in VHDL - Construction of parametrized components

Wednesday, March 10, 2010

GENERIC's in VHDL - Construction of parametrized components

THIS BLOG POST WAS UPDATED ON 06-03-2024!


      Generics are used to add customizable properties to VHDL designs. Here are some situations where you might want to use generics.

  1. Customizable Properties: An example of this would be when you want to design a RAM(Random Access Memory) or a FIFO(First In First Out) whose data width and maximum number of elements can be changed without rewriting the code in multiple places.
  2. Configuration Flexibility: As a code base goes through different stages in development, lets say simulation, synthesis, board level testing etc.., the developer might want to use different configurations to make the process easier. For example, the clock frequency used for functional simulation might be different from what is available in the fpga board. Instead of making multiple versions of the code, generics can be used to add multiple options in a single code base. 
  3. Platform Portability: There are different FPGA vendors and devices out there, each with their unique features. To create an efficient design, the developer might need to use specific features available in fpga's. But that means one would need to write several versions of codes to target multiple fpga's. Using generics would solve such a problem.
  4. Easier Hierarchical Design: Generic's can be passed down from top module to sub modules. This means that by changing specifications at the top level, you can make the changes reflect in lower-level modules as well.
    In essence, generics in VHDL allows you to create designs that are adaptable and reusable.

Example: piso.vhd

--Parallel in serial out shift register
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

--The top module has two instantiations- a 8 bit PISO(parallel in serial out) register and a 4 bit PISO.
--Without the use of generics these two components need a seperate .vhd  file.
--But I have used "generic" keyword to solve this problem.
entity piso is
    generic ( WIDTH : integer := 8 );   --default value is 8
    port(clk : in std_logic;
	 load : in std_logic;
	 in1 : in std_logic_vector(WIDTH-1 downto 0);  -- note how the size of 'in1' is not fixed.
         out1 : out std_logic ); end piso; architecture Behavioral of piso is -- note how the size of 'temp' is not fixed. signal temp: std_logic_vector (WIDTH-1 downto 0) := (others => '0'); --initialize to zero
begin

process(clk)
begin
    if (rising_edge(clk)) then
        if (load = '0') then  -- load the register. 'load' signal is active HIGH and synchronous.
            temp <= in1;
            out1 <= '0';
        else
            --shift the register elements and output a single bit.
            out1 <= temp(WIDTH-1);
            temp(WIDTH-1 downto 1) <= temp(WIDTH-2 downto 0);
        end if;
    end if;
end process;

end Behavioral;

    In the VHDL code above, we've left the size of the PISO (Parallel-In, Serial-Out) register open-ended. This flexibility allows us to easily adjust the size of the PISO register by simply modifying the value of the generic variable WIDTH. Remarkably, no other alterations in the code are necessary. 

    Let's delve into the testbench code to witness how this adaptability is implemented in practice.

Testbench - How do you instantiate a component with generic variables:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

--Now the instantiation of this component in top module is shown below:
entity tb_piso is
end tb_piso;

architecture behavior of tb_piso is

component piso
    generic ( WIDTH : integer := 8 );   --default value is 7
    port(clk : in std_logic;  load : in std_logic; in1 : in std_logic_vector(WIDTH-1 downto 0); -- note how the size of 'in1' is not fixed.
out1 : out std_logic ); end component;
--signal declarations
signal in1 : std_logic_vector(7 downto 0):="10000110";
signal in2 : std_logic_vector(3 downto 0):="1001";
signal load1,load2,o1,o2 : std_logic :='0';
signal clk : std_logic := '0';

begin

--Note down the next two lines.
--This is how you pass generic parameters to the instantiated components.
--At compile time, the variable 'width' will be replaced everywhere by the values passed through here.
--Note that I used positional association for port mapping here. This is not recommended.
--Please use named association in your actual designs.
piso1 : piso generic map (WIDTH => 8) port map(clk,load1,in1,o1);
piso2 : piso generic map (WIDTH => 4) port map(clk,load2,in2,o2);
end behavior;

A very cool way to get things done, isn't it?

    Generics are perfectly synthesisable. During compilation, all generic variables are substituted with their assigned hard-coded values, ensuring smooth integration into the final design.


9 comments:

  1. This is an example of poor coding style. This would actually make a good interview question. in fact, it generally is.

    There are three things wrong with the code. Remember that synthesizable doesn't mean "working" or "good".

    The first mistake is that you don't define "out1" for the async "load" case. This infers extra logic, as now there is a priority logic with load and clk.

    The second mistake is that your async load sets "temp" to a non-constant value. why is this bad? well, now the logic needs to set the value on the output, independent of the clock. FURTHER, if load is deasserted before the next clock edge, this value must be latched.

    lastly, the sim won't match the code. in fact, the .syr report will give this as a warning. notice that if load is asserted, it should be async. but load isn't in the sensitivity list, so it will only get counted if there is a clk'event. XST will ignore this, but simulators generally won't.


    other, less important notes -- in almost all languages constants are ALL_CAPS. generics usually follow this standard by most coders.

    its advisable to never instantiate components by port-order. doing such should cause you physical pain. not only does it make the code unreadable, it leads to all types of errors.

    ReplyDelete
    Replies
    1. its not an async load, since load does not appear in the sensitivity list of the process. This means the process only executes on clk changes. In fact, it kind of is implying that load happens on both rising and falling edge of clk.

      Delete
    2. thank you. I updated the code and the post. I have been going through old posts and re writing them or removing them. thanks.

      Delete
  2. @Chris : Thanks for making a through analysis of most of my posts.It helped me learning a lot.
    Anyway I agree my bad coding style, but this post was mainly for learning the usage of "generics".I think I have succeeded in that.

    ReplyDelete
  3. Definately, though one large advantage to VHDL over Verilog2001 is with the richness of generics. for example, you can pass an array of integers, or an array of arrays of vectors, or records, or strings as a generic. While not commonly used, this flexibility can be helpful.

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. Hello,
    nice example,
    Just a remark about your parameter width, it must be 8 (or 4), as it is the number of bits that you have and not 7 (or 3). And when you use them as width-1 for the upper boundary.

    Best regards.

    RB

    ReplyDelete
  6. i tried a generic MUX........but when i give the width 7....it shows me

    Fatal: (vsim-3738) (vopt-1152) Index value 7 is out of index range 6 downto 0 of ieee.std_logic_1164.std_logic_vector

    i cant fix it

    ReplyDelete