Máquinas de estado finito en VHDL

FSM VHDL

Las máquinas de estado finito, más conocidas por su acrónimo en inglés FSM (Finite State Machine), se utilizan ampliamente en el diseño de circuitos digitales (además de en otros ámbitos de la ingeniería, como la programación), para describir el comportamiento de un sistema según el valor de sus entradas y de cómo van cambiando en el tiempo. Ésta es una definición parcial pero que nos permite hacernos una primera idea intuitiva. Desde el punto de vista de las FSM, un sistema está compuesto de estados por los que va pasando el sistema, de señales de entrada que modifican esos estados y de señales de salida que pueden utilizarse para conocer el estado del sistema y actuar en consecuencia. Un ejemplo muy visual podría ser un semáforo, el cuál dispone de tres estados diferentes, uno para cada color. Las entradas del sistema las podría generar un temporizador que activa una señal cada cierto tiempo, indicando que hay que pasar al siguiente estado. Por último, las salidas del sistema podrían ser tres señales que indiquen qué lámpara, de las tres disponibles, tiene que encenderse. Para representar una máquina de estados se utilizan diagramas de estados como el siguiente.

 

Ejemplo FSM

 

Cada círculo representa un estado. En este caso tenemos cuatro estados que se llaman S1, S2, S3 y S4. El estado S1 tiene una flecha que indica que es el estado el inicial, al que se entra tras un reset del sistema. El S4 tiene un doble círculo, que indica que es un estado final. Los estados están unidos por una flechas llamadas transiciones, que indican cómo evoluciona el sistema de un estado a otro según se activan las señales de entrada. En este caso concreto hay tres señales de entrada (a, b, c y d). Si estando en el estado S1 se activa la señal a, el sistema evolucionará al estado S2. Del mismo modo, si estando en el estado S2 se activa la señal b, el sistema evolucionará de nuevo al estado S1. En este caso no hay señales de salida. Si las hay pueden ponerse dentro del círculo o, para no sobrecargar el diagrama, se pueden definir fuera de éste, por ejemplo en una tabla. Vamos a crear una FSM muy básica y a implementarla en VHDL, para poder llevarla a una FPGA. La máquina que vamos a implementar es la mostrada en el siguiente diagrama de estados.

 

diagrama FSM

Tenemos cuatro estados (S0, S1, S2 y S3) y dos señales de entrada (A y B). No hemos puesto en el diagrama las señales de salida, pero las definimos en la siguiente tabla:

S0 -> Y0

S1 -> Y1

S2 -> Y2

S3 -> Y3

Por lo tanto, tenemos cuatro señales de salida. La señal Y0 se activa cuando el sistema está en el estado S0, La Y1 cuando el sistema está en el estado S1 y así sucesivamente. Como se puede observar, la salida del sistema depende exclusivamente del estado en el que se encuentra el sistema. Cuando ocurre esto decimos que se trata de una máquina de estados de tipo Moore. Si la salida depende del estado y además de las entradas actuales del sistema, se trataría de una máquina de estados de tipo Mealy. Se puede demostrar que ambos modelos son equivalentes, así que aquí vamos a quedarnos con el primer tipo que es más sencillo.

A nivel físico, una de las formas más efectivas de implementar una FSM es según el siguiente esquema.

 

Esquema FSM

 

Obviamente necesitamos algún elemento de memoria para almacenar el estado actual del sistema. Nosotros usaremos un registro de biestables de tipo D (es por lo tanto un bloque secuencial). Los otros dos bloques son puramente combinacionales. El primero se encarga de generar el estado siguiente a partir del estado actual y de las entradas del sistema. Además, necesitamos otro circuito para generar las señales de salida a partir del estado actual (recordemos que es una máquina de tipo Moore).

Voy a llevar a la práctica el circuito usando una FPGA. En concreto usaré la placa BASYS 3 con una FPGA Artix 7 de Xilinx. Voy a utilizar el botón 0 como señal de reset y los botones 4 y 2 como señales A y B respectivamente. Las salidas Y0 a Y3 las voy a asociar a los LEDs 0 a 3. Para evitar utilizar circuitos antirebote para los pulsadores, he diseñado el diagrama de forma que dos pulsaciones seguidas del mismo botón no hagan evolucionar el sistema a otro estado.

Como tenemos cuatro estados, podríamos usar dos bit para codificar los estados. Por ejemplo: S0->00, S1->01, S2->10 y S3->11. Sin embargo, lo más habitual es dejar que el sintetizador lo haga por nosotros definiendo un tipo para los estados tal y como se ve en la siguiente definición. Así, si añadimos nuevos estados, no tenemos que andar redimensionando el registro de estados.

    -- declaraciones modelo FSM
    type STATES is (S0, S1, S2, S3);
    signal state_reg, state_next: STATES;

 

Veamos como se implementa en VHDL cada uno de estos tres bloques. Empecemos por el registro de estados.

    -- registro de estados
    process(CLK)
    begin
        if CLK'event and CLK='1' then
            if RST='1' then 
                state_reg <= s0;
            else 
                state_reg <= state_next;
            end if;
         end if;
    end process;

El registro de estados tiene una señal de reset síncrona que lleva al sistema al estado S0. Si la lógica de codificación del estado siguiente genera un nuevo estado, este se almacenará en el registro, si no, la salida del registro se mantiene. Veamos cómo es el circuito que genera el estado siguiente.

    -- Lógica de estado siguiente (circuito combinacional)
    process (state_reg, A, B)
    begin
        state_next <= state_reg; 
        case state_reg is 
        when S0 =>
            if A='1' then
                state_next <= S1; 
            end if; 
        when S1 =>
            if B='1' then
                state_next <= S2; 
            end if; 
        when S2 => 
            if A='1' then
                state_next <= S3; 
            end if; 
        when S3 =>
            state_next <= S3;
        end case;
    end process;

El estado siguiente se genera a partir del estado actual y de las entradas A y B, por lo que ponemos estas tres señales en la lista de sensibilidad del proceso. Para evitar que el sintetizador genere algún latch o elemento de memoria, asignamos por defecto el estado actual a la señal de estado siguiente. El código VHDL describe los estados y las transiciones asociadas a loas entradas según el diagrama de estados que definimos más arriba, de forma que, de ser necesario, asigna el valor del estado siguiente a la variable state_next cuando hay un cambio en las entradas o en el estado. Por último echamos un vistazo al circuito de decodificación para las salidas.

    -- salida tipo Moore
    process (state_reg)
    begin
        -- estableciendo la salida por defecto
        -- nos aseguramos de crear un circuito
        -- combinacional sin latches.
        Y0 <= '0';
        Y1 <= '0';
        Y2 <= '0';
        Y3 <= '0'; 
        case state_reg is 
        when S0 => Y0 <= '1'; 
        when S1 => Y1 <= '1'; 
        when S2 => Y2 <= '1'; 
        when S3 => Y3 <= '1';
        end case; 
    end process;

De nuevo, para evitar que el sintetizador cree elementos de memoria, asignamos un valor por defecto a las salidas (en este caso 0), y según el estado actual, se activa la salida correspondiente según definimos en la tabla de arriba.

El código VHDL  completo de nuestra FSM es el siguiente.

 

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity FSM is
    Port ( CLK : in STD_LOGIC;
           BTN : in STD_LOGIC_VECTOR (4 downto 0);
           LED : out STD_LOGIC_VECTOR (15 downto 0));
end FSM;

architecture Behavioral of FSM is
    -- alias
    alias RST : STD_LOGIC is BTN(0);
    alias A : STD_LOGIC is BTN(4);
    alias B : STD_LOGIC is BTN(2);
    alias Y0 : STD_LOGIC is LED(0);
    alias Y1 : STD_LOGIC is LED(1);
    alias Y2 : STD_LOGIC is LED(2);
    alias Y3 : STD_LOGIC is LED(3);
    
    -- declaraciones modelo FSM
    type STATES is (S0, S1, S2, S3);
    signal state_reg, state_next: STATES;

begin

    -- registro de estados
    process(CLK)
    begin
        if CLK'event and CLK='1' then
            if RST='1' then 
                state_reg <= s0;
            else 
                state_reg <= state_next;
            end if;
         end if;
    end process;

    -- Lógica de estado siguiente (circuito combinacional)
    process (state_reg, A, B)
    begin
        state_next <= state_reg; 
        case state_reg is 
        when S0 =>
            if A='1' then
                state_next <= S1; 
            end if; 
        when S1 =>
            if B='1' then
                state_next <= S2; 
            end if; 
        when S2 => 
            if A='1' then
                state_next <= S3; 
            end if; 
        when S3 =>
            state_next <= S3;
        end case;
    end process;
    
    -- salida tipo Moore
    process (state_reg)
    begin
        -- estableciendo la salida por defecto
        -- nos aseguramos de crear un circuito
        -- combinacional sin latches.
        Y0 <= '0';
        Y1 <= '0';
        Y2 <= '0';
        Y3 <= '0'; 
        case state_reg is 
        when S0 => Y0 <= '1'; 
        when S1 => Y1 <= '1'; 
        when S2 => Y2 <= '1'; 
        when S3 => Y3 <= '1';
        end case; 
    end process;    

end Behavioral;

 

Os dejo el archivo de restricciones para la placa BASYS 3 que he usado para este ejemplo, donde se definen los puertos de los pulsadores, los LEDs y el reloj del sistema.

## LEDs

set_property PACKAGE_PIN U16 [get_ports {LED[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[0]}]
set_property PACKAGE_PIN E19 [get_ports {LED[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[1]}]
set_property PACKAGE_PIN U19 [get_ports {LED[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[2]}]
set_property PACKAGE_PIN V19 [get_ports {LED[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[3]}]
set_property PACKAGE_PIN W18 [get_ports {LED[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[4]}]
set_property PACKAGE_PIN U15 [get_ports {LED[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[5]}]
set_property PACKAGE_PIN U14 [get_ports {LED[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[6]}]
set_property PACKAGE_PIN V14 [get_ports {LED[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[7]}]
set_property PACKAGE_PIN V13 [get_ports {LED[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[8]}]
set_property PACKAGE_PIN V3 [get_ports {LED[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[9]}]
set_property PACKAGE_PIN W3 [get_ports {LED[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[10]}]
set_property PACKAGE_PIN U3 [get_ports {LED[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[11]}]
set_property PACKAGE_PIN P3 [get_ports {LED[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[12]}]
set_property PACKAGE_PIN N3 [get_ports {LED[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[13]}]
set_property PACKAGE_PIN P1 [get_ports {LED[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[14]}]
set_property PACKAGE_PIN L1 [get_ports {LED[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[15]}]


## Botones
set_property IOSTANDARD LVCMOS33 [get_ports {BTN[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BTN[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BTN[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BTN[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BTN[0]}]
## BTNL -> W19
set_property PACKAGE_PIN W19 [get_ports {BTN[4]}] 
## BTND -> U17
set_property PACKAGE_PIN U17 [get_ports {BTN[3]}] 
## BTNR -> T17
set_property PACKAGE_PIN T17 [get_ports {BTN[2]}] 
## BTNU -> T18
set_property PACKAGE_PIN T18 [get_ports {BTN[1]}] 
## BTNC -> U18
set_property PACKAGE_PIN U18 [get_ports {BTN[0]}] 

## Reloj CLK conectado a W5
set_property IOSTANDARD LVCMOS33 [get_ports {CLK}]
set_property PACKAGE_PIN W5 [get_ports {CLK}]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK]

En la simulación de la FSM vemos como evoluciona el circuito desde S0 a S3 según van cambiando las entradas A y B. También podemos observar cómo se activan las entradas Y0 a Y3.

Simulacion FSM

 

CompartirShare on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

Sé el primero en comentar en "Máquinas de estado finito en VHDL"

Deja un comentario.

Tu dirección de correo no será publicada.


*