VHDL: construyendo circuitos para autómatas

VHDL simulando con GTKWave y Ghdl autómatas..

Sigo practicando, poniéndome al día con la creación de circuitos digitales. Si has seguido los últimos posts sobre VHDL, verás que hemos llegado a los autómatas. Estos autómatas, son las partes de los circuitos que controlan los procesos internos de muchos otros componentes. Es decir, las lavadoras, neveras, microondas, televisores, aires acondicionados, alarmas, routers, puentes de comunicación de las placas madre, microprocesadores, etcétera.. llevan autómatas dentro, ya sean máquinas de Moore, de Mealy, finitos, seguros, etc…

Pensemos por ejemplo en un autómata para una aspiradora autónoma, que tenga unos estados de funcionamiento como aspirando, en reposo, en recarga, una serie de sensores de entrada de choque, infrarrojos, ultrasonidos, quizá una cámara, y una serie de actuadores como motores para las ruedas, aspiración.. Pensemos por ejemplo en un autómata para un microondas, en el que vamos seleccionando el programa, le ponemos un tiempo, y le damos al botón de start para que vaya calentando la comida con un contador decreciente de tiempo.. O pensemos por ejemplo en un autómata para controlar unos cultivos hidropónicos, tendrá sensores de agua, temperatura, luces, actuadores para las bombas de agua, quizá ventiladores o ventanas si es un invernadero, altavoces para hacer sonar notificaciones, etc..

En todos estos casos, parte de la circuitería, si se hace con circuitería digital directa sin un PC completo que controle los procesos.. entonces deberá de estar controlado por autómatas que almacenen estados. Recibirán una serie de señales de entrada, cambiará su estado según las señales de entrada, y emitirá una serie de señales de salida, según cada caso y para el propósito de la máquina o componente en concreto.

Cómo plantear la construcción de autómatas

Estoy viendo que en líneas generales se construyen de igual manera. Así que aquí vengo a dejar este apunte, a modo de code-kata. De lo que estoy aprendiendo.

Lo primero interesante es construir una tabla de transiciones de estados: con los estados actuales, todas las posibles entradas, y todas las posibles salidas. Para el caso de este post, simplemente se trata de un autómata que lleva la cuenta de si hemos puesto 1 en la entrada durante 8 ciclos de reloj consecutivos. Es decir, para simplificar sólo tiene 8 estados, 1 entrada, 8 salidas y una señal de reloj para ir haciendo los cambios de estado al son del tic tac..

Estado actualCodificación estadoEntradaSiguienteLas 8 salidas
S00000S000000000
S00001S100000000
S10010S000000001
S10011S200000001
S20100S000000011
S20101S300000011
S30110S000000111
S30111S400000111
S41000S000001111
S41001S500001111
S51010S000011111
S51011S600011111
S61100S000111111
S61101S700111111
S71110S001111111
S71111S701111111

El siguiente paso ya es ponerse a definir el circuito digital..

Código fuente en VHDL de un package general

Aquí dejo la definición de los estados y variables principales:

library IEEE;
use IEEE.std_logic_1164.all;

-- Definiciones de estados, las principales variables..
package automaton_package is
    constant BITSTATUS : integer := 3;
    constant S0 : std_logic_vector (BITSTATUS-1 downto 0) := "000";
    constant S1 : std_logic_vector (BITSTATUS-1 downto 0) := "001";
    constant S2 : std_logic_vector (BITSTATUS-1 downto 0) := "010";
    constant S3 : std_logic_vector (BITSTATUS-1 downto 0) := "011";
    constant S4 : std_logic_vector (BITSTATUS-1 downto 0) := "100";
    constant S5 : std_logic_vector (BITSTATUS-1 downto 0) := "101";
    constant S6 : std_logic_vector (BITSTATUS-1 downto 0) := "110";
    constant S7 : std_logic_vector (BITSTATUS-1 downto 0) := "111";
end package;

El código fuente en VHDL del circuito del autómata general

Este autómata simplemente empieza en el estado S0, y va pasando de estado con cada flanco de reloj y si recibe un 1 en la entrada, hasta el estado S7.

library IEEE;
use IEEE.std_logic_1164.all;
use work.automaton_package.all;

-- Definición de entradas y salidas principales..
entity automaton is
    port (
        status : out std_logic_vector (BITSTATUS-1 downto 0);
        output7, output6, output5, output4, output3, output2, output1, output0 : out std_logic;
        input : in std_logic;
        clock : in std_logic
    );
end entity;

-- Arquitectura que define el comportamiento de este autómata..
architecture arch_automaton of automaton is

    -- Variables auxiliares internas.
    signal internalStatus : std_logic_vector (BITSTATUS-1 downto 0);
    signal internalOutput : std_logic_vector (7 downto 0);

begin

    -- Cableando salidas.
    status <= internalStatus;
    output7 <= internalOutput(7);
    output6 <= internalOutput(6);
    output5 <= internalOutput(5);
    output4 <= internalOutput(4);
    output3 <= internalOutput(3);
    output2 <= internalOutput(2);
    output1 <= internalOutput(1);
    output0 <= internalOutput(0);

    -- Procesando las entradas para cambiar entre
    -- estados según las entradas, y en el flanco de 
    -- subida del reloj..
    processing_inputs : process (clock)
    begin
        if rising_edge(clock) then
            case internalStatus is
                when S0 =>
                    if input = '1' then
                        internalStatus <= S1;
                    else
                        internalStatus <= S0;
                    end if;
                when S1 =>
                    if input = '1' then
                        internalStatus <= S2;
                    else
                        internalStatus <= S0;
                    end if;
                when S2 =>
                    if input = '1' then
                        internalStatus <= S3;
                    else
                        internalStatus <= S0;
                    end if;
                when S3 =>
                    if input = '1' then
                        internalStatus <= S4;
                    else
                        internalStatus <= S0;
                    end if;
                when S4 =>
                    if input = '1' then
                        internalStatus <= S5;
                    else
                        internalStatus <= S0;
                    end if;
                when S5 =>
                    if input = '1' then
                        internalStatus <= S6;
                    else
                        internalStatus <= S0;
                    end if;
                when S6 =>
                    if input = '1' then
                        internalStatus <= S7;
                    else
                        internalStatus <= S0;
                    end if;
                when S7 =>
                    if input = '1' then
                        internalStatus <= S7;
                    else
                        internalStatus <= S0;
                    end if;
                when others =>
                    -- Sino definido el estado exacto entonces S0..
                    internalStatus <= S0;
            end case;
        end if;
    end process;

    -- Procesando las salidas. Al tratarse de salidas que sólo
    -- dependen del estado interno se trata de una máquina de Moore.
    -- Si generásemos las salidas en función al estado y a las 
    -- entradas, entonces estaríamos haciendo una máquina de Mealy.
    generating_outputs : process (internalStatus)
    begin
        case internalStatus is
            when S0 =>
                internalOutput <= "00000000";
            when S1 =>
                internalOutput <= "00000001";
            when S2 =>
                internalOutput <= "00000011";
            when S3 =>
                internalOutput <= "00000111";
            when S4 =>
                internalOutput <= "00001111";
            when S5 =>
                internalOutput <= "00011111";
            when S6 =>
                internalOutput <= "00111111";
            when S7 =>
                internalOutput <= "01111111";
            when others =>
                internalOutput <= "ZZZZZZZZ";
        end case;
    end process;

end architecture;

La definición en VHDL del banco de pruebas

Simplemente simula otro circuito que conecta y va generando señales de entrada y el reloj para visualizar las salidas.

library IEEE;
use IEEE.std_logic_1164.all;
use work.automaton_package.all;

entity automaton_tb is
end entity;

-- Comportamiento del banco de pruebas..
architecture arch_automaton_tb of automaton_tb is

    component automaton is
        port (
            status : out std_logic_vector (BITSTATUS-1 downto 0);
            output7, output6, output5, output4, output3, output2, output1, output0 : out std_logic;
            input : in std_logic;
            clock : in std_logic
        );
    end component;

    signal testStatus : std_logic_vector (BITSTATUS-1 downto 0);
    signal testOutput : std_logic_vector (7 downto 0);
    signal testInput : std_logic;
    signal testClock : std_logic := '0';
    signal tics : integer := 0;

begin

    -- Cableando con el autómata..
    testing_unit: automaton port map (
        status => testStatus,
        output7 => testOutput(7),
        output6 => testOutput(6),
        output5 => testOutput(5),
        output4 => testOutput(4),
        output3 => testOutput(3),
        output2 => testOutput(2),
        output1 => testOutput(1),
        output0 => testOutput(0),
        input => testInput,
        clock => testClock
    );

    -- Generando el tic tac del reloj..
    generate_100Mhzs_clock : process
    begin
        report "Tic tac.. testClock=" & std_logic'image(testClock);
        testClock <= not testClock;
        if testClock = '1' then tics <= tics + 1; end if;
        if tics >= 25 then wait; end if;
        wait for 5 ns; -- Tiempo de espera en un flanco de reloj.
    end process;

    -- Unas señales de entrada y esperas para ver los resultados..
    main_process : process
    begin
        wait for 5 ns;
        testInput <= '0'; wait for 20 ns;
        testInput <= '1'; wait for 20 ns;
        testInput <= '0'; wait for 20 ns;
        testInput <= '1'; wait for 50 ns;
        testInput <= '0'; wait for 20 ns;
        testInput <= '1'; wait for 100 ns;
        wait;

    end process;

end architecture;

Terminando

Si todo ha ido bien, al simularlo se tiene que ver una imagen de las señales como la del principio. En teoría, si el autómata es sintetizable y tienes una FPGA podrías inyectarlo para probarlo experimentalmente.

Me remito a la Wikipedia que explica muy bien más casos de posibles aplicaciones. Sobretodo en la industria y domótica que podemos encontrarlos:
https://es.wikipedia.org/wiki/Aut%C3%B3mata_programable

Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *