Ich arbeite an einem Projekt, bei dem ich eine in Verilog codierte endliche Zustandsmaschine in ein ROM konvertieren muss. Dazu muss ich eine Speicherdatei für die ROM-Version des FSM erstellen, die den nächsten Zustand und die Ausgangswerte an Adressen speichert, die aus dem aktuellen Zustand und den Eingangswerten bestehen. Mein Gedanke zum Erstellen dieser Datei war, die betreffende FSM zu nehmen und die Eingabe "current_state" und die Ausgabe "next_state" hinzuzufügen und dann kleine Änderungen an der fsm vorzunehmen, sodass sie diese verwendet. Ich hatte jedoch mehrere Probleme mit diesem Testbench, die ich anscheinend nicht herausfinden kann, also dachte ich, ich würde ein kleineres FSM zusammenwerfen, um zu zeigen, was ich tue, und hoffentlich einen Einblick bekommen, was ich falsch mache. Ich glaube, mein Hauptproblem ist, dass das Taktsignal überhaupt nicht auszulösen scheint (zumindest wenn ich debugge, der Block, der bei der Posedge-Uhr aktiviert werden sollte, scheint nie ausgelöst zu werden). Das fragliche FSM, das ich zu modellieren versuche, hat so etwas wie 15 aktuelle Zustands- und Eingangsbits und 76 nächste Zustands- und Ausgangsbits, also anstatt einen der Codes zu verwenden, habe ich einfach dieses kleinere FSM zusammengeworfen (ich bekomme das gleiche Verhalten Das Problem ist also nicht zustandsmaschinenspezifisch und liegt wahrscheinlich eher an meiner schlechten Testbench).
Hier ist also die zu testende FSM-Einheit. Es ist ein grundlegendes FSM, das ich aus einem alten Lehrbuch entnommen habe:
module mealy_traditional(input wire clk,
input wire reset,
input wire a,
input wire b,
output wire y);
//Symbollic State Definition
localparam [1:0] state0 = 2'b00,
state1 = 2'b01,
state2 = 2'b10;
//signal declaration
reg [1:0] stateCurrent,
stateNext;
//state register
always @(posedge clk, posedge reset)
if(reset)
stateCurrent <= state0;
else
stateCurrent <= stateNext;
//_______________________________________________________Next State Logic
always @* begin
stateNext = stateCurrent
//______________________________________________________START_CASE
case(stateCurrent)
//___________________________________________________state0
state0:
if(a)
if(b)
stateNext = state2;
else
stateNext = state1;
else
stateNext = state0;
//___________________________________________________state1
state1:
if(a)
stateNext = state0;
else
stateNext = state1;
//___________________________________________________state2
state2: stateNext = state0;
//__________________________________________________default
default: stateNext = state0;
endcase
end
//Mealy Output Logic, function of both current state and current inputs
assign y = (stateCurrent == state0) & a & b;
endmodule
Und hier ist dieselbe FSM, die (glaube ich) modifiziert wurde, um den aktuellen Zustand als Eingang zu akzeptieren und den nächsten Zustandsausgang einem Drahtausgang zuzuweisen.
module mealy_traditional(input wire clk,
input wire reset,
input wire [1:0] state_current,
input wire a,
input wire b,
output wire [1:0] state_next,
output wire y);
//Symbollic State Definition
localparam [1:0] state0 = 2'b00,
state1 = 2'b01,
state2 = 2'b10;
//signal declaration
reg [1:0] stateCurrent,
stateNext;
//state register
always @(posedge clk, posedge reset)
if(reset)
stateCurrent <= state0;
else
stateCurrent <= state_current;
//_______________________________________________________Next State Logic
always @* begin
stateNext = stateCurrent;
//______________________________________________________START_CASE
case(stateCurrent)
//___________________________________________________state0
state0:
if(a)
if(b)
stateNext = state2;
else
stateNext = state1;
else
stateNext = state0;
//___________________________________________________state1
state1:
if(a)
stateNext = state0;
else
stateNext = state1;
//___________________________________________________state2
state2: stateNext = state0;
//__________________________________________________default
default: stateNext = state0;
endcase
end
//Mealy Output Logic, function of both current state and current inputs
assign y = (stateCurrent == state0) & a & b;
assign state_next = stateNext;
endmodule
Hier liegt also mit ziemlicher Sicherheit das Problem, die Testbench. Jetzt weiß ich, dass es angesichts der begrenzten Eingaben dieser FSM sehr einfach wäre, die fsm mit jeder Eingabekombination manuell zu treffen und die Ausgabe anzuzeigen, aber offensichtlich ist dies bei der anderen FSM, die fast 100 Bit IO hat, nicht wirklich eine Option . Daher habe ich mich entschieden, eine for-Schleife zu erstellen, um einfach alle möglichen Eingabebitkombinationen zu durchlaufen und dann die Ausgaben zu generieren, um sie in der Speicherdatei zu speichern. Das Problem, auf das ich stoße, ist, dass es nicht so aussieht, als ob der Takteingang überhaupt zyklisch läuft. Derzeit versuche ich den folgenden Testbench-Code
`timescale 1ns / 1ns
module mealy_traditional_tb;
reg clk = 1'b0;
reg reset = 1'b0;
reg [3:0] in;
wire [2:0] out;
integer i = 0;
integer maxValue = 16;
//Instantiation
mealy_traditional
uut(.clk(clk),
.reset(reset),
.state_current(in[3:2]),
.a(in[1]),
.b(in[0]),
.state_next(out[2:1]),
.y(out[0]));
initial begin
reset = 1;
#10
reset = 0;
#10
for(i = 0; i < maxValue; i = i + 1) begin
in = i;
clk = 1;
clk = 0;
clk = 1;
clk = 0;
$display("%b : %b", in, out);
end
$finish;
end
endmodule
Anfangs hatte ich versucht, einen eigenständigen Taktzyklus zu erstellen, indem ich eine verwendete
always #10 clk = ~clk;
Und dann Verzögerungen in die for-Schleife einfügen, was Verilog NICHT mochte (würde nicht einmal kompilieren, warf Syntaxfehler).
So wie es jetzt aussieht, wird dieser Testbench-Code kompiliert und der Simulator gestartet, aber es ist ziemlich klar, dass auf den Else-Zweig der Statusregisteraktualisierung in der FSM nicht zugegriffen wird, da sie niemals ausgelöst werden, wenn ich dort Haltepunkte setze Die Ausgabe der Zustandsmaschine scheint nur der Grundzustand von einem System-Reset zu sein. (folgendermaßen)
# 0000 : 00x
# 0001 : 00x
# 0010 : 00x
# 0011 : 00x
# 0100 : 00x
# 0101 : 00x
# 0110 : 00x
# 0111 : 00x
# 1000 : 00x
# 1001 : 00x
# 1010 : 00x
# 1011 : 00x
# 1100 : 00x
# 1101 : 00x
# 1110 : 00x
# 1111 : 00x
Ich hätte gedacht, dass das manuelle Erzwingen des Taktzyklus in der for-Schleife dazu führen würde, dass die endliche Zustandsmaschine aktualisiert wird, aber anscheinend nicht. Gibt es eine Möglichkeit für mich sicherzustellen, dass der Taktzyklus auf diese Weise ausgelöst wird?
Ja, das Problem liegt in Ihrem Testbench-Code. Es ist immer eine gute Idee, die Simulationszeit anzuzeigen, wenn Sie $display
Werte angeben, da dies beim Debuggen Ihres Problems hilft. Dies zeigt, dass Ihre gesamte Ausgabe gleichzeitig erfolgt (20 ns):
20 0000 : 00x
20 0001 : 00x
20 0010 : 00x
20 0011 : 00x
20 0100 : 00x
20 0101 : 00x
20 0110 : 00x
20 0111 : 00x
20 1000 : 00x
20 1001 : 00x
20 1010 : 00x
20 1011 : 00x
20 1100 : 00x
20 1101 : 00x
20 1110 : 00x
20 1111 : 00x
Die erste Spalte ist die Simulationszeit. Das Problem ist, dass innerhalb der Schleife keine Zeit vergeht for
. #
Eine Möglichkeit, dies zu beheben, besteht darin , Verzögerungen in der Schleife wie folgt hinzuzufügen :
initial begin
reset = 1;
#10
reset = 0;
#10
for(i = 0; i < maxValue; i = i + 1) begin
in = i;
#5 clk = 1;
#5 clk = 0;
#5 clk = 1;
#5 clk = 0;
$display($time,, "%b : %b", in, out);
end
$finish;
end
Das druckt:
40 0000 : 000
60 0001 : 000
80 0010 : 010
100 0011 : 101
120 0100 : 010
140 0101 : 010
160 0110 : 000
180 0111 : 000
200 1000 : 000
220 1001 : 000
240 1010 : 000
260 1011 : 000
280 1100 : 000
300 1101 : 000
320 1110 : 000
340 1111 : 000
out
Wie Sie sehen können, ändern sich die Werte der Simulationszeit und und out
haben keine Unbekannten mehr ( x
).
Hier ist eine andere Möglichkeit, den Testbench-Code zu schreiben. Es verwendet den traditionelleren always
Block für die Uhr, die Sie zum Laufen bringen wollten. Es stellt auch sicher, dass der Eingang mit der Uhr synchron ist.
initial begin
$monitor($time,, "%b : %b", in, out);
reset = 1;
#10
reset = 0;
#10
for (i = 0; i < maxValue; i = i + 1) begin
@(posedge clk) in <= i;
end
$finish;
end
always #10 clk = ~clk;