Considere el siguiente diagrama de estado donde las entradas son c
y v
. El sistema también está recibiendo un reloj de alta frecuencia clk
, alrededor de 50 MHz.
Como se muestra en el diagrama, la primera entrada se usa para avanzar los estados hacia la derecha y la segunda entrada es para avanzar los estados hacia la izquierda. En la implementación hardware (Altera DE1) estas entradas son botones.
El problema es que, dado que la frecuencia del reloj es muy alta (en relación con la duración de la presión del botón), cuando c
se presiona el botón, cae al último estado S3
. Luego, si v
se presiona el botón, vuelve a subir por el estado inicial S0
. Los estados intermedios S1
y S2
sólo se visitan muy brevemente. La siguiente figura ilustra la situación.
Aquí está mi código VHDL para implementar la máquina de estado.
Library IEEE;
Use IEEE.std_logic_1164.all;
Entity state_machine is
port (
clk, c, v : in std_logic;
outp : out std_logic_vector(1 downto 0)
);
end state_machine;
Architecture beh of state_machine is
type state_type is (S0, S1, S2, S3); -- there are four states
signal current_state : state_type := S0 ;
Begin
Process(clk)
Begin
if (clk'event and clk = '1' ) then
case current_state is
when S0 =>
if c = '1' then
current_state <= S1;
end if;
when S1 =>
if c = '1' then
current_state <= S2;
elsif v = '1' then
current_state <= S0;
end if;
when S2 =>
if c = '1' then
current_state <= S3;
elsif v = '1' then
current_state <= S1;
end if;
when S3 =>
if v = '1' then
current_state <= S2;
end if;
End case;
End if;
End Process;
With current_state select
outp <= "00" when S0,
"01" when S1,
"10" when S2,
"11" when S3;
End beh;
En un experimento reduje el reloj a 2 Hz. Funciona como se pretendía en el diagrama de estado anterior, pero no es muy confiable. A veces avanza un estado, a veces "pierde" el reloj, a veces avanza dos estados. Seguramente esto no es aceptable.
También traté de separar el reloj y la lógica de la máquina de estado, en el siguiente código.
Library IEEE;
Use IEEE.std_logic_1164.all;
Entity state_machine_2 is
port (
clk, c, v : in std_logic;
outp : out std_logic_vector(1 downto 0)
);
end state_machine_2;
Architecture beh of state_machine_2 is
type state_type is (S0, S1, S2, S3); -- there are four states
signal current_state : state_type := S0 ;
signal next_state : state_type := S0 ;
Begin
Process(clk)
Begin
if (clk'event and clk = '1' ) then
current_state <= next_state ;
end if;
End Process;
Process(c, v)
Begin
Case current_state is
when S0 =>
if c = '1' then
next_state <= S1;
end if;
when S1 =>
if c = '1' then
next_state <= S2;
elsif v = '1' then
next_state <= S0;
end if;
when S2 =>
if c = '1' then
next_state <= S3;
elsif v = '1' then
next_state <= S1;
end if;
when S3 =>
if v = '1' then
next_state <= S2;
end if;
End case;
End Process;
With current_state select
outp <= "00" when S0,
"01" when S1,
"10" when S2,
"11" when S3;
End beh;
El código anterior se compila, pero el resultado resultante no está definido.
¿Cómo implementar el diagrama de estado anterior en VHDL y funcionaría como cabría esperar? Es decir, uno presiona c
una vez y simplemente avanzaría un solo estado.
¿Quizás no debería usar el reloj en el FSM por completo? ¿O hay una mejor estrategia?
Veo dos opciones. El primero, como sugiere Kevin White, es agregar estados intermedios que esperan c = 0
y v = 0
entre cada estado. Sin embargo, no se comportaría como se esperaba si presiona c, luego presiona v, suelta c, presiona c, suelta v y repite el ciclo. En ese caso, permanecería en el mismo estado en lugar de moverse de un lado a otro (a menos que agregue aún más estado de transición).
Una segunda solución, que soluciona ese problema, es agregar 2 entradas, c_d
y v_d
, que sería una versión retrasada (en 1 ciclo de reloj) de la entrada correspondiente. La transición de estado ocurriría en c = '1' and c_d = '0'
su lugar. Es fácil hacerlo:
architecture beh of state_machine_2 is
signal c_d, v_d : std_logic;
-- other declarations
begin
DELAY_INPUTS: process(clk)
begin
if rising_edge(clk) then
c_d <= c;
v_d <= v;
end if;
end process DELAY_INPUTS;
Agregaré, dado que parece un principiante en FPGA, que debe considerar la metaestabilidad y el rebote de botones. La metaestabilidad es un riesgo cada vez que usa una señal asíncrona (como la entrada de un botón) en un diseño síncrono. Se elimina poniendo 2 flip-flop consecutivos en la entrada. Altera tiene más documentación al respecto .
El otro problema es el rebote. Al igual que con cualquier interruptor mecánico, el contacto que se hace cuando presiona el botón no es de encendido y apagado, sino más bien una larga serie de transiciones de encendido-apagado-encendido-apagado a medida que el interruptor mecánico alcanza su posición final. En un circuito digital, dará como resultado la lectura de varias presiones y liberaciones para la misma carrera. Se soluciona con un circuito antirrebote, que generalmente se basa en esperar algunos milisegundos cuando el interruptor cambia antes de volver a leerlo.
Necesita estados adicionales después de S0, S1... donde espera que c baje antes de continuar.
Puede usar los mismos estados para esperar a que V baje. Deberá definir qué sucede cuando C y V están activos simultáneamente.
kevin
Paebbels
sig_re <= not sig_d and sig;
(re = flanco ascendente). Puede incluirlo en el FSM solicitando esa condición completa o puede calcularlo fuera de su proceso de FSM, lo que brinda una solución más limpia. Ahora puede usarc_re
yv_re
como una entrada directa de FSM sin preocuparse por fallas. Si agrega un circuito de sincronización de pines y antirrebote a su diseño, puede decidir emitir solo señales de botón detectadas en el borde que se pueden usar directamente en sus FSM.