Menu

Verilog Initial-Block: Setup-Code beim Simulationsstart ausführen

Wie sich initial-Blöcke von always unterscheiden, warum sie nur in der Simulation existieren und die üblichen Muster - Stimulus, Waveform-Setup, Log-Header - für die sie benutzt werden.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Ein einmalig laufender prozeduraler Block

initial begin ... end ist das Geschwister von always. Der Unterschied: initial läuft genau einmal, beginnend bei Zeit 0, und endet dann. Es gibt keine Sensitivitätsliste und keine Schleife.

Er existiert aus einem Grund: Testbench-Setup. Initialwerte setzen, Stimulus anstoßen, Log-Dateien öffnen, $finish nach einer festen Simulationszeit aufrufen - alles, was einmalig zu Beginn eines Laufs in einer bekannten Reihenfolge passieren soll.

Geh durch, was der Simulator macht:

  1. Die Zeit rückt auf 0 vor.
  2. Der initial-Block startet. Der erste $display druckt.
  3. data wird auf 8'hA5 gesetzt.
  4. #10 rückt die simulierte Zeit um 10 Einheiten vor.
  5. Der zweite $display druckt.
  6. $finish beendet die Simulation.

Der Block lief einmal. Es gibt keinen zweiten Durchlauf.

Das klassische Testbench-Skelett

Fast jede Testbench, die du je schreibst, benutzt einen initial-Block wie diesen:

Schau dir die Form an - das ist die Testbench-Vorlage, zu der du jedes Mal greifst:

  1. VCD-Dump-Datei öffnen ($dumpfile, $dumpvars) - behandelt in Dumpfile und VCD.
  2. Initialwerte setzen und Reset halten für einige Zyklen.
  3. Stimulus treiben durch eine Folge von Änderungen, jeweils mit #-Verzögerungen.
  4. $finish, um die Simulation sauber zu beenden.

Das war's. Jeder Verilog-Test, den du siehst, einschließlich der in Hersteller-Beispielen, folgt diesem Muster.

Mehrere initial-Blöcke laufen parallel

Mehrere initial-Blöcke im selben Modul starten alle gleichzeitig zur Zeit 0. Jeder läuft seine eigenen Anweisungen in seiner eigenen Zeitscheibe:

Der Simulator startet beide Blöcke zur Zeit 0. Block Bs erstes $display läuft sofort. Block A wartet 5 Einheiten, bevor er druckt. Block B druckt erneut bei Zeit 2. Block As zweiter Druck feuert bei Zeit 15. Block Bs $finish killt die Simulation bei Zeit 22.

Setup, Taktgenerierung und Stimulus in separate initial-Blöcke aufzuteilen, ist üblicher Stil - jeder Block ist kurz und macht eine Sache.

Inline-Initialisierung in Deklarationen

Eine übliche kompakte Form: ein reg direkt bei der Deklaration initialisieren. Die meisten Simulatoren behandeln das wie ein initial:

reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;

Das ist äquivalent zu:

reg clk;
reg reset;
reg [7:0] count;

initial begin
    clk = 0;
    reset = 1;
    count = 0;
end

Die kompakte Form ist das, was du in Testbenches sehen wirst - sie setzt den Initialwert direkt neben die Deklaration, wo er leicht zu erkennen ist.

initial ist simulation-only

Synthese-Tools ignorieren initial-Blöcke. Echte Hardware hat keinen "Moment Null", an dem Setup-Code läuft - sie hat Power-On, Reset-Signale und die Konfiguration, die daraus folgt. Wenn ein Register in echter Hardware in einem bekannten Zustand starten soll, treibst du es mit einem Reset-Signal in einem always @(posedge clk):

always @(posedge clk) begin
    if (reset) state <= IDLE;
    else       state <= next_state;
end

Dieser if (reset)-Zweig ist das synthetisierbare Äquivalent zu initial state = IDLE. Reset ist die Antwort auf "wie initialisiere ich ein Register auf echter Hardware?".

Einige FPGA-Flows akzeptieren ein eingeschränktes initial für Register-Reset-Werte (Xilinx-Tools zum Beispiel), aber das ist eine herstellerspezifische Erweiterung. Verlass dich darauf nicht in portablem Code.

Dinge, die du innerhalb von initial siehst

Ein paar gängige Muster jenseits des Standard-Testbench-Skeletts:

Zeitverzögerte Beendigung

initial begin
    #1000 $finish;   // Sicherheitsnetz: Sim nach 1000 Zeiteinheiten killen
end

Ein separates initial, dessen einzige Aufgabe es ist, eine harte Obergrenze für die Simulation zu setzen. Selbst wenn dein Haupt-Stimulus-Block hängt, weil er auf ein Signal wartet, das nie kommt, feuert dieser Block und beendet den Lauf.

Waveform-Setup

initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, test);   // alles unterhalb des `test`-Scopes dumpen
end

Diese beiden Zeilen weisen den Simulator an, eine VCD-Datei mit jedem Signal im test-Scope zu schreiben. Ohne sie bekommst du keine Waveform.

Anfängliches Speicher-Image

reg [7:0] memory [0:255];

initial begin
    $readmemh("image.hex", memory);
end

$readmemh lädt eine hex-formatierte Datei in das Array. Wird in CPU-Testbenches verwendet, um den Instruktionsspeicher vorzuladen. Simulation-only.

Häufige Fehler

initial für synthetisierbare Logik verwenden. Wird nicht synthetisiert. Nutze stattdessen Reset-Signale.

$finish vergessen. Ohne läuft der Simulator weiter, bis etwas anderes ihn stoppt (eine Default-Zeitgrenze, manueller Abbruch usw.). Für einen schnellen Test ist das okay; für ein Regressions-Skript immer $finish.

#delay zwischen Stimulus-Zuweisungen vergessen. Schreibst du a = 0; b = 1; ohne Verzögerung, passieren beide zur selben Simulationszeit, und das DUT sieht sie vielleicht gleichzeitig statt als separate Ereignisse. Setze #1 oder mehr zwischen verschiedene Stimulus-Ereignisse.

Versuchen, einen wire aus initial zu treiben. Gleiche Regel wie bei always: nur reg ist ein zulässiges Ziel.

Wie es weitergeht

Du hast die beiden Sorten prozeduraler Blöcke gesehen. Das nächste Doc behandelt das am häufigsten missverstandene Thema in Anfänger-Verilog: Blocking vs Non-blocking-Zuweisung. Zu wissen, wann du = und wann <= nimmst, ist der Unterschied zwischen einem funktionierenden Flip-Flop und einem Race-Condition-Chaos.

Häufig gestellte Fragen

Was ist ein initial-Block in Verilog?

initial begin ... end ist ein prozeduraler Block, der genau einmal zu Beginn der Simulation, zur Zeit 0, läuft. Das ist der Standardort, an dem du Testbench-Zustand aufsetzt: Signale initialisieren, Log-Dateien öffnen, $dumpfile/$dumpvars aufrufen, Stimulus treiben und die Simulation mit $finish beenden. Mehrere initial-Blöcke können in einem Modul nebeneinander existieren; sie starten alle zur Zeit 0 parallel.

Was ist der Unterschied zwischen initial und always in Verilog?

initial läuft einmal zur Zeit 0 und endet dann. always läuft ewig - es hat eine Sensitivitätsliste und wacht jedes Mal auf, wenn sich die aufgeführten Signale ändern. initial wird fast ausschließlich in Testbenches verwendet. always ist das Arbeitspferd sowohl von Testbenches als auch von synthetisierbarem RTL.

Ist ein initial-Block synthetisierbar?

In klassischem Verilog nicht. Synthese-Tools ignorieren initial-Blöcke, weil echte Hardware keinen 'Zeitpunkt Null' hat, an dem Setup-Code läuft. Manche FPGA-Toolchains akzeptieren eine eingeschränkte Form, um Reset-Werte für Register zu setzen, aber der allgemeine Fall ist simulation-only. Lass initial-Blöcke in Testbenches; nutze Reset-Signale, um synthetisierbare Logik zu initialisieren.

Kann man mehrere initial-Blöcke in einem Verilog-Modul haben?

Ja. Jeder initial-Block startet zur Zeit 0 und läuft unabhängig bis zum Ende. Das Aufteilen des Setups in mehrere Blöcke ist ein übliches Testbench-Muster - ein Block für die Taktgenerierung, einer für den Stimulus, einer für den Waveform-Dump. Sie laufen ab Zeit 0 nebenläufig; der Simulator verschränkt ihre Anweisungen, während die Zeit fortschreitet.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S