Un bloc procédural one-shot
initial begin ... end est le frère de always. La différence : initial s'exécute exactement une fois, démarre au temps 0, puis se termine. Pas de liste de sensibilité, pas de boucle.
Il existe pour une raison : le setup de testbench. Fixer les valeurs initiales, lancer le stimulus, ouvrir les fichiers de log, appeler $finish après un temps de simulation fixé - toutes les choses que tu veux qu'il se passe une fois au début d'une exécution, dans un ordre connu.
Pas à pas, ce que fait le simulateur :
- Le temps avance à 0.
- Le bloc
initialdémarre. Le premier$displayaffiche. dataest mis à8'hA5.#10avance le temps simulé de 10 unités.- Le second
$displayaffiche. $finishtermine la simulation.
Le bloc a tourné une fois. Pas de second passage.
Le squelette canonique de testbench
Presque chaque testbench que tu écriras utilise un bloc initial comme celui-ci :
Regarde la forme - c'est le template de testbench que tu retrouveras à chaque fois :
- Ouvrir le fichier de dump VCD (
$dumpfile,$dumpvars) - couvert dans Dumpfile et VCD. - Fixer les valeurs initiales et maintenir le reset pendant quelques cycles.
- Piloter le stimulus à travers une série de changements entrecoupés de délais
#. $finishpour terminer la simulation proprement.
Voilà. Chaque test Verilog que tu verras, y compris ceux dans les exemples constructeurs, suit ce motif.
Plusieurs blocs initial sont parallèles
Plusieurs blocs initial dans le même module démarrent tous au temps 0 simultanément. Chacun exécute ses propres instructions dans sa propre tranche de temps :
Le simulateur démarre les deux blocs au temps 0. Le premier $display du bloc B s'exécute immédiatement. Le bloc A attend 5 unités avant d'afficher. Le bloc B affiche à nouveau au temps 2. Le second print du bloc A se déclenche au temps 15. Le $finish du bloc B tue la simulation au temps 22.
Diviser setup, génération d'horloge et stimulus en plusieurs blocs initial est un style courant - chaque bloc est court et fait une chose.
Initialisation inline dans les déclarations
Une forme compacte courante : initialiser un reg directement au moment de la déclaration. La plupart des simulateurs traitent ça comme un initial :
reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;
C'est équivalent à :
reg clk;
reg reset;
reg [7:0] count;
initial begin
clk = 0;
reset = 1;
count = 0;
end
La forme compacte est ce que tu verras dans les testbenches - elle met la valeur initiale juste à côté de la déclaration où elle est facile à parcourir.
initial est simulation uniquement
Les outils de synthèse ignorent les blocs initial. Le vrai matériel n'a pas de « moment zéro » où le code de setup s'exécute - il a un power-on, des signaux reset, et la configuration qui vient de ces événements. Si tu as besoin qu'un registre démarre dans un état connu sur du vrai matériel, pilote-le avec un signal reset à l'intérieur d'un always @(posedge clk) :
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
Cette branche if (reset) est l'équivalent synthétisable de initial state = IDLE. Le reset est la réponse à « comment initialiser un registre sur du vrai matériel ? »
Certains flows FPGA acceptent un initial restreint pour les valeurs reset de registres (les outils Xilinx, par exemple), mais c'est une extension par constructeur. Ne compte pas dessus dans du code portable.
Choses que tu verras à l'intérieur de initial
Quelques motifs courants au-delà du squelette de testbench standard :
Terminaison avec délai
initial begin
#1000 $finish; // filet de sécurité : tue la sim après 1000 unités de temps
end
Un initial séparé dont le seul rôle est de mettre une borne supérieure dure à la simulation. Même si ton bloc de stimulus principal se bloque en attendant un signal qui ne vient jamais, ce bloc se déclenche et termine l'exécution.
Setup de waveform
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test); // dump tout sous la portée `test`
end
Ces deux lignes disent au simulateur d'écrire un fichier VCD avec chaque signal dans la portée test. Sans elles, pas de waveform.
Image mémoire initiale
reg [7:0] memory [0:255];
initial begin
$readmemh("image.hex", memory);
end
$readmemh charge un fichier au format hex dans le tableau. Utilisé dans les testbenches de CPU pour pré-charger la mémoire d'instructions. Simulation uniquement.
Erreurs courantes
Utiliser initial pour de la logique synthétisable. Ça ne se synthétisera pas. Utilise les signaux reset à la place.
Oublier $finish. Sans lui, le simulateur tourne jusqu'à ce que quelque chose d'autre l'arrête (une limite de temps par défaut, interruption manuelle, etc.). Pour un test rapide c'est OK ; pour un script de regression, mets toujours $finish.
Oublier le #delay entre les assignations de stimulus. Si tu écris a = 0; b = 1; sans délai, les deux arrivent au même temps de simulation et le DUT pourrait les voir simultanément plutôt que comme des événements séparés. Insère #1 ou plus entre les événements de stimulus distincts.
Essayer de piloter un wire depuis initial. Même règle que pour always : seul reg est une cible légale.
La suite
Tu as vu les deux saveurs de blocs procéduraux. Le prochain document couvre le sujet le plus confus du Verilog débutant : Assignation blocking vs non-blocking. Savoir quand utiliser = et quand utiliser <= est la différence entre un flip-flop qui marche et un bazar de race conditions.
Questions fréquentes
Qu'est-ce qu'un bloc initial en Verilog ?
initial begin ... end est un bloc procédural qui s'exécute exactement une fois au démarrage de la simulation, au temps 0. C'est l'endroit standard pour mettre en place l'état du testbench : initialiser des signaux, ouvrir des fichiers de log, appeler $dumpfile/$dumpvars, piloter le stimulus, et terminer la simulation avec $finish. Plusieurs blocs initial peuvent coexister dans un module ; ils démarrent tous au temps 0 en parallèle.
Quelle est la différence entre initial et always en Verilog ?
initial s'exécute une fois au temps 0 puis se termine. always se ré-exécute indéfiniment - il a une liste de sensibilité et se réveille quand les signaux listés changent. initial est utilisé presque exclusivement dans les testbenches. always est le pilier à la fois des testbenches et du RTL synthétisable.
Le bloc initial est-il synthétisable ?
Pas en Verilog classique. Les outils de synthèse ignorent les blocs initial parce que le vrai matériel n'a pas de moment « temps zéro » où le code de setup s'exécute. Certaines toolchains FPGA acceptent une forme restreinte pour fixer les valeurs reset des registres, mais le cas général est simulation uniquement. Garde les blocs initial dans les testbenches ; utilise les signaux reset pour initialiser la logique synthétisable.
Peut-on avoir plusieurs blocs initial dans un module Verilog ?
Oui. Chaque bloc initial démarre au temps 0 et s'exécute jusqu'à terminaison indépendamment. Diviser le setup en plusieurs blocs est un motif testbench courant - un bloc pour la génération d'horloge, un pour le stimulus, un pour le dump waveform. Ils tournent concurremment depuis le temps 0 ; le simulateur entrelace leurs instructions à mesure que le temps avance.