Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
Blocking Assignments
Blocking-Zuweisungen verwenden das Symbol = und werden in einem prozeduralen Block nacheinander ausgeführt. Dies verhindert jedoch nicht die Ausführung von Anweisungen, die in einem parallelen Block laufen.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Beachten Sie, dass es zwei initial-Blöcke gibt, die parallel ausgeführt werden, wenn die Simulation startet. Anweisungen werden in jedem Block sequenziell ausgeführt und beide Blöcke enden zur Zeit 0ns. Genauer gesagt: Variable a wird zuerst zugewiesen, gefolgt von der Display-Anweisung, dann allen anderen Anweisungen. Dies ist in der Ausgabe sichtbar, wo Variable b und c im ersten $display noch 8'hxx sind. Dies liegt daran, dass die Zuweisungen an b und c noch nicht ausgeführt wurden, als der erste $display aufgerufen wird.
Simulationslog:
ncsim> run [0] a=0xda b=0xx c=0xx [0] a=0xda b=0xf1 c=0xx [0] a=0xda b=0xf1 c=0x30 [0] d=0xaa e=0xx [0] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
Im nächsten Beispiel fügen wir ein paar Delays in dieselbe Anweisungsmenge ein, um zu sehen, wie es sich verhält.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Simulationslog:
ncsim> run [0] a=0xda b=0xx c=0xx [5] d=0xaa e=0xx [10] a=0xda b=0xf1 c=0xx [10] a=0xda b=0xf1 c=0x30 [10] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
Non-Blocking Assignments
Non-blocking-Zuweisungen erlauben es, Zuweisungen zu planen, ohne die Ausführung nachfolgender Anweisungen zu blockieren, und werden durch das Symbol <= spezifiziert. Interessanterweise wird dasselbe Symbol als relationaler Operator in Ausdrücken und als Zuweisungsoperator im Kontext einer Non-Blocking-Zuweisung verwendet. Wenn wir das erste Beispiel von oben nehmen und alle =-Symbole durch den Non-Blocking-Zuweisungsoperator <= ersetzen, sehen wir Unterschiede in der Ausgabe.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Man sieht, dass alle $display-Anweisungen 'h'x ausgaben. Der Grund für dieses Verhalten liegt in der Art, wie Non-Blocking-Zuweisungen ausgeführt werden. Die RHS jeder Non-Blocking-Anweisung eines bestimmten Zeitschritts wird erfasst, und es wird zur nächsten Anweisung übergegangen. Der erfasste RHS-Wert wird der LHS-Variable erst am Ende des Zeitschritts zugewiesen.
Simulationslog:
ncsim> run [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] d=0xx e=0xx [0] d=0xx e=0xx ncsim: *W,RNQUIE: Simulation is complete.
Wenn wir den Ausführungsfluss des obigen Beispiels aufschlüsseln, erhalten wir etwa Folgendes:
|__ Spawn Block1: initial | |___ Zeit #0ns: a <= 8'DA, non-blocking, also RHS-Wert (8'hDA) merken und nächsten Schritt ausführen | |___ Zeit #0ns: $display() ist blocking, also diese Anweisung ausführen: Aber a hat noch keine neuen Werte, also a=8'hx | |___ Zeit #0ns: b <= 8'F1, non-blocking, also RHS-Wert (8'hF1) merken und nächsten Schritt ausführen | |___ Zeit #0ns: $display() ist blocking, ausführen. Aber b hat noch keine neuen Werte, also b=8'hx | |___ Zeit #0ns: c <= 8'30, non-blocking, also RHS-Wert (8'h30) merken und nächsten Schritt ausführen | |___ Zeit #0ns: $display() ist blocking, ausführen. Aber c hat noch keine neuen Werte, also c=8'hx | |___ Ende des Zeitschritts und initial-Block, erfasste Werte in Variablen a, b, c zuweisen |__ Spawn Block2: initial | |___ Zeit #0ns: d <= 8'AA, non-blocking, also RHS-Wert (8'hAA) merken und nächsten Schritt ausführen | |___ Zeit #0ns: $display() ist blocking, ausführen: Aber d hat noch keine neuen Werte, also d=8'hx | |___ Zeit #0ns: e <= 8'55, non-blocking, also RHS-Wert (8'h55) merken und nächsten Schritt ausführen | |___ Zeit #0ns: $display() ist blocking, ausführen. Aber e hat noch keine neuen Werte, also e=8'hx | |___ Ende des Zeitschritts und initial-Block, erfasste Werte in Variablen d und e zuweisen |__ Ende der Simulation bei #0ns
Als Nächstes verwenden wir das zweite Beispiel und ersetzen alle Blocking-Anweisungen durch Non-Blocking.
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
Auch hier sehen wir, dass die Ausgabe anders ist als zuvor.
Simulationslog:
ncsim> run [0] a=0xx b=0xx c=0xx [5] d=0xx e=0xx [10] a=0xda b=0xx c=0xx [10] a=0xda b=0xx c=0xx [10] d=0xaa e=0xx ncsim: *W,RNQUIE: Simulation is complete.
Wenn wir den Ausführungsfluss aufschlüsseln, erhalten wir etwa Folgendes:
|__ Spawn Block1 bei #0ns: initial | |___ Zeit #0ns: a <= 8'DA, non-blocking, also RHS-Wert (8'hDA) merken und nächsten Schritt ausführen | |___ Zeit #0ns: $display() ist blocking, ausführen: Aber a hat noch keine neuen Werte, also a=8'hx | |___ Ende des Zeitschritts: Erfassten Wert der Variable a zuweisen, a ist jetzt 8'hDA | |___ Warten bis Zeit um 10 Zeiteinheiten auf #10ns fortschreitet | | |___ Zeit #10ns: b <= 8'F1, non-blocking, also RHS-Wert (8'hF1) merken und nächsten Schritt ausführen | |___ Zeit #10ns: $display() ist blocking, ausführen. Aber b hat noch keine neuen Werte, also b=8'hx | |___ Zeit #10ns: c <= 8'30, non-blocking, also RHS-Wert (8'h30) merken und nächsten Schritt ausführen | |___ Zeit #10ns: $display() ist blocking, ausführen. Aber c hat noch keine neuen Werte, also c=8'hx | |___ Ende des Zeitschritts und initial-Block, erfasste Werte in Variablen b, c zuweisen |__ Spawn Block2 bei #0ns: initial | |___ Warten bis Zeit um 5 Zeiteinheiten auf #5ns fortschreitet | | |___ Zeit #5ns: d <= 8'AA, non-blocking, also RHS-Wert (8'hAA) merken und nächsten Schritt ausführen | |___ Zeit #5ns: $display() ist blocking, ausführen: Aber d hat noch keine neuen Werte, also d=8'hx | |___ Ende des Zeitschritts: Erfassten Wert der Variable d zuweisen, d ist jetzt 8'hAA | |___ Warten bis Zeit um 5 Zeiteinheiten auf #5ns fortschreitet | | |___ Zeit #10ns: e <= 8'55, non-blocking, also RHS-Wert (8'h55) merken und nächsten Schritt ausführen | |___ Zeit #10ns: $display() ist blocking, ausführen. Aber e hat noch keine neuen Werte, also e=8'hx | |___ Ende des Zeitschritts und initial-Block, erfasste Werte der Variable e zuweisen, e ist jetzt 8'h55 |__ Ende der Simulation bei #10ns
Verilog
- Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
- Verilog Concatenation meistern: Praktischer Leitfaden
- Verilog Functions meistern für effizientes RTL-Design
- Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
- Verilog Clock Generator: Design & Implementierungsleitfaden
- Verilog Math Functions meistern für effizientes Hardware-Design
- Verilog $timeformat meistern: Präzision und Anzeigesteuerung
- Verilog Timescales an jedem Scope verstehen und ausgeben
- Datei-E/A in Verilog meistern: Öffnen, Lesen, Schreiben und Schließen
- Verilog für Einsteiger: Hello-World-Beispiel