Verilog Blocking vs Non-Blocking : Différences Clés Expliquées
Assignations Bloquantes (Blocking)
Les instructions d'affectation bloquantes utilisent le symbole = et sont exécutées les unes après les autres dans un bloc procédural. Cependant, cela n'empêche pas l'exécution d'instructions qui s'exécutent dans un bloc parallèle.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Notez qu'il y a deux blocs initial qui s'exécutent en parallèle au démarrage de la simulation. Les instructions sont exécutées séquentiellement dans chaque bloc et les deux blocs se terminent à 0ns. Plus précisément, la variable a est affectée en premier, suivie de l'instruction display qui est ensuite suivie de toutes les autres instructions. Cela est visible dans la sortie où les variables b et c sont 8'hxx dans la première instruction display. C'est parce que les affectations des variables b et c n'ont pas encore été exécutées lorsque le premier $display est appelé.
Journal de Simulation
ncsim> run [0] a=0xda b=0xx c=0xx [0] a=0xda b=0xf1 c=0xx [0] a=0xda b=0xf1 c=0x30 [0] d=0xaa e=0xx [0] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
Dans l'exemple suivant, nous ajouterons quelques délais au même ensemble d'instructions pour voir comment cela se comporte.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Journal de Simulation
ncsim> run [0] a=0xda b=0xx c=0xx [5] d=0xaa e=0xx [10] a=0xda b=0xf1 c=0xx [10] a=0xda b=0xf1 c=0x30 [10] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
Assignations Non-Bloquantes (Non-blocking)
L'affectation non-bloquante permet de planifier des affectations sans bloquer l'exécution des instructions suivantes et est spécifiée par le symbole <=. Il est intéressant de noter que le même symbole est utilisé comme opérateur relationnel dans les expressions, et comme opérateur d'affectation dans le contexte d'une affectation non-bloquante. Si nous reprenons le premier exemple ci-dessus et remplaçons tous les symboles = par l'opérateur d'affectation non-bloquante <=, nous verrons une différence dans la sortie.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
On voit que toutes les instructions $display ont affiché 'h'x. La raison de ce comportement réside dans la façon dont les affectations non-bloquantes sont exécutées. Le RHS de chaque instruction non-bloquante d'un pas de temps particulier est capturé, puis on passe à l'instruction suivante. La valeur RHS capturée est affectée à la variable LHS seulement à la fin du pas de temps.
Journal de Simulation
ncsim> run [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] d=0xx e=0xx [0] d=0xx e=0xx ncsim: *W,RNQUIE: Simulation is complete.
Si nous décomposons le flux d'exécution de l'exemple ci-dessus, nous obtenons quelque chose comme ce qui est montré ci-dessous.
|__ Spawn Block1: initial | |___ Time #0ns : a <= 8'DA, est non-bloquant donc note la valeur du RHS (8'hDA) et exécute l'étape suivante | |___ Time #0ns : $display() est bloquant, donc exécute cette instruction : Mais a n'a pas reçu les nouvelles valeurs donc a=8'hx | |___ Time #0ns : b <= 8'F1, est non-bloquant donc note la valeur du RHS (8'hF1) et exécute l'étape suivante | |___ Time #0ns : $display() est bloquant, donc exécute cette instruction. Mais b n'a pas reçu les nouvelles valeurs donc b=8'hx | |___ Time #0ns : c <= 8'30, est non-bloquant donc note la valeur du RHS (8'h30) et exécute l'étape suivante | |___ Time #0ns : $display() est bloquant, donc exécute cette instruction. Mais c n'a pas reçu les nouvelles valeurs donc c=8'hx | |___ Fin du pas de temps et du bloc initial, affecte les valeurs capturées dans les variables a, b, c | |__ Spawn Block2: initial | |___ Time #0ns : d <= 8'AA, est non-bloquant donc note la valeur du RHS (8'hAA) et exécute l'étape suivante | |___ Time #0ns : $display() est bloquant, donc exécute cette instruction : Mais d n'a pas reçu les nouvelles valeurs donc d=8'hx | |___ Time #0ns : e <= 8'55, est non-bloquant donc note la valeur du RHS (8'h55) et exécute l'étape suivante | |___ Time #0ns : $display() est bloquant, donc exécute cette instruction. Mais e n'a pas reçu les nouvelles valeurs donc e=8'hx | |___ Fin du pas de temps et du bloc initial, affecte les valeurs capturées dans les variables d et e | |__ Fin de la simulation à #0ns
Ensuite, utilisons le second exemple et remplaçons toutes les instructions bloquantes par des non-bloquantes.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Une fois de plus, on peut voir que la sortie est différente de ce que nous avions obtenu auparavant.
Journal de Simulation
ncsim> run [0] a=0xx b=0xx c=0xx [5] d=0xx e=0xx [10] a=0xda b=0xx c=0xx [10] a=0xda b=0xx c=0xx [10] d=0xaa e=0xx ncsim: *W,RNQUIE: Simulation is complete.
Si nous décomposons le flux d'exécution, nous obtenons quelque chose comme ce qui est montré ci-dessous.
|__ Spawn Block1 à #0ns: initial | |___ Time #0ns : a <= 8'DA, est non-bloquant donc note la valeur du RHS (8'hDA) et exécute l'étape suivante | |___ Time #0ns : $display() est bloquant, donc exécute cette instruction : Mais a n'a pas reçu les nouvelles valeurs donc a=8'hx | |___ Fin du pas de temps : Affecte la valeur capturée à la variable a, et a est maintenant 8'hDA | |___ Attend que le temps avance de 10 unités de temps à #10ns | | |___ Time #10ns : b <= 8'F1, est non-bloquant donc note la valeur du RHS (8'hF1) et exécute l'étape suivante | |___ Time #10ns : $display() est bloquant, donc exécute cette instruction. Mais b n'a pas reçu les nouvelles valeurs donc b=8'hx | |___ Time #10ns : c <= 8'30, est non-bloquant donc note la valeur du RHS (8'h30) et exécute l'étape suivante | |___ Time #10ns : $display() est bloquant, donc exécute cette instruction. Mais c n'a pas reçu les nouvelles valeurs donc c=8'hx | |___ Fin du pas de temps et du bloc initial, affecte les valeurs capturées dans les variables b, c | |__ Spawn Block2 à #0ns: initial | |___ Attend que le temps avance de 5 unités de temps à #5ns | | |___ Time #5ns : d <= 8'AA, est non-bloquant donc note la valeur du RHS (8'hAA) et exécute l'étape suivante | |___ Time #5ns : $display() est bloquant, donc exécute cette instruction : Mais d n'a pas reçu les nouvelles valeurs donc d=8'hx | |___ Fin du pas de temps : Affecte la valeur capturée à la variable d, et d est maintenant 8'hAA | |___ Attend que le temps avance de 5 unités de temps à #5ns | | |___ Time #10ns : e <= 8'55, est non-bloquant donc note la valeur du RHS (8'h55) et exécute l'étape suivante | |___ Time #10ns : $display() est bloquant, donc exécute cette instruction. Mais e n'a pas reçu les nouvelles valeurs donc e=8'hx | |___ Fin du pas de temps et du bloc initial, affecte les valeurs capturées à la variable e, et e est maintenant 8'h55 | |__ Fin de la simulation à #10ns
Verilog
- Maîtriser Verilog : Tutoriel complet pour la conception de circuits numériques
- Maîtriser la concaténation Verilog : Guide pratique
- Maîtriser les Fonctions Verilog pour une Conception RTL Efficace
- Comprendre les Tasks Verilog : Fonctions vs Tasks, Syntaxe et Utilisation
- Générateur d'Horloge Verilog : Guide de Conception et d'Implémentation
- Maîtriser les Fonctions Mathématiques Verilog pour une Conception Matérielle Efficace
- Maîtriser Verilog $timeformat : Contrôle de Précision et d'Affichage
- Comprendre et Afficher les Timescales Verilog à Toute Portée
- Maîtriser les entrées/sorties de fichiers en Verilog : Ouvrir, Lire, Écrire et Fermer