Verilogs hierarchischen Referenz-Gültigkeitsbereich meistern: Namenskonflikte in Modulen und Blöcken vermeiden
Die meisten Programmiersprachen haben ein charakteristisches Merkmal namens Scope (Gültigkeitsbereich), das die Sichtbarkeit bestimmter Codeabschnitte für Variablen und Methoden definiert. Der Scope definiert einen Namespace, um Kollisionen zwischen verschiedenen Objektnamen innerhalb desselben Namespaces zu vermeiden.
Verilog definiert einen neuen Scope für Module, Funktionen, Tasks, benannte Blöcke und Generate-Blöcke.
module tb;
reg signal;
// Eine weitere Variable kann nicht mit
// einem bereits existierenden Namen im selben Scope deklariert werden
reg signal; // Fehler!
// Der Name 'signal' kann jedoch innerhalb
// einer Task wiederverwendet werden, da sie einem anderen Scope angehört.
task display();
reg signal = 1;
$display("signal = %0b", signal);
endtask
endmodule
Ein Identifier, wie ein Signalname, kann in einem gegebenen Scope nur für die Deklaration eines Typs von Element verwendet werden. Das bedeutet, dass zwei Variablen unterschiedlichen oder gleichen Datentyps nicht denselben Namen haben dürfen, oder eine Task und eine Variable desselben Namens, oder sogar ein Net und eine Gatter-Instanz mit demselben Namen im selben Scope.
Jeder Identifier in Verilog hat einen einzigartigen hierarchischen Pfadnamen, wobei jede Modulinstanz, Task, Funktion oder benannter begin end- oder fork join-Block eine neue Ebene oder einen neuen Scope definiert.
Hierarchische Referenz Beispiel
module tb;
// Zwei Instanzen verschiedener Module erstellen
A uA();
B uB();
// Einen benannten Block erstellen, der ein Signal deklariert
// und den Wert 10ns nach Simulationsstart ausgibt
initial begin : TB_INITIAL
reg signal;
#10 $display("signal=%0d", signal);
end
// Wir versuchen, auf andere Scopes mit hierarchischen
// Referenzen von diesem initial-Block aus zuzugreifen
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
Simulationslog:
xcelium> run Hello, this is A TB signal=0 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
Aufwärts-Namenreferenzierung (Upwards Name Referencing)
Ein niedrigeres Modul kann Elemente in einem Modul darüber in der Hierarchie referenzieren. Zum Beispiel wäre das Signal im TB_INITIAL-Block von der display-Task in A sichtbar.
module A;
task display();
$display("Hello, this is A");
// Aufwärts-Referenzierung, TB_INITIAL ist in diesem Modul sichtbar
#5 TB_INITIAL.signal = 1;
endtask
endmodule
Beachten Sie, dass das TB-Signal jetzt 1 statt 0 ist, wegen der durch Modul A vorgenommenen aufwärts gerichteten Referenzänderung am Signal.
Simulationslog:
xcelium> run Hello, this is A TB signal=1 signal_1=1 signal_2=0 xmsim: *W,RNQUIE: Simulation is complete.
Hier ist ein weiteres Beispiel mit mehreren verschachtelten Modulen, bei dem der Blattknoten direkt auf Mitglieder von oben liegenden Knoten durch aufwärts gerichtete hierarchische Referenz zugreifen kann.
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(); // oder A.display()
b.display(); // oder B.display()
c.display(); // oder C.display()
a.b.c.display();
end
endmodule
Simulationslog:
xcelium> run Hello, this is A Hello, this is B Hello, this is C Hello, this is C xmsim: *W,RNQUIE: Simulation is complete.
Wenn der Compiler b.display() findet:
- Er sucht im aktuellen Scope innerhalb von Modul D, ob
bdefiniert ist. Wenn es nicht existiert, sucht er nach dem Namen im umgebenden Scope und bewegt sich nach oben, bis der Modul-Scope erreicht ist. Wenn der Name immer noch nicht gefunden wird, geht er zum nächsten Schritt. - Er sucht im äußersten Scope des Elternmoduls und wenn er nicht gefunden wird, geht er weiter die Hierarchie hinauf.
Verilog
- Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
- Verilog Concatenation meistern: Praktischer Leitfaden
- Verilog Essentials: Kurzer Überblick
- Verilog Assignments verstehen: Typen, Syntax und Best Practices
- Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
- Verilog Functions meistern für effizientes RTL-Design
- Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
- Verilogs hierarchischen Referenz-Gültigkeitsbereich meistern: Namenskonflikte in Modulen und Blöcken vermeiden
- Verilog Clock Generator: Design & Implementierungsleitfaden
- Verilog Display Tasks meistern: Effizientes Debugging & Logging