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.

FSM Ejemplo

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.

Partes de una máquina de estados

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:

01_fsm2always.v

instacia al módulo 01_fsm2always.v

01_fsm2always.ice

Ejemplo de uso

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.

Always único

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:

instacia 01_fsm1always.v

Y usamos dicho módulo para crear nuestra máquina fsm:

Ejemplo usando 1 always

Ejemplo usando módulo con 1 único always

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.

Esquema 3 always con 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:

01_fsm3alwaysOutReg.v

instacia a 01_fsm3alwaysOutReg

Y creamos el ejemplo con dicho módulo:

03_ejemplo3alwaysOutReg.ice

Ejemplo 01_fsm3alwaysOutReg.ice