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.
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 actual | Codificación estado | Entrada | Siguiente | Las 8 salidas |
S0 | 000 | 0 | S0 | 00000000 |
S0 | 000 | 1 | S1 | 00000000 |
S1 | 001 | 0 | S0 | 00000001 |
S1 | 001 | 1 | S2 | 00000001 |
S2 | 010 | 0 | S0 | 00000011 |
S2 | 010 | 1 | S3 | 00000011 |
S3 | 011 | 0 | S0 | 00000111 |
S3 | 011 | 1 | S4 | 00000111 |
S4 | 100 | 0 | S0 | 00001111 |
S4 | 100 | 1 | S5 | 00001111 |
S5 | 101 | 0 | S0 | 00011111 |
S5 | 101 | 1 | S6 | 00011111 |
S6 | 110 | 0 | S0 | 00111111 |
S6 | 110 | 1 | S7 | 00111111 |
S7 | 111 | 0 | S0 | 01111111 |
S7 | 111 | 1 | S7 | 01111111 |
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