Maîtriser la Portée de Référence Hiérarchique Verilog : Éviter les Collisions de Noms dans les Modules et Blocs
La plupart des langages de programmation ont une caractéristique appelée portée (scope) qui définit la visibilité de certaines sections de code aux variables et méthodes. La portée définit un espace de noms pour éviter les collisions entre différents noms d'objets dans le même espace de noms.
Verilog définit une nouvelle portée pour les modules, fonctions, tasks, blocs nommés et blocs generate.
module tb;
reg signal;
// Une autre variable ne peut pas être déclarée avec
// un nom déjà existant dans la même portée
reg signal; // Erreur !
// Cependant, le nom 'signal' peut être réutilisé à l'intérieur
// d'une task car elle appartient à une portée différente.
task display();
reg signal = 1;
$display("signal = %0b", signal);
endtask
endmodule
Un identifiant, comme un nom de signal, ne peut être utilisé pour déclarer qu'un seul type d'élément dans une portée donnée. Cela signifie que deux variables de types de données différents ou identiques ne peuvent pas avoir le même nom, ou une task et une variable du même nom, ou même un net et une instance de porte avec le même nom dans la même portée.
Chaque identifiant en Verilog a un nom de chemin hiérarchique unique, où chaque instance de module, task, function ou bloc nommé begin end ou fork join définit un nouveau niveau ou portée.
Exemple de Référence Hiérarchique
module tb;
// Créer deux instances de modules différents
A uA();
B uB();
// Créer un bloc nommé qui déclare un signal et
// affiche la valeur à 10ns depuis le début de la simulation
initial begin : TB_INITIAL
reg signal;
#10 $display("signal=%0d", signal);
end
// Nous allons essayer d'accéder à d'autres portées en utilisant des
// références hiérarchiques depuis ce bloc initial
initial begin
TB_INITIAL.signal = 0;
uA.display();
uB.B_INITIAL.B_INITIAL_BLOCK1.b_signal_1 = 1;
uB.B_INITIAL.B_INITIAL_BLOCK2.b_signal_2 = 0;
end
endmodule
module A;
task display();
$display("Hello, this is A");
endtask
endmodule
module B;
initial begin : B_INITIAL
#50;
begin : B_INITIAL_BLOCK1
reg b_signal_1;
#10 $display("signal_1=%0d", b_signal_1);
end
#50;
begin : B_INITIAL_BLOCK2
reg b_signal_2;
#10 $display("signal_2=%0d", b_signal_2);
end
end
endmodule
Journal de Simulation
xcelium> run Hello, this is A TB signal=0 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
Référence de Nom Vers le Haut
Un module de niveau inférieur peut référencer des éléments dans un module au-dessus dans la hiérarchie. Par exemple, signal dans le bloc TB_INITIAL serait visible depuis la task display dans A.
module A;
task display();
$display("Hello, this is A");
// Référence vers le haut, TB_INITIAL est visible dans ce module
#5 TB_INITIAL.signal = 1;
endtask
endmodule
Notez que le signal TB est maintenant 1 au lieu de 0, à cause du changement de référence vers le haut fait au signal par le module A.
Journal de Simulation
xcelium> run Hello, this is A TB signal=1 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
Voici un autre exemple avec plusieurs modules imbriqués et le nœud feuille peut accéder directement aux membres des nœuds du haut par référence hiérarchique vers le haut.
module tb;
A a();
function display();
$display("Hello, this is TB");
endfunction
endmodule
module A;
B b();
function display();
$display("Hello, this is A");
endfunction
endmodule
module B;
C c();
function display();
$display("Hello, this is B");
endfunction
endmodule
module C;
D d();
function display();
$display("Hello, this is C");
endfunction
endmodule
module D;
initial begin
a.display(); // ou A.display()
b.display(); // ou B.display()
c.display(); // ou C.display()
a.b.c.display();
end
endmodule
Journal de Simulation
xcelium> run Hello, this is A Hello, this is B Hello, this is C Hello, this is C xmsim: *W,RNQUIE: Simulation is complete.
Quand le compilateur trouve b.display(),
- Il regarde dans la portée courante dans le module D pour voir si b est défini. S'il n'existe pas, alors il cherche le nom dans la portée englobante et monte jusqu'à ce que la portée du module soit atteinte. Si le nom n'est toujours pas trouvé, il passe à l'étape suivante.
- Il regarde dans la portée la plus externe du module parent et s'il n'est pas trouvé, il continue de monter dans la hiérarchie.
Verilog
- Maîtriser Verilog : Tutoriel complet pour la conception de circuits numériques
- Maîtriser la concaténation Verilog : Guide pratique
- Verilog Essentiel : Aperçu concis
- Comprendre les affectations Verilog : Types, Syntaxe et Bonnes Pratiques
- Verilog Blocking vs Non-Blocking : Différences Clés Expliquées
- Maîtriser les Fonctions Verilog pour une Conception RTL Efficace
- Comprendre les Tasks Verilog : Fonctions vs Tasks, Syntaxe et Utilisation
- Maîtriser la Portée de Référence Hiérarchique Verilog : Éviter les Collisions de Noms dans les Modules et Blocs
- Générateur d'Horloge Verilog : Guide de Conception et d'Implémentation