Die Simulation eines 3-Stufen-Schieberegisters mit Blocking Assignment Statement in Verilog führt zu unterschiedlichen Simulationsergebnissen über die Simulatoren hinweg:
Der RTL-Code lautet wie folgt:
`include "timescale.hv"
module shift_register
#(
parameter DATA_WIDTH = 3
)
(
input wire [(DATA_WIDTH-1):0] din ,
input wire clk ,
input wire rst_n ,
output wire [(DATA_WIDTH-1):0] out
);
reg [(DATA_WIDTH-1):0] q1,q2,q3;
assign out = q3;
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
q1 = {(DATA_WIDTH){1'b0}};
q2 = {(DATA_WIDTH){1'b0}};
q3 = {(DATA_WIDTH){1'b0}};
end else begin
q3 = q2;
q2 = q1;
q1 = din;
end
end
endmodule
Der Testbench-Code für die Simulation der obigen RTL lautet wie folgt:
`include "timescale.hv"
module tb_shift_register ();
localparam DATA_WIDTH = 4;
reg [(DATA_WIDTH-1):0] din ;
reg clk ;
reg rst_n ;
wire [(DATA_WIDTH-1):0] out ;
shift_register
#(
.DATA_WIDTH(DATA_WIDTH)
)
inst_shift_register
(
.din ( din ),
.clk ( clk ),
.rst_n ( rst_n ),
.out ( out )
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
din = {(DATA_WIDTH){1'b0}};
#20;
rst_n = 1'b1;
end
always #5 clk = ~clk;
always@(posedge clk) begin
if(rst_n) begin
din = 4'd15;
end
end
endmodule
Wenn die Funktionssimulation unter Verwendung des obigen TB für die RTL (oben erwähnt) in ModelSim durchgeführt wird, wird die folgende Wellenform beobachtet:
Die Simulation erscheint genauso wie die eines 3-Stufen-Schieberegisters mit nicht-blockierender Zuweisung, wo die Ausgabe nach 3 Taktzyklen erscheint und die Eingabe "din" erst beim nächsten Taktzyklus bei "q1" erscheint.
Wenn die Simulation für dieselbe RTL und TB mit Xilinx Vivado durchgeführt wird, wird die folgende Wellenform beobachtet:
Es wird beobachtet, dass die Ausgabe bei "q3" nach nur 2 Taktzyklen erscheint und die Eingabe "din" bei "q1" im selben Taktzyklus erscheint.
Da stellen sich mir 2 Fragen:
Hinweis: Ich bin mir der Tatsache bewusst, dass eine blockierende Zuweisungsanweisung zum Modellieren einer kombinatorischen Logik verwendet werden sollte, während eine nicht blockierende Zuweisungsanweisung zum Modellieren einer sequentiellen Logik verwendet werden sollte. Ich stelle diese Frage, da dies eine Interviewfrage war, die mir bezüglich der Implementierung eines 3-stufigen Schieberegisters mit Sperranweisung gestellt wurde.
Sie din
ändert sich gleichzeitig mit der Taktflanke. Dies ist eine Racebedingung und als solche ist das Verhalten des Simulators nicht definiert.
Dies liegt daran, dass Sie hier eine blockierende Zuweisung verwenden:
always@(posedge clk) begin
if(rst_n) begin
din = 4'd15; // << WRONG!
end
end
Ändern Sie das in eine nicht blockierende Zuweisung:
always@(posedge clk) begin
if(rst_n) begin
din <= 4'd15;
end
end
Aber ändern Sie auch alle Ihre anderen Zuweisungen auf nicht blockierend: Der einzige Grund, warum es im Moment funktioniert, ist versehentlich, weil Sie die Zuweisungen in die Reihenfolge q3..q1 gesetzt haben.
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
q1 <= {(DATA_WIDTH){1'b0}};
q2 <= {(DATA_WIDTH){1'b0}};
q3 <= {(DATA_WIDTH){1'b0}};
end else begin
q3 <= q2;
q2 <= q1;
q1 <= din;
end
end
RAMA KRISHNA MEDA