Paramètres, localparam et generate
Rendre un module réutilisable sans transformer ses réglages internes en entrées matérielles.
Un paramètre est fixé avant la synthèse
Un parameter règle la structure d'un module au moment de son instanciation. Il ne devient pas une entrée du circuit et ne peut pas changer pendant son fonctionnement.
module masked_register #(
parameter WIDTH = 8
) (
input wire i_clk,
input wire i_reset,
input wire i_load,
input wire [WIDTH-1:0] i_data,
input wire [WIDTH-1:0] i_mask,
output reg [WIDTH-1:0] o_data
);
always @(posedge i_clk) begin
if (i_reset)
o_data <= {WIDTH{1'b0}};
else if (i_load)
o_data <= (o_data & ~i_mask) | (i_data & i_mask);
end
endmoduleLe même code peut produire un registre de 8, 16 ou 32 bits. La répétition {WIDTH{1'b0}} suit automatiquement la largeur choisie.
Modifier un paramètre à l'instanciation
L'association par nom évite de dépendre de l'ordre des paramètres :
masked_register #(
.WIDTH(16)
) u_control_register (
.i_clk (i_clk),
.i_reset (i_reset),
.i_load (i_load),
.i_data (i_data),
.i_mask (i_mask),
.o_data (o_data)
);Une largeur utilisée dans les ports doit être strictement cohérente avec les signaux reliés à l'instance. Un avertissement de troncature n'est pas un détail à ignorer.
Garder les calculs internes avec localparam
Un localparam définit une constante interne que l'instance ne peut pas remplacer. Il convient aux valeurs dérivées et aux codages qui appartiennent au module.
module pulse_every #(
parameter PERIOD = 10
) (
input wire i_clk,
input wire i_reset,
output reg o_tick
);
localparam COUNTER_WIDTH = 32;
localparam LAST_COUNT = PERIOD - 1;
reg [COUNTER_WIDTH-1:0] count;
always @(posedge i_clk) begin
if (i_reset) begin
count <= 0;
o_tick <= 1'b0;
end else if (count == LAST_COUNT) begin
count <=
Ici, PERIOD fait partie de l'interface de configuration. LAST_COUNT est seulement la borne utilisée par l'implémentation. Dans un vrai projet, il faut aussi refuser ou traiter clairement PERIOD = 0.
Répéter une structure avec generate
Une boucle generate crée plusieurs instances pendant l'élaboration. Ce n'est pas une boucle exécutée à chaque cycle.
module parity_per_byte #(
parameter BYTE_COUNT = 4
) (
input wire [BYTE_COUNT*8-1:0] i_data,
output wire [BYTE_COUNT-1:0] o_parity
);
genvar lane;
generate
for (lane = 0; lane < BYTE_COUNT; lane = lane + 1) begin : g_parity
assign o_parity[lane] = ^i_data[lane*8 +: 8];
end
endgenerate
endmoduleChaque itération produit une logique de parité distincte. Le nom g_parity permet de retrouver facilement une voie dans la hiérarchie de simulation.
Choisir une structure à l'élaboration
Un if dans un bloc generate dépend d'une constante. Une seule branche existe dans le circuit obtenu.
module optional_output_register #(
parameter WIDTH = 8,
parameter REGISTER_OUTPUT = 1
) (
input wire i_clk,
input wire i_reset,
input wire [WIDTH-1:0] i_data,
output wire [WIDTH-1:0] o_data
);
generate
if (REGISTER_OUTPUT) begin : g_registered
reg [WIDTH-1:0] data_reg;
always @(posedge i_clk) begin
if (i_reset)
data_reg <= {WIDTH{1'b0}};
else
La version enregistrée ajoute une bascule et un cycle de latence. La version directe ne les ajoute pas. Ce choix doit donc apparaître dans la documentation du bloc.
À retenir
- Un
parameterconfigure une instance avant la synthèse. - Un
localparamprotège une constante propre à l'implémentation. - Une boucle
generateduplique du matériel. - Un
if generateconserve une seule architecture pour une configuration donnée. - Une valeur paramétrable doit rester valable pour toutes les configurations annoncées.
📝 Tester mes connaissances - Quiz du chapitre