03_FSM_parte3 - jospicant/FSM_ASMD_Verilog_Icestudio GitHub Wiki
Nuestro ejemplo inicial usando distintos estilos.
Vamos a intentar mostrar distintas formas de crear una máquina FSM, tomando como referencia el ejemplo de la primera parte de FSM.
En el ejemplo que realizamos en el apartado 01_FSM nos basamos en un modelo donde la máquina de estado la formábamos con tres bloques constituidos por 2 bloques always combinacionales y 1 bloque always secuencial.
Puedes revisar los detalles en: 01_FSM
Este modelo, puede presentar algún inconveniente ya que puede aparecer algún tipo de glitch indeseado por poder aparecer problemas de carreras en la entrada del último bloque combinacional debido a que los tiempos de propagación de los flancos de subida de todos los flip-flops no son iguales. Vamos a presentar pues otros modelos que nos mejoren este aspecto.
ESTILO B. Nuestro ejemplo con 2 bloques always (Estilo recomendado)
Otra forma de realizar nuestro diseño es usar 2 bloques always:
- 1 always secuencial que se encarga de capturar el estado siguiente que debe tener el sistema
- 1 always combinacional que es función de la el estado actual y de las entradas que se encarga de calcular el estado siguiente y el valor de las salidas.
podemos ver el código resultante como:
//Máquina de estados usando dos always ( 1 combinacional y 1 secuencial )
module my_fsm2always(clk,reset,inA,inB,OutA,OutB);
input clk,reset,inA,inB;
output OutA,OutB;
reg OutA=0,OutB=0;
reg[1:0] estado_actual=0,estado_siguiente=0;
parameter E0=0,E1=1,E2=2,E3=3; // 4 estados posibles
//********************************* Parte 1 ****************************************************************
//Parte combinacional formada por procedimiento "always @( entradas y estado_actual )" **********************
// En un único always de tipo combinacional, añadimos "las salidas" y el "cálculo del siguiente estado" ******
//************************************************************************************************************
always @(inA or inB or estado_actual)
begin
//Valores por defecto *************************************************************************
estado_siguiente='bx; // asigno valores por defecto, se puede añadir aquí o como un default
OutA=0;
OutB=0;
//********************************************************************************************
case(estado_actual)
E0:begin
// aquí añadimos las salidas también en un único always, en esta línea
// no se añade porque la salida no cambia del valor por defecto inicial
case ({inA,inB}) // cálculo del siguiente estado
0:estado_siguiente=E0;
1:estado_siguiente=E3;
2:estado_siguiente=E1;
default:estado_siguiente=E0;
endcase
end
E1:begin
OutB=1; //añado el valor de la salida que cambia de su valor por defecto
case ({inA,inB})
0:estado_siguiente=E1;
1:estado_siguiente=E2;
2:estado_siguiente=E0;
default:estado_siguiente=E1;
endcase
end
E2:begin
OutA=1; //añado el valor de la salida que cambia de su valor por defecto
case ({inA,inB})
0:estado_siguiente=E2;
1:estado_siguiente=E1;
2:estado_siguiente=E3;
default:estado_siguiente=E2;
endcase
end
E3:begin
{OutA,OutB}=3; //añado el valor de la salida que cambia de su valor por defecto
case ({inA,inB})
0:estado_siguiente=E3;
1:estado_siguiente=E0;
2:estado_siguiente=E2;
default:estado_siguiente=E3;
endcase
end
//default:estado_siguiente='bx; // Esto nos previene de errores, ya que en un proceso de simulación,
// si se nos ha olvidado codificar alguno de los estados, cuando pasemos por el estado no codificado
// nos aperecerá que el estado siguiente será XX y sabremos donde no hemos codificado el estado siguiente
endcase
end
// ************************** Parte 2 *****************************************************
// Parte secuencial, el estado_actual capturará el nuevo estado (estado_siguiente) ********
// ****************************************************************************************
always @(posedge clk or posedge reset)
begin
if(reset) estado_actual<=E0;
else estado_actual <= estado_siguiente;
end
endmodule
podemos ver que:
-
El bloque always combinacional está formado por una lista sensible a los cambios del estado_actual y de las entradas inA, inB; al inicio del bloque, antes del case podemos ver que se asignan unos valores por defecto tanto para las salidas OutA, OutB como para el estado_siguiente, esto elimina la creación de latches y reduce la cantidad de código requerido para codificar el resto de las salidas dentro del case, añadiendo sólo las salidas que vayan a cambiar, de esta forma, en el estado donde la salida no toma el valor por defecto, se hará una nueva asignación. Habrá un if, else if , else por cada arco de transición existente en el diagrama de estado o como en este caso un case con todas las combinaciones posibles que representarían el arco de la transición
-
Un truco muy común es usar como valor por defecto para estado_siguiente el valor 'bx de forma que en una simulación será fácil ver que hemos olvidado una asiganción a un estado al ver que el valor del ** estado_siguiente = x **
El módulo en icestudio se puede crear de forma fácil creando una caja de código y añadiendo la instancia al fichero verilog que contiene el código para luego usar ese módulo en el ejemplo completo donde ponemos en práctica el sistema fsm realizado:
ESTILO C. Nuestro ejemplo en un único always (Estilo no recomendado para grandes FSM)
Se podría caer en la tentación de querer realizar todo el código usando un único always secuencial donde incluimos incluimos todo.
El código unificando todo en un único always secuencial es:
//Máquina de estados usando un único always secuencial
module my_fsm1always(clk,reset,inA,inB,OutA,OutB);
input clk,reset,inA,inB;
output OutA,OutB;
reg OutA=0,OutB=0;
reg[1:0] estado_actual=0; // No empleo estado anterior
parameter E0=0,E1=1,E2=2,E3=3; // 4 estados posibles
//**************************************************************************************
always @(posedge clk or posedge reset)
if(reset) begin
estado_actual<=E0; //Reset
OutA <=0;
OutB <=0;
end
else
begin
//Valores por defecto *************************************************************************
estado_actual<='bx; // asigno valores por defecto, se puede añadir aquí o como un default
OutA<=0;
OutB<=0;
//********************************************************************************************
case(estado_actual)
E0:begin
// aquí añadimos las salidas también en un único always, en esta línea
// no se añade porque la salida no cambia del valor por defecto inicial
case ({inA,inB}) // cálculo del siguiente estado
0:estado_actual<=E0;
1:estado_actual<=E3;
2:estado_actual<=E1;
default:estado_actual<=E0;
endcase
end
E1:begin
OutB<=1; //añado el valor de la salida que cambia de su valor por defecto
case ({inA,inB})
0:estado_actual<=E1;
1:estado_actual<=E2;
2:estado_actual<=E0;
default:estado_actual<=E1;
endcase
end
E2:begin
OutA<=1; //añado el valor de la salida que cambia de su valor por defecto
case ({inA,inB})
0:estado_actual<=E2;
1:estado_actual<=E1;
2:estado_actual<=E3;
default:estado_actual<=E2;
endcase
end
E3:begin
{OutA,OutB}<=3; //añado el valor de la salida que cambia de su valor por defecto
case ({inA,inB})
0:estado_actual<=E3;
1:estado_actual<=E0;
2:estado_actual<=E2;
default:estado_actual<=E3;
endcase
end
//default:estado_actual<='bx; // Esto nos previene de errores, ya que en un proceso de simulación,
// si se nos ha olvidado codificar alguno de los estados, cuando pasemos por el estado no codificado
// nos aperecerá que el estado siguiente será XX y sabremos donde no hemos codificado el estado siguiente
endcase
end
endmodule
NOTAS
- Vamos a usar un único bloque always de tipo secuencial por lo que siempre vamos a usar asignaciones nonblocking <=
- El uso de un único bloque es más largo y más propenso a generar errores, para pequeñas máquinas fsm, el código no es muy extenso para grandes fsm el código puede incrementarse entre un 88-165% más que usando estilos de 2 o 3 always.
En icestudio volvemos a crear un módulo instanciando al código verilog creado usando un único always:
Y usamos dicho módulo para crear nuestra máquina fsm:
ESTILO D. Uso de tres bloques always con las salidas registradas (Estilo recomendado)
Una buena opción a la hora de crear una máquina fsm, es emplear 3 bloques always implementando las salidas registradas.
Tendremos 2 bloques secuenciales con asignaciones non-blocking <= y un bloque combinacional con asignaciones blocking =
- Hay un bloque combinacional que se encarga de calcular estado_siguiente en función del estado_actual y entradas actuales
- Hay un bloque secuencial que únicamente se encarga de actualizar el estado_actual a partir del estado_siguiente calculado en el bloque comentado anterioremente.
- Hay un tercer bloque secuencial que se encarga de calcular el valor que toman las salidas en función del estado_siguiente y además estas son registradas.
Esta estructura de tres bloques es eficiente porque la asignación de las salidas solo se realiza una vez por cada estado y son puestas en distintos bloques secuenciales always para que sean registradas. Es una estructura organizada y fácil de mantener.
El código verilog quedaría como:
// Máquina fsm formado por 3 bloques always con un un bloque de salida registrado
module my_fsm3alwaysOutReg(clk,reset,inA,inB,OutA,OutB);
input clk,reset,inA,inB;
output OutA,OutB;
reg OutA=0,OutB=0;
reg[1:0] estado_actual=0,estado_siguiente=0;
parameter E0=0,E1=1,E2=2,E3=3; // 4 estados posibles
//********************************* Parte 1 ****************************************************************
//Parte combinacional formada por procedimiento "always @( entradas y estado_actual )" **********************
//************************************************************************************************************
always @(inA or inB or estado_actual)
begin
//Valores por defecto
estado_siguiente= 'bx;
case(estado_actual)
E0:begin
case ({inA,inB})
0:estado_siguiente=E0;
1:estado_siguiente=E3;
2:estado_siguiente=E1;
default:estado_siguiente=E0;
endcase
end
E1:begin
case ({inA,inB})
0:estado_siguiente=E1;
1:estado_siguiente=E2;
2:estado_siguiente=E0;
default:estado_siguiente=E1;
endcase
end
E2:begin
case ({inA,inB})
0:estado_siguiente=E2;
1:estado_siguiente=E1;
2:estado_siguiente=E3;
default:estado_siguiente=E2;
endcase
end
E3:begin
case ({inA,inB})
0:estado_siguiente=E3;
1:estado_siguiente=E0;
2:estado_siguiente=E2;
default:estado_siguiente=E3;
endcase
end
//default:estado_siguiente='bx; //Valor por defecto
endcase
end
// ************************** Parte 2 *****************************************************
// Parte secuencial, el estado_actual capturará el nuevo estado (estado_siguiente) ********
// ****************************************************************************************
always @(posedge clk or posedge reset)
begin
if(reset) estado_actual<=E0;
else estado_actual <= estado_siguiente;
end
// ********************** Parte 3 *****************************************************************
// Parte secuencial con salidas registradas ( en funcion del estado siguiente)
//**************************************************************************************************
always @(posedge clk or posedge reset)
begin
if (reset) begin
{OutA,OutB}=0;
end
else
begin
{OutA,OutB}=0; // Valores por defecto
case(estado_siguiente) //miramos el estado_siguiente, no el actual
//E0:{OutA,OutB}=0;
E1:OutB=1;
E2:OutA=1;
E3:{OutA,OutB}=3;
//default: {OutA,OutB}=0;
endcase
end
end
endmodule
Crearemos un módulo con Icestudio instanciado al nuevo fichero verilog creado 01_fsm3alwaysOutReg.v:
Y creamos el ejemplo con dicho módulo: