Des constantes avec une astuce
Un parameter est une constante - le compilateur choisit sa valeur et la grave dans le design avant que la simulation ne démarre. L'astuce, c'est que celui qui instancie le module peut changer la valeur. Le même fichier source de module peut produire des circuits de tailles différentes selon la façon dont les appelants le paramètrent.
C'est comme ça que tu construis de l'IP réutilisable. Un module FIFO avec des paramètres WIDTH et DEPTH peut servir chaque équipe d'une entreprise. Un compteur avec un paramètre WIDTH est le même source qu'il compte jusqu'à 16 ou jusqu'à 4 milliards.
Déclaration de paramètre basique
Trois morceaux de syntaxe nouvelle :
- Bloc de paramètre :
#( parameter WIDTH = 8 )entre le nom du module et la liste de ports. La valeur par défaut8est ce que le module utilise quand personne ne surcharge. - Site d'utilisation :
output reg [WIDTH-1:0] count. Le paramètre est juste une constante - on le branche dans la plage de bits. Le même source produit une sortie 8 ou 16 bits selonWIDTH. - Syntaxe d'override :
counter #(.WIDTH(16)) dut16(...)au moment de l'instantiation. Le bloc#(...)vient avant le nom d'instance, après le nom du module. Les paramètres que tu ne mentionnes pas gardent leurs défauts.
localparam : constantes internes
Il y a des constantes que tu ne veux pas que les appelants surchargent. Les encodages d'état sont le cas classique :
localparam rend IDLE, RUNNING, DONE disponibles à l'intérieur du module mais non surchargeables depuis l'instantiation. C'est le bon choix - changer ce que la valeur d'état signifie pour IDLE depuis l'extérieur du module serait terrifiant.
Utilise parameter pour les choses que les appelants doivent configurer (largeurs, profondeurs, options comportementales) et localparam pour tout le reste. Un motif courant est de dériver des localparams à partir de parameters :
parameter WIDTH = 32;
localparam WIDTH_M1 = WIDTH - 1; // calculé une fois ; non surchargeable
Largeurs paramétrées en pratique
L'usage le plus courant des paramètres est de rendre flexibles les largeurs de bus. Voici un additionneur qui marche à n'importe quelle largeur :
Un fichier source, deux instances, deux largeurs différentes, pas de copier-coller. C'est tout le bénéfice des paramètres.
Plusieurs paramètres, override nommé
Pour les modules plus gros tu auras souvent plusieurs paramètres. Surcharge-les par nom dans n'importe quel ordre :
module fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16,
parameter AFULL = DEPTH - 2
)(
input wire clk,
input wire reset,
// ... ports ...
);
// ...
endmodule
// Au site d'appel :
fifo #(.WIDTH(32), .DEPTH(1024)) cmd_queue (.clk(clk), .reset(reset), ...);
Tu n'as pas à surcharger chaque paramètre ; ceux non mentionnés gardent leurs défauts. Le défaut AFULL dans l'exemple est calculé à partir de DEPTH, ce qui veut dire que si tu surcharges DEPTH, AFULL suit automatiquement - exactement le genre de dépendance que localparam gérerait aussi si tu ne voulais pas que les appelants puissent surcharger AFULL indépendamment.
Erreurs courantes
Oublier le # dans l'override. counter (.WIDTH(16)) dut(...) a l'air d'un override mais Verilog lit (.WIDTH(16)) comme une connexion de port. Tu as besoin de counter #(.WIDTH(16)) dut(...).
Utiliser des paramètres là où ils ne sont pas assez constants. Les paramètres se résolvent à l'élaboration, avant qu'aucun signal n'existe. Tu ne peux pas paramétrer sur un signal runtime - si la valeur dépend de ce qu'une entrée fait au moment de la simulation, ce n'est pas un paramètre, c'est de la logique.
Confondre parameter et localparam dans la liste de ports. Seul parameter va dans le bloc #(...) en haut. localparam vit à l'intérieur du corps. Le compilateur te le dira si tu les inverses.
La suite
Tu peux maintenant rendre la taille de tes modules réglable à la compilation. Les deux prochains documents couvrent les règles d'écriture des constantes littérales (8'h1F, 4'b1010, 32'd100) et les valeurs x et z qui apparaissent quand les signaux ne sont pas pilotés ou en contention. Ensuite on passe aux opérateurs - tout ce que tu peux faire avec les vecteurs et paramètres que tu as maintenant vus.
Questions fréquentes
Qu'est-ce qu'un parameter en Verilog ?
Un parameter est une constante à la compilation déclarée à l'intérieur d'un module. Il peut être utilisé partout où une constante est permise - largeurs de bus, profondeurs de mémoire, encodages d'état, valeurs par défaut. Crucial : chaque instance du module peut surcharger le paramètre, donc le même fichier source peut produire, par exemple, un compteur 8 bits à un endroit et un compteur 32 bits ailleurs.
Quelle est la différence entre parameter et localparam en Verilog ?
Les valeurs de parameter peuvent être surchargées depuis l'extérieur du module lors de son instantiation. Les valeurs localparam ne le peuvent pas - ce sont des constantes internes uniquement. Utilise localparam pour les encodages d'état et les constantes dérivées que l'auteur du module ne veut pas que les appelants touchent.
Comment surcharger un parameter en Verilog ?
Quand tu instancies le module, ajoute un bloc d'override de paramètre avant le nom d'instance : counter #(.WIDTH(16)) my_inst (.clk(clk), .count(count));. La syntaxe .WIDTH(16) fixe ce paramètre spécifique ; les paramètres que tu ne listes pas gardent leurs défauts. Plusieurs overrides sont séparés par des virgules à l'intérieur de #(...).
Qu'est-ce qu'un module paramétré ?
Un module qui expose une ou plusieurs déclarations parameter que les appelants peuvent surcharger. Une FIFO paramétrée pourrait avoir des paramètres WIDTH et DEPTH pour que le même source produise une FIFO 32 bits par 16 profond à un endroit et 8 bits par 1024 profond ailleurs. C'est comme ça que sont écrites les bibliothèques de blocs IP réutilisables.