Konstanten mit einem Twist
Ein parameter ist eine Konstante - der Compiler wählt ihren Wert und backt ihn vor Beginn der Simulation in das Design ein. Der Twist: Wer auch immer das Modul instanziiert, kann den Wert ändern. Dieselbe Modul-Quelldatei kann Schaltungen unterschiedlicher Größen erzeugen, je nachdem, wie Aufrufer sie parametrieren.
So baut man wiederverwendbare IP. Ein FIFO-Modul mit WIDTH- und DEPTH-Parametern kann jedem Team in einer Firma dienen. Ein Counter mit einem WIDTH-Parameter ist dieselbe Quelle, ob er bis 16 oder bis 4 Milliarden zählt.
Grundlegende Parameter-Deklaration
Drei neue Syntaxteile:
- Parameter-Block:
#( parameter WIDTH = 8 )zwischen Modulnamen und Port-Liste. Der Default-Wert8ist das, was das Modul nimmt, wenn niemand überschreibt. - Nutzungsstelle:
output reg [WIDTH-1:0] count. Der Parameter ist nur eine Konstante - wir stecken ihn in den Bit-Bereich. Dieselbe Modul-Quelle produziert je nachWIDTHeinen 8-Bit- oder 16-Bit-Ausgang. - Override-Syntax:
counter #(.WIDTH(16)) dut16(...)bei der Instanziierung. Der#(...)-Block steht vor dem Instanznamen, nach dem Modulnamen. Nicht erwähnte Parameter behalten ihre Defaults.
localparam: Interne Konstanten
Es gibt Konstanten, die Aufrufer nicht überschreiben sollen. Zustandskodierungen sind der Klassiker:
localparam macht IDLE, RUNNING, DONE innerhalb des Moduls verfügbar, aber nicht von außen überschreibbar. Das ist die richtige Wahl - von außen zu ändern, welcher Zustandswert IDLE ist, wäre erschreckend.
Nutze parameter für Dinge, die Aufrufer konfigurieren sollen (Breiten, Tiefen, Verhaltensoptionen), und localparam für alles andere. Ein übliches Muster: localparams aus parameters ableiten:
parameter WIDTH = 32;
localparam WIDTH_M1 = WIDTH - 1; // einmal berechnet; nicht überschreibbar
Parametrierte Breiten in der Praxis
Die häufigste Nutzung von Parametern ist, Busbreiten flexibel zu machen. Hier ein Addierer, der bei jeder Breite funktioniert:
Eine Quelldatei, zwei Instanzen, zwei verschiedene Breiten, kein Copy-Paste. Das ist der ganze Lohn von Parametern.
Mehrere Parameter, benannter Override
Bei größeren Modulen hast du oft mehrere Parameter. Überschreibe sie namentlich in beliebiger Reihenfolge:
module fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16,
parameter AFULL = DEPTH - 2
)(
input wire clk,
input wire reset,
// ... Ports ...
);
// ...
endmodule
// An der Aufrufstelle:
fifo #(.WIDTH(32), .DEPTH(1024)) cmd_queue (.clk(clk), .reset(reset), ...);
Du musst nicht jeden Parameter überschreiben; nicht erwähnte behalten ihre Defaults. Der AFULL-Default im Beispiel wird aus DEPTH berechnet, was heißt: Überschreibst du DEPTH, folgt AFULL automatisch - genau die Art Abhängigkeit, die auch localparam abdecken würde, wenn du nicht wolltest, dass Aufrufer AFULL unabhängig überschreiben können.
Häufige Fehler
Das # im Override vergessen. counter (.WIDTH(16)) dut(...) sieht aus wie ein Override, aber Verilog liest (.WIDTH(16)) als Port-Verbindung. Du brauchst counter #(.WIDTH(16)) dut(...).
Parameter dort nutzen, wo sie nicht konstant genug sind. Parameter lösen sich zur Elaborationszeit auf, bevor irgendein Signal existiert. Du kannst nicht auf ein Laufzeitsignal parametrieren - hängt der Wert von dem ab, was ein Eingang zur Simulationszeit tut, ist es kein Parameter, sondern Logik.
parameter und localparam in Port-Listen verwechseln. Nur parameter gehört in den #(...)-Block oben. localparam lebt im Body. Der Compiler sagt dir, wenn du sie vertauschst.
Wie es weitergeht
Du kannst jetzt Module bauen, deren Größe zur Compile-Zeit festgelegt wird. Die nächsten beiden Docs behandeln die Regeln zum Schreiben literaler Konstanten (8'h1F, 4'b1010, 32'd100) und die x- und z-Werte, die auftauchen, wenn Signale ungetrieben sind oder umkämpft werden. Danach gehen wir zu Operatoren über - alles, was du mit den Vektoren und Parametern machen kannst, die du jetzt gesehen hast.
Häufig gestellte Fragen
Was ist ein parameter in Verilog?
Ein parameter ist eine Compile-Zeit-Konstante, die innerhalb eines Moduls deklariert wird. Er kann überall verwendet werden, wo eine Konstante zulässig ist - Busbreiten, Speichertiefen, Zustandskodierungen, Default-Werte. Entscheidend: Jede Instanz des Moduls kann den Parameter überschreiben, sodass dieselbe Quelldatei z.B. einen 8-Bit-Counter an einer Stelle und einen 32-Bit-Counter an einer anderen erzeugt.
Was ist der Unterschied zwischen parameter und localparam in Verilog?
parameter-Werte können von außen überschrieben werden, wenn das Modul instanziiert wird. localparam-Werte können nicht überschrieben werden - sie sind interne Konstanten. Nimm localparam für Zustandskodierungen und abgeleitete Konstanten, an denen der Modul-Autor Aufrufer nicht herumschrauben lassen will.
Wie überschreibt man einen Parameter in Verilog?
Wenn du das Modul instanziierst, füge einen Override-Block vor dem Instanznamen ein: counter #(.WIDTH(16)) my_inst (.clk(clk), .count(count));. Die .WIDTH(16)-Syntax setzt diesen spezifischen Parameter; nicht aufgelistete Parameter behalten ihre Defaults. Mehrere Overrides trennst du mit Kommas innerhalb des #(...).
Was ist ein parametrisiertes Modul?
Ein Modul, das eine oder mehrere parameter-Deklarationen offenlegt, die Aufrufer überschreiben können. Ein parametriertes FIFO könnte WIDTH- und DEPTH-Parameter haben, sodass dieselbe Quelle ein 32-Bit-x-16-tiefes FIFO an einer Stelle und ein 8-Bit-x-1024-tiefes FIFO an einer anderen produziert. So werden Bibliotheken wiederverwendbarer IP-Blöcke geschrieben.