Paramétrer un bloc VHDL avec des generics et générer des structures répétées ou optionnelles.
Idée générale
Un generic permet de paramétrer un bloc VHDL avant la synthèse. Un generate permet de créer une structure répétée ou conditionnelle pendant l'élaboration du design.
La différence importante :
un if dans un process décrit un choix qui existe dans le matériel ;
un if ... generate décide si un bloc existe ou non dans le matériel.
Generic de largeur
Le cas le plus courant est la largeur d'un bus.
library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity counter is generic ( g_WIDTH : positive := 8 ); port ( i_clk : in
std_logic
;
i_rst : in std_logic;
i_en : in std_logic;
o_cnt : out std_logic_vector(g_WIDTH - 1 downto 0)
);
end entity counter;
architecture rtl of counter is
signal r_cnt : unsigned(g_WIDTH - 1 downto 0);
begin
p_count : process(i_clk)
begin
if rising_edge(i_clk) then
if i_rst = '1' then
r_cnt <= (others => '0');
elsif i_en = '1' then
r_cnt <= r_cnt + 1;
end if;
end if;
end process p_count;
o_cnt <= std_logic_vector(r_cnt);
end architecture rtl;
Ici, le même code peut produire un compteur 8 bits, 16 bits ou 32 bits. Le matériel final dépend de la valeur donnée au generic.
Comme pour les ports, l'association nommée est préférable : elle évite les erreurs quand l'ordre change.
for ... generate
for ... generate instancie plusieurs copies d'un même bloc à l'élaboration.
entity reg_bank is generic ( g_WIDTH : positive := 8 ); port ( i_clk : in std_logic; i_d : in std_logic_vector(g_WIDTH - 1 downto 0); o_q : out std_logic_vector(g_WIDTH - 1 downto 0) );end entity reg_bank;architecture rtl of reg_bank isbegin g_bits : for i in 0 to g_WIDTH - 1 generate p_bit : process(i_clk) begin if rising_edge(i_clk) then o_q(i) <= i_d(i); end if; end process p_bit; end generate g_bits;end architecture rtl;
Ce n'est pas une boucle à l'exécution. Le synthétiseur crée réellement g_WIDTH structures.
if ... generate
if ... generate rend un bloc optionnel.
entity pipeline_stage is generic ( g_REGISTER_OUTPUT : boolean := true ); port ( i_clk : in std_logic; i_a : in unsigned(7 downto 0); i_b : in unsigned(7 downto 0); o_y : out unsigned(8 downto 0) );end entity pipeline_stage;architecture rtl of pipeline_stage is signal w_sum : unsigned(8 downto 0); signal r_y : unsigned(8 downto 0);begin w_sum <= resize(i_a, w_sum'length) + resize(i_b, w_sum'length); g_registered : if g_REGISTER_OUTPUT generate p_reg : process(i_clk) begin if rising_edge(i_clk) then r_y <= w_sum; end if; end process p_reg; o_y <= r_y; end generate g_registered; g_comb : if not g_REGISTER_OUTPUT generate o_y <= w_sum; end generate g_comb;end architecture rtl;
Si g_REGISTER_OUTPUT = false, le registre n'existe pas dans le netlist.
Bonnes pratiques
Donnez des valeurs par défaut raisonnables aux generic.
Utilisez positive ou natural quand un integer libre n'est pas nécessaire.
Préférez g_WIDTH à WIDTH pour distinguer les paramètres.
N'utilisez pas un generic pour une valeur qui doit changer pendant l'exécution.
Labellisez les blocs generate, surtout dans les designs structurés.
VHDL-2008 : generics plus avancés
VHDL-2008 permet aussi des generics de type, de sous-programme ou de package. C'est puissant, mais ce n'est pas nécessaire au début.
Exemple d'intention :
generic ( type t_data);
Ce style sert à créer des blocs très génériques, par exemple une FIFO capable de stocker différents types. Pour un parcours FPGA classique, les generics de largeur, de profondeur et d'activation de blocs sont déjà largement suffisants.