Menu

Verilog Timescale und Delays: Simulationszeit steuern

Wie die timescale -Direktive die Einheit von #delay` setzt, die Regeln für die Kombination verschiedener Einheiten über Dateien hinweg und wie Delays mit getakteter Logik interagieren.

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

Was "Zeit" in Verilog bedeutet

Verilog hat keinen eingebauten Begriff von Sekunden. Der Simulator rückt "Zeiteinheiten" - beliebige ganzzahlige Ticks - vor, und dein Code nutzt #N, um N davon zu warten. Die `timescale-Direktive bildet diese Ticks auf echte Zeit ab.

`timescale 1ns / 1ps

module test;
    initial begin
        $display("t = %0t", $time);   // 0
        #5;
        $display("t = %0t", $time);   // 5 ns
        #1.5;
        $display("t = %0t", $time);   // 6500 ps (oder 6,5 ns)
        $finish;
    end
endmodule

Zwei Parameter:

  • Einheit (erste Zahl): was #1 bedeutet. 1ns sagt, ein Tick ist eine Nanosekunde.
  • Präzision (zweite Zahl): wie fein die Simulation Zeit innerhalb dieser Einheit verfolgt. 1ps sagt, gebrochene Delays werden auf Pikosekunden gerundet.

Die Präzision darf nicht gröber als die Einheit sein (1ps / 1ns ist illegal). Übliche Wahl:

  • 1ns / 1ps - der De-facto-Standard. Alles in Nanosekunden, Sub-Nanosekunden-Präzision für Gatter-Delays.
  • 1ps / 1ps - wenn extrem schnelle Schaltungen modelliert werden oder jedes Delay sub-nanosekundig ist.
  • 1us / 1ns - für langsame, Embedded-Simulationen (UART-Byte-Zeiten, langsame Protokolle).

Wo es hingehört

`timescale ist eine Compiler-Direktive, kein Modul-Konstrukt. Sie steht ganz oben in einer Datei, vor jedem module:

`timescale 1ns / 1ps

module foo(...);
    // ...
endmodule

Sein Scope ist "ab hier in der Kompilierreihenfolge, bis zum nächsten `timescale oder Ende der Kompilierung". Das hat eine subtile Implikation: Eine Datei ohne explizites `timescale erbt das, was die vorher kompilierte Datei deklarierte, was von der Reihenfolge abhängt, in der der Compiler die Dateien liest. Vermeide die Überraschung: Schreibe `timescale an die Spitze jeder Datei.

Der Browser-Editor in diesen Docs setzt eine Default-Timescale für dich (üblicherweise 1ns / 1ps), deshalb funktionierten die Beispiele in früheren Docs ohne Deklaration. In einem echten Projekt wärst du explizit.

#delay in der Praxis

Nach `timescale 1ns / 1ps:

#5            // warte 5 ns
#100          // warte 100 ns
#1.5          // warte 1,5 ns (Präzision erlaubt es)
#0.001        // warte 1 ps (gerade noch über der Präzision)
#0            // Null-Delay; nützlich zum Ordnen von Events zur selben Zeit

Innerhalb eines initial- oder always-Blocks blockiert #N den prozeduralen Fluss für N Zeiteinheiten. Der Simulator pausiert diesen Block (andere nebenläufige Blöcke laufen weiter) und setzt nach der Verzögerung fort.

Du kannst einer Zuweisung ein Delay voranstellen:

#10 a = 1;        // warte 10 ns, dann zuweisen
data <= #2 new_value;   // plane die non-blocking-Zuweisung 2 ns von jetzt

Die erste Form ist Testbench-Standard. Die zweite (delayed non-blocking) wird in Gate-Level-Simulationen genutzt, um Propagations-Delays zu modellieren.

Einen Takt erzeugen

Das klassische Muster:

`timescale 1ns / 1ps

module test;
    reg clk = 0;
    always #5 clk = ~clk;
    // ...
endmodule

Mit Timescale 1ns / 1ps ist #5 5 ns. Der Takt schaltet alle 5 ns um, ergibt eine 10-ns-Periode - ein 100-MHz-Takt. Um die Frequenz zu ändern, ändere das Delay:

Halb-PeriodePeriodeFrequenz
#12 ns500 MHz
#2.55 ns200 MHz
#510 ns100 MHz
#1020 ns50 MHz
#2550 ns20 MHz
#50100 ns10 MHz

Willst du eine gebrochene Halb-Periode (#2.5), muss deine Präzision das unterstützen - 1ps-Präzision schafft alles bis 0,001 ns, also ist jede vernünftige Frequenz okay.

Timescales über Dateien hinweg mischen

Ein echtes Design hat viele Dateien, und sie können unterschiedliche Timescales deklarieren. Der Simulator nutzt jeweils die eigene Timescale der Datei, um die Delays in ihr zu interpretieren. Sagt module_a.v `timescale 1ns / 1ps und nutzt #5, sind das 5 ns. Sagt module_b.v `timescale 1us / 1ns und nutzt #5, sind das 5 µs.

Das ist meist unsichtbar, weil der Simulator unabhängig davon eine globale Zeitachse präsentiert - aber es heißt, dasselbe #N in zwei Dateien kann sehr unterschiedliche Dinge bedeuten. Der Fix: Eine Timescale wählen (Industriestandard ist 1ns / 1ps) und sie an die Spitze jeder Datei setzen. Nicht mischen.

Delays sind nicht synthetisierbar

Wichtig: #delay existiert nur für Simulation. Das Synthese-Tool liest:

// In synthetisierbarem RTL - FALSCH
always @(posedge clk) begin
    out <= #2 in;
end

… und ignoriert entweder das #2 (die meisten Tools) oder weist das Konstrukt ab (strengere Linter). Das Timing echter Hardware wird vom Takt und der Gatter-Propagation bestimmt - beides für den Quelltext unsichtbar.

Die Regel: # nur in Testbenches. Synthetisierbares RTL hat keine #-Delays. Findest du dich dabei, ein Delay in synthetisierbarem Code zu wollen, willst du eigentlich einen Counter, der am Takt herunterzählt - so "wartet" echte Hardware.

$time vs $realtime

Zwei Wege, die aktuelle Simulationszeit zu lesen:

  • $time liefert einen 64-Bit-Integer in der Einheit der aktuellen Timescale-Einheit.
  • $realtime liefert ein real in der Einheit der aktuellen Timescale-Einheit, aber mit voller Präzision.

Für Testbench-Logging reicht $time fast immer. Greife nur dann zu $realtime, wenn du in Print-Anweisungen Sub-Tick-Präzision brauchst.

Praktische Tipps

  • Deklariere `timescale immer oben in jeder Datei. 1ns / 1ps ist der sichere Default.
  • Nutze #delay nur in Testbenches. Behandle die Abwesenheit in synthetisierbarem Code als statische Regel.
  • Passe Taktperioden zur Zielfrequenz. Simulierst du ein 50-MHz-Design, nimm eine 20-ns-Periode - nicht passende Perioden können timing-empfindliche Bugs verdecken.
  • Für zyklenbasierten Stimulus nimm @(posedge clk) statt #. Das ist robust gegen Änderungen der Taktperiode.

Wie es weitergeht

Du hast jetzt jedes Doc dieser Verilog-Tutorials gesehen. Von den Grundlagen der Sprache (wire vs reg, Module, Operatoren) über prozedurale Blöcke und Kontrollfluss bis hin zu synchronem Design und Automaten - und schließlich das Testbench-Werkzeug, das beweist, dass alles funktioniert. Zeit, etwas zu bauen - der Playground neben diesen Docs ist derselbe Simulator, den wir die ganze Zeit benutzt haben, bereit für jedes Modul, das du skizzierst.

Häufig gestellte Fragen

Was bedeutet `timescale 1ns / 1ps in Verilog?

Es sagt dem Simulator: 'eine Zeiteinheit in dieser Datei ist eine Nanosekunde, und Zeit wird mit Pikosekunden-Präzision verfolgt.' Nach dieser Direktive wartet #5 5 ns, #1.5 wartet 1,5 ns (auf Pikosekunden gerundet), und $time wird in Nanosekunden ausgegeben. Die erste Zahl ist die Einheit; die zweite die Präzision.

Brauche ich `timescale in jeder Verilog-Datei?

Best Practice: ja. Der Scope der Direktive endet beim nächsten \timescaleoder am Ende der Kompilierung, sodass Dateien ohne sie das erben, was die zuletzt kompilierte Datei deklarierte. Das macht Timing nicht-deterministisch über Builds hinweg. Schreibe`timescale 1ns / 1ps` oben in jede Quelldatei - das ist die häufigste Konvention - und du wirst nie überrascht.

Was bedeutet #5 in Verilog?

#5 rückt die Simulationszeit um 5 Zeiteinheiten vor. Die Einheit kommt von der aktiven \timescale-Direktive. Mit `timescale 1ns / 1psist#55 Nanosekunden. Mit`timescale 1us / 1nsist#55 Mikrosekunden. Die Zahl kann gebrochen sein -#1.5` funktioniert, wenn deine Präzision feiner als die Einheit ist.

Ist #delay in Verilog synthetisierbar?

Nein. #delay beeinflusst nur die Simulation - das Synthese-Tool ignoriert oder weist es ab. Das Timing echter Hardware kommt vom Taktsignal und der Gatter-Propagationsverzögerung, nicht aus #-Anweisungen. Nutze # frei in Testbenches; schreib es nie in synthetisierbarem RTL.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S