Module in Modulen
Ein Verilog-Design ist ein Baum aus Modulen. Das Top-Level-Modul (deine Testbench oder das Top-Level-Wrapper eines Chips) instanziiert tiefere Module, die wiederum noch tiefere Module instanziieren, bis hinunter zu herstellerseitigen Gatter-Primitiven. Instanziierung ist die Syntax für dieses Verschachteln.
Die Form hast du schon gesehen - wir haben sie in Dein erstes Modul verwendet:
and_gate dut(.a(a), .b(b), .y(y));
Diese Zeile stanzt eine einzelne Instanz von and_gate aus, nennt sie dut und verbindet ihre Ports mit lokalen Signalen. Zerlegen wir die einzelnen Teile.
Die Form einer Instanziierung
module_name instance_name (port_connections);
module_namemuss zu einem Namen aus einermodule-Deklaration irgendwo in deinem Projekt passen. Verilog ist case-sensitive.instance_nameist ein Label, das du wählst - üblicherweise beschreibend für die Rolle, die diese Instanz spielt. Du wirst ihn in hierarchischen Pfaden und Waveform-Ansichten verwenden.port_connectionsverdrahten die Ports der Instanz mit lokalen Signalen. Dafür gibt es zwei Schreibweisen.
Benannte Port-Verbindungen (nimm diese)
Die benannte Form sieht so aus:
my_module instance_name(
.clk (clk),
.reset (reset_n),
.data_in(in_bus),
.data_out(out_bus),
.valid (out_valid)
);
Jedes .port(signal)-Paar sagt: "Verbinde den Port der Instanz mit Namen port mit dem lokalen Signal mit Namen signal." Die Reihenfolge ist egal. Wenn du der Modul-Deklaration einen neuen Port hinzufügst, brechen bestehende Instanziierungen nicht, solange du dem neuen Port einen Default gibst oder jede Stelle aktualisierst.
Zwei praktische Hinweise:
- Der Port-Name (links der Klammern) muss exakt mit der Modul-Deklaration übereinstimmen.
- Der Signalname (innerhalb der Klammern) ist lokal zu der Stelle, an der die Instanziierung steht - üblicherweise das Eltern-Modul.
Wenn ein Port unverbunden bleibt, lass die Innenseite leer: .optional_port(). Das Signal floatet (z) in der Instanz. Manche Synthese-Tools warnen; die meisten akzeptieren es.
Positionsbasierte Port-Verbindungen (vermeide diese)
Die knappere Form listet Signale in Port-Listen-Reihenfolge:
my_module instance_name(clk, reset_n, in_bus, out_bus, out_valid);
Das ist kürzer, aber fragil. Sortiere die Port-Liste des Moduls um (ein echtes Refactor, das vorkommt) und jede positionsbasierte Instanziierung verdrahtet sich leise falsch. Greife nicht zu positionsbasiert, außer die Port-Liste hat genau ein oder zwei Mitglieder und wird sich vermutlich nie ändern.
Wo sie noch akzeptabel ist: winzige Utility-Module, deren Port-Reihenfolge Teil der API ist. Ein Zwei-Eingang-Gatter ist positionsbasiert in Ordnung. Ein Memory-Controller mit 30 Ports ist eine Einladung zum Bug.
Ein vollständiges hierarchisches Beispiel
Das ist eine echte dreistufige Hierarchie: test → full_adder → zwei half_adder-Instanzen. Jede Instanz hat ihre eigene Kopie der Gatter in half_adder; das Synthese-Tool gibt pro Instanziierung eine Schaltung aus.
Mehrere Instanzen desselben Moduls
Wenn du dasselbe Modul mehrfach instanziierst, ist jede Instanz unabhängige Hardware. Sie teilen sich keinen Zustand. Sie teilen sich keine Gatter. Stell dir jede Instanz als frische Kopie vor, die aus dem Design ausgestanzt wird.
adder add0(.a(a0), .b(b0), .sum(s0));
adder add1(.a(a1), .b(b1), .sum(s1));
adder add2(.a(a2), .b(b2), .sum(s2));
adder add3(.a(a3), .b(b3), .sum(s3));
Das sind vier separate Addierer, die parallel laufen. Würde adder ein Register enthalten, hätte jede Instanz ihre eigene Kopie davon mit eigenem Zustand.
generate-Schleifen: Wiederholte Hardware ausstanzen
Vier Instanzen von Hand zu schreiben, ist okay. 64 zu schreiben, ist nervig. Der generate-Block lässt den Elaborator die Tipparbeit für dich machen:
Drei neue Syntaxteile:
genvar ideklariert eine Schleifenvariable, die ingeneratenutzbar ist. Sie ist kein Laufzeit-Signal - sie existiert nur zur Elaborationszeit.generate ... endgenerateumschließt die Schleife. Manche Tools akzeptieren generate-Schleifen ohne das explizitegenerate-Schlüsselwort, aber es zu schreiben macht die Absicht klar.begin : invert_loopbenennt den Generate-Scope. Das Label wird Teil des hierarchischen Namens jeder erzeugten Instanz (dut.invert_loop[0].u_inv,dut.invert_loop[1].u_invusw.).
Der Synthesizer rollt die Schleife auf und erzeugt WIDTH Kopien von bit_inverter. Jede Kopie ist unabhängige Hardware.
Parameter-Overrides bei der Instanziierung
Hat das Modul Parameter, kannst du sie mit #(.PARAM(value)) zwischen Modulname und Instanzname überschreiben:
counter #(.WIDTH(16)) c16 (.clk(clk), .count(out16));
counter #(.WIDTH(32)) c32 (.clk(clk), .count(out32));
Beide Instanzen nutzen denselben counter-Quelltext, haben aber unterschiedliche Breiten. Die Syntax haben wir in Parameter behandelt; sie fügt sich sauber in die Instanziierung ein.
Hierarchische Namen
Sobald du eine Hierarchie hast, hat jedes Signal einen hierarchischen Pfad:
test.dut.ha0.sum
Das liest sich als: im test-Modul, in der dut-Instanz, in der ha0-Instanz, das Signal mit Namen sum. Du wirst diese Pfade in Waveform-Viewern, Fehlermeldungen und gelegentlich in $display-Aufrufen sehen, die aus einer Testbench tief in ein Submodul hineinschauen:
$display("internal carry1 = %b", dut.carry1);
Solche hierarchischen Referenzen gelten nur für Testbenches und Debug - synthetisierbarer RTL greift nicht in andere Module hinein.
Häufige Fehler
Port-Namen passen nicht. .clk_in(clk) verbindet das lokale clk mit einem Port namens clk_in. Heißt der Port des Moduls eigentlich clk, wird dir der Parser das sagen (manche Tools klarer als andere).
Breiten-Mismatch an einem Port. Ein 4-Bit-Signal an einen 8-Bit-Port anzuschließen, erweitert still mit Nullen; umgekehrt schneidet es still ab. Die meisten Tools warnen; siehst du keine Warnungen, schau genauer.
Vergessenes # beim Parameter. counter (.WIDTH(8)) c(.clk(clk)) sieht aus wie ein Override, ist aber keiner - der Parser versucht, (.WIDTH(8)) als Port-Verbindung zu lesen und scheitert. Richtig: counter #(.WIDTH(8)) c(.clk(clk)).
Instanznamen wiederverwenden. Zwei Instanzen können im selben Scope nicht denselben Namen haben. Die Fehlermeldung ist meist deutlich; was dich erwischt, ist die Versuchung zu kopieren.
Wie es weitergeht
Du kannst jetzt Module zu einer echten Hierarchie verdrahten. Das nächste Doc rundet die strukturelle Seite von Verilog ab - Kontinuierliche Zuweisung - und geht tiefer auf die assign-Anweisung ein, die wir seit dem ersten Kapitel frei benutzt haben.
Häufig gestellte Fragen
Wie instanziiert man ein Modul in Verilog?
Du schreibst den Modulnamen, dann einen Instanznamen, dann eine geklammerte Liste von Port-Verbindungen: my_module instance_name(.port(signal), ...);. Der üblichste Stil verwendet benannte Verbindungen (.port(signal)), die unabhängig von der Reihenfolge nach Port-Namen verbinden. Der knappere positionsbasierte Stil (my_module instance(signal1, signal2)) hängt von der Reihenfolge der Port-Liste ab und ist wartungsgefährlich.
Was ist der Unterschied zwischen benannten und positionsbasierten Port-Verbindungen?
Positionsbasierte Verbindungen listen Signale in derselben Reihenfolge wie die Port-Liste des Moduls - das erste Signal geht an den ersten Port, das zweite an den zweiten usw. Benannte Verbindungen nutzen .port_name(signal_name) und matchen über den Namen. Benannt ist ausführlicher, aber immun gegen Port-Umsortierungen und dokumentiert sich an der Aufrufstelle selbst. Nimm benannte Verbindungen ab zwei oder drei Ports.
Kann man dasselbe Verilog-Modul mehrfach instanziieren?
Ja - genau darum geht es. Jede Instanz ist unabhängige Hardware mit eigenem Zustand. Wenn du ein adder-Modul hast, kannst du es 64-mal in einer SIMD-Einheit instanziieren, jede mit anderen Eingängen. Die generate-Schleife ist die übliche Syntax, wenn die Instanzen ähnlich und indiziert sind.
Was ist ein generate-Block in Verilog?
generate ... endgenerate ist ein Konstrukt zur Kompilierzeit, das wiederholte Hardware ausstanzt. Eine for-Schleife innerhalb von generate erzeugt N Instanzen dessen, was im Body steht. generate läuft zur Elaborationszeit, vor Start der Simulation - es ist keine Laufzeitschleife, es ist ein Codegenerator für den Synthesizer.