verilog-implementacion-fsm-plantilla-3-procesos

Cómo implementar una FSM en Verilog con tres procesos

  • 5 min

Una FSM en Verilog es una máquina de estados escrita como bloques de lógica separados, normalmente memoria de estado, lógica de transición y lógica de salida.

En el artículo anterior vimos la teoría de las Máquinas de Estados Finitos (FSM). Vimos círculos, flechas y debatimos entre Moore y Mealy. Hoy toca bajar al barro: Vamos a escribir el código.

Si buscas por internet, verás que hay mil formas de escribir una FSM en Verilog. Alguna gente lo mete todo en un solo bloque gigante, otros usan dos…

Nosotros vamos a usar la Técnica de los 3 Procesos. Es la metodología más ordenada, legible y segura contra errores (especialmente contra la creación involuntaria de Latches). Consiste en dividir mentalmente y en código la máquina en tres bloques hardware distintos.

Definición de estados

Antes de los procesos, necesitamos definir nuestros estados. Para que el código sea legible, jamás uses números hardcodeados (case(0)). Usa constantes simbólicas.

En Verilog moderno (localparam), solemos dejar que el sintetizador elija la codificación (Binaria o One-Hot), así que nosotros solo damos nombres.

// Definición de estados con nombres legibles
localparam ESTADO_IDLE    = 2'b00;
localparam ESTADO_TRABAJO = 2'b01;
localparam ESTADO_FIN     = 2'b10;

// Necesitamos DOS registros de estado
reg [1:0] state;       // Estado Actual (Memoria)
reg [1:0] next_state;  // Próximo Estado (Calculado)
Copied!

Fíjate en esta distinción

  • :state: Es lo que somos AHORA
  • next_state: Es lo que seremos en el SIGUIENTE ciclo de reloj.

Proceso 1: memoria de estado

Este es el único bloque que tiene reloj (posedge clk) y que usa asignación no bloqueante (<=). Su única misión es actualizar el estado actual con el valor calculado del siguiente estado o resetear la máquina.

// --- PROCESO 1: Actualización de Estado ---
always @(posedge clk) begin
    if (reset) begin
        state <= ESTADO_IDLE; // Reset síncrono
    end else begin
        state <= next_state;  // Avanzamos al futuro
    end
end
Copied!

Proceso 2: lógica de próximo estado

Aquí reside la inteligencia de las transiciones (las flechas del diagrama). Es un bloque puramente combinacional (always @*). Su misión es mirar el state actual y las entradas (inputs), y decidir cuál debe ser el next_state.

¡Peligro de Latch!

Para evitar crear memorias indeseadas, SIEMPRE debemos asignar un valor por defecto a next_state al principio del bloque. Generalmente, el valor por defecto es “quedarse igual”.

// --- PROCESO 2: Cálculo del Siguiente Estado ---
always @(*) begin
    // 1. Valor por defecto (Evita Latches)
    next_state = state; 

    // 2. Lógica de transiciones
    case (state)
        ESTADO_IDLE: begin
            if (boton_start == 1'b1)
                next_state = ESTADO_TRABAJO;
        end

        ESTADO_TRABAJO: begin
            if (tarea_terminada == 1'b1)
                next_state = ESTADO_FIN;
        end

        ESTADO_FIN: begin
            // Volvemos al inicio incondicionalmente
            next_state = ESTADO_IDLE;
        end
        
        default: next_state = ESTADO_IDLE; // Seguridad
    endcase
end
Copied!

Proceso 3: lógica de salida

Finalmente, decidimos qué hacen los pines de salida de la FPGA en función del estado. Si estamos diseñando una máquina de Moore, las salidas solo dependen de state.

// --- PROCESO 3: Decodificación de Salidas ---
// Ejemplo: Controlar un LED y un Motor
always @(*) begin
    // 1. Valores por defecto (Apagado)
    led_status = 1'b0;
    motor_run  = 1'b0;

    // 2. Activación según estado
    case (state)
        ESTADO_IDLE: begin
            led_status = 1'b1; // LED encendido esperando
        end

        ESTADO_TRABAJO: begin
            motor_run = 1'b1;  // Motor en marcha
        end
        
        // En ESTADO_FIN usamos los valores por defecto (todo a 0)
    endcase
end
Copied!

A veces, si la lógica de salida es muy simple, puedes sustituir este bloque always por un simple assign.

assign motor_run = (state == ESTADO_TRABAJO);

Ejemplo completo: un semáforo simple

Vamos a juntar todo en un ejemplo real. Un semáforo que cambia de Verde a Amarillo y a Rojo cuando pasa el tiempo. Asumiremos que tenemos una entrada timer_done que nos avisa cuándo cambiar.

module semaforo (
    input wire clk,
    input wire rst,
    input wire timer_done,      // Señal externa de tiempo
    output reg [2:0] luces      // R, A, V
);

    // 1. Definición de Estados (codificación binaria simple)
    localparam VERDE    = 2'b00;
    localparam AMARILLO = 2'b01;
    localparam ROJO     = 2'b10;

    reg [1:0] state, next_state;

    // --- PROCESO 1: Memoria ---
    always @(posedge clk) begin
        if (rst) state <= VERDE;
        else     state <= next_state;
    end

    // --- PROCESO 2: Transiciones ---
    always @(*) begin
        next_state = state; // Por defecto: Quedarse quieto

        case (state)
            VERDE: begin
                if (timer_done) next_state = AMARILLO;
            end
            AMARILLO: begin
                if (timer_done) next_state = ROJO;
            end
            ROJO: begin
                if (timer_done) next_state = VERDE;
            end
            default: next_state = VERDE;
        endcase
    end

    // --- PROCESO 3: Salidas (Moore) ---
    always @(*) begin
        luces = 3'b000; // Por defecto todo apagado

        case (state)
            VERDE:    luces = 3'b001; // Bit 0 ON
            AMARILLO: luces = 3'b010; // Bit 1 ON
            ROJO:     luces = 3'b100; // Bit 2 ON
        endcase
    end

endmodule
Copied!

¿Por qué hacerlo así?

Podrías pensar: “¡Cuánto código para algo tan simple! Podría haber usado ifs anidados”.

La ventaja de esta plantilla es la Escalabilidad y Depuración.

  1. Si el semáforo no cambia de color, miras el Proceso 2.
  2. Si el semáforo está en rojo pero la luz no se enciende, miras el Proceso 3.
  3. Si la máquina se resetea sola, miras el Proceso 1.

Separar las preocupaciones nos permite diseñar máquinas de estados con decenas o cientos de estados sin volvernos locos.