Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> Verilog

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:

  1. Er sucht im aktuellen Scope innerhalb von Modul D, ob b definiert 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.
  2. Er sucht im äußersten Scope des Elternmoduls und wenn er nicht gefunden wird, geht er weiter die Hierarchie hinauf.

Verilog

  1. Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
  2. Verilog Concatenation meistern: Praktischer Leitfaden
  3. Verilog Essentials: Kurzer Überblick
  4. Verilog Assignments verstehen: Typen, Syntax und Best Practices
  5. Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
  6. Verilog Functions meistern für effizientes RTL-Design
  7. Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
  8. Verilogs hierarchischen Referenz-Gültigkeitsbereich meistern: Namenskonflikte in Modulen und Blöcken vermeiden
  9. Verilog Clock Generator: Design & Implementierungsleitfaden
  10. Verilog Display Tasks meistern: Effizientes Debugging & Logging