Verilog Delay Control verstehen: Timing-Techniken und Best Practices
Es gibt zwei Arten von Timing-Kontrollen in Verilog: Delay- und Event-Ausdrücke. Die Delay-Kontrolle ist lediglich eine Möglichkeit, eine Verzögerung zwischen dem Zeitpunkt hinzuzufügen, zu dem der Simulator die Anweisung trifft, und dem Zeitpunkt, zu dem er sie tatsächlich ausführt. Der Event-Ausdruck erlaubt es, die Anweisung so lange zu verzögern, bis das Eintreten eines Simulationsereignisses eintritt, was eine Wertänderung auf einem Net oder einer Variable (implizites Ereignis) oder ein explizit benanntes Ereignis sein kann, das in einer anderen Prozedur ausgelöst wird.
Die Simulationszeit kann durch eine der folgenden Methoden vorangetrieben werden.
Gatter und Nets, die mit internen Delays modelliert wurden, treiben die Simulationszeit ebenfalls voran.
Delay Control
Wenn der Delay-Ausdruck zu einem unbekannten oder High-Impedance-Wert ausgewertet wird, wird er als Null-Delay interpretiert. Wenn er zu einem negativen Wert ausgewertet wird, wird er als 2-Komplement-vorzeichenlose Ganzzahl derselben Größe wie eine Time-Variable interpretiert.
`timescale 1ns/1ps
module tb;
reg [3:0] a, b;
initial begin
{a, b} <= 0;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#10; a <= $random;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#10 b <= $random;
$display ("T=%0t a=%0d b=%0d", $realtime, a, b);
#(a) $display ("T=%0t After a delay of a=%0d units", $realtime, a);
#(a+b) $display ("T=%0t After a delay of a=%0d + b=%0d = %0d units", $realtime, a, b, a+b);
#((a+b)*10ps) $display ("T=%0t After a delay of %0d * 10ps", $realtime, a+b);
#(b-a) $display ("T=%0t Expr evaluates to a negative delay", $realtime);
#('h10) $display ("T=%0t Delay in hex", $realtime);
a = 'hX; #(a) $display ("T=%0t Delay is unknown, taken as zero a=%h", $realtime, a);
a = 'hZ; #(a) $display ("T=%0t Delay is in high impedance, taken as zero a=%h", $realtime, a);
#1ps $display ("T=%0t Delay of 10ps", $realtime);
end
endmodule
Beachten Sie, dass die Präzision des Timescale in 1ps liegt und daher $realtime erforderlich ist, um den Präzisionswert für die Anweisung mit dem Delay-Ausdruck (a+b)*10ps anzuzeigen.
Simulationslog:
xcelium> run T=0 a=x b=x T=10000 a=0 b=0 T=20000 a=4 b=0 T=24000 After a delay of a=4 units T=29000 After a delay of a=4 + b=1 = 5 units T=29050 After a delay of 5 * 10ps T=42050 Expr evaluates to a negative delay T=58050 Delay in hex T=58050 Delay is unknown, taken as zero a=x T=58050 Delay is in high impedance, taken as zero a=z T=58051 Delay of 10ps xmsim: *W,RNQUIE: Simulation is complete.
Event Control
Wertänderungen auf Nets und Variablen können als Synchronisationsereignis verwendet werden, um die Ausführung anderer prozeduraler Anweisungen auszulösen, und sind ein implizites Ereignis. Das Ereignis kann auch auf der Änderungsrichtung basieren, wie z. B. in Richtung 0, was es zu einem negedge macht, und eine Änderung in Richtung 1 macht es zu einem posedge.
- Ein
negedgeliegt vor, wenn ein Übergang von 1 nach X, Z oder 0 und von X oder Z nach 0 stattfindet - Ein
posedgeliegt vor, wenn ein Übergang von 0 nach X, Z oder 1 und von X oder Z nach 1 stattfindet
Ein Übergang vom selben Zustand in denselben Zustand wird nicht als Edge betrachtet. Ein Edge-Ereignis wie posedge oder negedge kann nur am LSB eines Vektor-Signals oder einer Variable detektiert werden. Wenn ein Ausdruck zum selben Ergebnis ausgewertet wird, kann er nicht als Ereignis betrachtet werden.
module tb;
reg a, b;
initial begin
a <= 0;
#10 a <= 1;
#10 b <= 1;
#10 a <= 0;
#15 a <= 1;
end
// Einen weiteren prozeduralen Block starten, der auf ein Update von
// Signalen wartet, die im obigen prozeduralen Block gemacht wurden
initial begin
@(posedge a);
$display ("T=%0t Posedge of a detected for 0->1", $time);
@(posedge b);
$display ("T=%0t Posedge of b detected for X->1", $time);
end
initial begin
@(posedge (a + b))
$display ("T=%0t Posedge of a+b", $time);
@(a)
$display ("T=%0t Change in a found", $time);
end
endmodule
Simulationslog:
ncsim> run T=10 Posedge of a detected for 0->1 T=20 Posedge of b detected for X->1 T=30 Posedge of a+b T=45 Change in a found ncsim: *W,RNQUIE: Simulation is complete.
Benannte Events (Named Events)
Das Schlüsselwort event kann verwendet werden, um ein benanntes Event zu deklarieren, das explizit ausgelöst werden kann. Ein event kann keine Daten halten, hat keine Zeitdauer und kann zu jedem bestimmten Zeitpunkt eintreten. Ein benanntes Event wird durch den ->-Operator ausgelöst, indem er vor dem benannten Event-Handle präfixiert wird. Ein benanntes Event kann mit dem oben beschriebenen @-Operator erwartet werden.
module tb;
event a_event;
event b_event[5];
initial begin
#20 -> a_event;
#30; ->a_event;
#50 ->a_event;
#10 ->b_event[3];
end
always @ (a_event)
$display ("T=%0t [always] a_event is triggered", $time);
initial begin
#25; @(a_event) $display ("T=%0t [initial] a_event is triggered", $time);
#10 @(b_event[3]) $display ("T=%0t [initial] b_event is triggered", $time);
end
endmodule
Benannte Events können verwendet werden, um zwei oder mehr parallel laufende Prozesse zu synchronisieren. Zum Beispiel werden der always-Block und der zweite initial-Block durch a_event synchronisiert. Events können als Arrays deklariert werden, wie im Fall von b_event, das ein Array der Größe 5 ist und der Index 3 für Trigger- und Wartezwecke verwendet wird.
Simulationslog:
ncsim> run T=20 [always] a_event is triggered T=50 [always] a_event is triggered T=50 [initial] a_event is triggered T=100 [always] a_event is triggered T=110 [initial] b_event is triggered ncsim: *W,RNQUIE: Simulation is complete.
Event-OR-Operator
, kann ebenfalls anstelle des or-Operators verwendet werden.
module tb;
reg a, b;
initial begin
$monitor ("T=%0t a=%0d b=%0d", $time, a, b);
{a, b} <= 0;
#10 a <= 1;
#5 b <= 1;
#5 b <= 0;
end
// "or" zwischen Events verwenden
always @ (posedge a or posedge b)
$display ("T=%0t posedge of a or b found", $time);
// Komma zwischen Events verwenden
always @ (posedge a, negedge b)
$display ("T=%0t posedge of a or negedge of b found", $time);
always @ (a, b)
$display ("T=%0t Any change on a or b", $time);
endmodule
Simulationslog:
ncsim> run T=0 posedge of a or negedge of b found T=0 Any change on a or b T=0 a=0 b=0 T=10 posedge of a or b found T=10 posedge of a or negedge of b found T=10 Any change on a or b T=10 a=1 b=0 T=15 posedge of a or b found T=15 Any change on a or b T=15 a=1 b=1 T=20 posedge of a or negedge of b found T=20 Any change on a or b T=20 a=1 b=0 ncsim: *W,RNQUIE: Simulation is complete.
Implizite Event-Ausdrucksliste
module tb;
reg a, b, c, d;
reg x, y;
// Event-Ausdruck/Sensitivitätsliste wird von allen
// Signalen innerhalb von () nach @ Operator gebildet, in diesem Fall a, b, c oder d
always @ (a, b, c, d) begin
x = a | b;
y = c ^ d;
end
initial begin
$monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b x=%0b y=%0b", $time, a, b, c, d, x, y);
{a, b, c, d} <= 0;
#10 {a, b, c, d} <= $random;
#10 {a, b, c, d} <= $random;
#10 {a, b, c, d} <= $random;
end
endmodule
Simulationslog:
ncsim> run T=0 a=0 b=0 c=0 d=0 x=0 y=0 T=10 a=0 b=1 c=0 d=0 x=1 y=0 T=20 a=0 b=0 c=0 d=1 x=0 y=1 T=30 a=1 b=0 c=0 d=1 x=1 y=1 ncsim: *W,RNQUIE: Simulation is complete.
Wenn der Benutzer beschließt, ein neues Signal e hinzuzufügen und den Inversen in z zu erfassen, muss besondere Sorgfalt darauf verwendet werden, e auch in die Sensitivitätsliste aufzunehmen.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// "e" auch in Sensitivitätsliste hinzufügen
always @ (a, b, c, d, e) begin
x = a | b;
y = c ^ d;
z = ~e;
end
initial begin
$monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
{a, b, c, d, e} <= 0;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
end
endmodule
Simulationslog:
ncsim> run T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1 T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1 T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0 T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0 ncsim: *W,RNQUIE: Simulation is complete.
Verilog erlaubt nun, die Sensitivitätsliste durch * zu ersetzen, was eine praktische Abkürzung ist, die diese Probleme beseitigt, indem alle Nets und Variablen hinzugefügt werden, die von der Anweisung gelesen werden, wie unten gezeigt.
module tb;
reg a, b, c, d, e;
reg x, y, z;
// @* oder @(*) verwenden
always @ * begin
x = a | b;
y = c ^ d;
z = ~e;
end
initial begin
$monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
{a, b, c, d, e} <= 0;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
#10 {a, b, c, d, e} <= $random;
end
endmodule
Simulationslog:
ncsim> run T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1 T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1 T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0 T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0 ncsim: *W,RNQUIE: Simulation is complete.
Level-Sensitive Event Control
Die wait-Anweisung wertet eine Bedingung aus, und wenn sie falsch ist, bleiben die nachfolgenden prozeduralen Anweisungen blockiert, bis die Bedingung wahr wird.
module tb;
reg [3:0] ctr;
reg clk;
initial begin
{ctr, clk} <= 0;
wait (ctr);
$display ("T=%0t Counter reached non-zero value 0x%0h", $time, ctr);
wait (ctr == 4)
$display ("T=%0t Counter reached 0x%0h", $time, ctr);
$finish;
end
always #10 clk = ~clk;
always @ (posedge clk) ctr <= ctr + 1;
endmodule
Simulationslog:
ncsim> run T=10 Counter reached non-zero value 0x1 T=70 Counter reached 0x4 T=90 Counter reached 0x5 T=170 Counter reached 0x9 Simulation complete via $finish(1) at time 170 NS + 1
Verilog
- Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
- Verilog Concatenation meistern: Praktischer Leitfaden
- Verilog Assignments verstehen: Typen, Syntax und Best Practices
- Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
- Verilog Control Flow meistern: IF-ELSE, Case, Loops und mehr
- Verilog Functions meistern für effizientes RTL-Design
- Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
- Inter- und Intra-Assignment Delays in Verilog verstehen
- Gate Delays in Verilog verstehen: Genaues Timing für zuverlässige Digitaldesigns
- Verilog Clock Generator: Design & Implementierungsleitfaden