Estoy trabajando en un proyecto en el que necesito convertir una máquina de estados finitos codificada en Verilog en una ROM. Para hacer esto, necesito crear un archivo de memoria para la versión ROM del FSM que almacena el estado siguiente y los valores de salida en las direcciones compuestas por el estado actual y los valores de entrada. Mi idea de cómo construir este archivo fue tomar el FSM en cuestión y agregar la entrada "estado_actual" y las salidas "estado_siguiente", y luego hacer una pequeña modificación en el FSM para que los use. Sin embargo, he tenido varios problemas con este tesbench que parece que no puedo resolver, así que pensé en armar un fsm más pequeño para mostrar lo que estoy haciendo y, con suerte, obtener una idea de lo que estoy haciendo incorrectamente. Creo que mi problema principal es que la señal del reloj no parece activarse en absoluto (al menos cuando depuro, el bloque que debería activarse en el reloj posedge no parece activarse nunca). El FSM en cuestión que estoy tratando de modelar tiene algo así como 15 bits de estado actual y de entrada, y 76 bits de estado siguiente y de salida, así que en lugar de usar el código, acabo de juntar este FSM más pequeño (obtengo el mismo comportamiento por lo que el problema no es específico de la máquina de estado y probablemente se deba más a mi mal banco de pruebas).
Así que aquí está la unidad FSM bajo prueba. Es un FSM básico que saqué de un viejo libro de texto:
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
Y aquí está el mismo FSM modificado (creo) para aceptar el estado actual como entrada y asignar la siguiente salida de estado a una salida de cable.
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
Así que aquí es donde seguramente radica el problema, el banco de pruebas. Ahora sé que, dadas las entradas limitadas de este FSM, sería muy fácil presionar manualmente el fsm con cada combinación de entrada y mostrar la salida, sin embargo, obviamente, con el otro FSM que tiene cerca de 100 bits de IO, esa no es realmente una opción. . Por lo tanto, decidí crear un bucle for para iterar a través de todas las posibles combinaciones de bits de entrada y luego generar las salidas para guardarlas en el archivo de memoria. El problema con el que me encuentro es que no parece que la entrada del reloj cicle en absoluto. Actualmente estoy probando el siguiente código de banco de pruebas
`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
Inicialmente había intentado crear un ciclo de reloj independiente mediante el uso de un
always #10 clk = ~clk;
Y luego poner retrasos dentro del bucle for, que a verilog NO le gustó (ni siquiera compilaba, arrojaba errores de sintaxis).
Entonces, tal como está ahora, este código de banco de pruebas se compila y el simulador se activa, sin embargo, está bastante claro que no se accede a la rama else de la actualización del registro de estado en el FSM, ya que cuando configuro puntos de interrupción allí, nunca se activan y la salida de la máquina de estado parece ser solo el estado de referencia de un reinicio del sistema. (como sigue)
# 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
Hubiera pensado que forzar manualmente el ciclo del reloj en el bucle for habría hecho que la máquina de estados finitos se actualizara, pero parece que no. ¿Hay alguna manera de asegurarme de que el ciclo del reloj se active de esa manera?
Sí, el problema está en el código de su banco de pruebas. Siempre es una buena idea mostrar el tiempo de simulación cuando valora, $display
ya que ayuda a depurar su problema. Si lo hace, muestra que toda su salida se produce al mismo tiempo (20ns):
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
La primera columna es el tiempo de simulación. El problema es que no transcurre tiempo dentro del for
bucle. Una forma de solucionarlo es agregar #
retrasos en el ciclo de la siguiente manera:
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
Esto imprime:
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
Como puede ver, el tiempo de simulación y out
están cambiando de valor, y out
ya no tiene incógnitas ( x
).
Aquí hay otra forma de escribir el código del banco de pruebas. Utiliza el always
bloque más tradicional para el reloj, que estaba tratando de hacer funcionar. También asegura que la entrada esté sincronizada con el reloj.
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;