State Machine usando Case obteniendo un resultado inesperado

Estoy tratando de escribir una máquina de estado muy simple que implemente un bloqueo combinado.

El código es: Switch1 -> Switch2 -> Switch3 -> Switch4

Me doy cuenta de que es Switch 7, 6, 5, 4 en consecuencia en el código.

Si no se hace en ese orden, da el estado de error (incorrecto).

El problema que tengo es que aunque statees state_start(como lo veo en los LED) no cambiará state_1_righty en su lugar simplemente bombeará el archivo error_state. Sé que entra en esa declaración if porque cambié a elsey state <= "00001010";muestra eso.

¿Qué estoy haciendo mal? No veo ningún error en mi lógica (a menos que haya algún cambio de rebote extraño).

Aquí está el código que estoy intentando ahora:

entity CombinationLockFSM is
    Port(
        Switches: in std_logic_vector(7 downto 0);
        LEDs: out std_logic_vector(7 downto 0)
    );
end CombinationLockFSM;

architecture Behavioral of CombinationLockFSM is
    constant state_start: std_logic_vector(7 downto 0) := "10000000";
    constant state_1_right: std_logic_vector(7 downto 0) := "01000000";
    constant state_2_right: std_logic_vector(7 downto 0) := "00100000";
    constant state_3_right: std_logic_vector(7 downto 0) := "00010000";
    constant state_error: std_logic_vector(7 downto 0) := "00001111";

    signal state: std_logic_vector(7 downto 0) := (others => '0');
begin

    LEDs <= state;

    process(Switches)
    begin
        case Switches is
            when "00000000" => 
                state <= state_start;
            when "10000000" => 
                if state = state_start then
                    state <= state_1_right;
                else
                    state <= state_error;
                end if;
            when "11000000" => 
                if state = state_1_right then
                    state <= state_2_right;
                else
                    state <= state_error;
                end if;
            when "11100000" => 
                if state = state_2_right then
                    state <= state_3_right;
                else
                    state <= state_error;
                end if;
            when "11110000" => 
                if state = state_3_right then
                    state <= "11110000";
                else
                    state <= state_error;
                end if;


            when others =>
                state <= state_error;
        end case;

    end process;

end Behavioral;

Gracias a Brian Drummond por encontrar el error en mi lógica y la sugerencia de un reloj. Tuve que agregar algo de lógica adicional en las declaraciones if ya que el reloj recorre el bloque de casos rápidamente y el estado podría permanecer igual.

Aquí está el código actualizado que resuelve el problema:

entity CombinationLockFSM is
    Port(
        mclk: in std_logic;
        sw: in std_logic_vector(7 downto 0);
        Led: out std_logic_vector(7 downto 0)
    );
end CombinationLockFSM;

architecture Behavioral of CombinationLockFSM is
    constant state_start: std_logic_vector(7 downto 0) := "10000000";
    constant state_1_right: std_logic_vector(7 downto 0) := "01000000";
    constant state_2_right: std_logic_vector(7 downto 0) := "00100000";
    constant state_3_right: std_logic_vector(7 downto 0) := "00010000";
    constant state_4_right: std_logic_vector(7 downto 0) := "11110000";
    constant state_error: std_logic_vector(7 downto 0) := "00001111";

    signal state: std_logic_vector(7 downto 0) := (others => '0');
begin

    Led <= state;

    process(mclk)
    begin
        if rising_edge(mclk) then
            case sw is
                when "00000000" => 
                    state <= state_start;
                when "10000000" => 
                    if state = state_start or state = state_1_right then
                        state <= state_1_right;
                    else
                        state <= state_error;
                    end if;
                when "11000000" => 
                    if state = state_1_right or state = state_2_right then
                        state <= state_2_right;
                    else
                        state <= state_error;
                    end if;
                when "11100000" => 
                    if state = state_2_right or state = state_3_right then
                        state <= state_3_right;
                    else
                        state <= state_error;
                    end if;
                when "11110000" => 
                    if state = state_3_right or state = state_4_right then
                        state <= state_4_right;
                    else
                        state <= state_error;
                    end if;


                when others =>
                    state <= state_error;
            end case;
        end if;

    end process;

end Behavioral;
Como tienes conectados los leds? Es una posibilidad remota, pero tal vez todo esté invertido, por ejemplo, si los LED dicen 10000000, en realidad es 01111111
@geometrikal LEDs está bien e incluso si fue al revés, el error es de 4 luces seguidas, por lo que realmente no puede estropearlo.

Respuestas (2)

La otra respuesta es correcta acerca de necesitar un reloj.

Pero ignore el ejemplo de dos procesos al que se vinculó: busque en los lugares habituales "máquina de estado de proceso único VHDL" para obtener una mejor solución.

http://www.openhdl.com/vhdl/664-vhdl-tip-single-process-vhdl-state-machine-design.html para uno.

Agregué un reloj y puse la declaración de borde ascendente alrededor de mi caso, pero no hay cambios.
¿No necesitaría Switches en su propia declaración de proceso? De lo contrario, pasaría directamente al estado de error.
@MLM: (1) ¿y "clk" es lo único en la lista de sensibilidad? debería ser. (2) Error en su lógica: habiendo llegado al Estado 1 con el patrón de interruptor correcto, debe permanecer allí hasta que cambien los interruptores. Dada esa pista, es un cambio fácil a la expresión "si". Lo mismo se aplica a otros estados...
@BrianDrummond Lo probé solo con el clk en la lista de sensibilidad y no hay cambios. Te quedas en state_1 hasta que te equivocas o vas a state_2. Aunque ni siquiera puedo llegar a state_1. Ni siquiera estoy seguro de que necesito un reloj...
Simularlo. La mejor manera de ver lo que realmente está haciendo... Hay varios simuladores gratuitos, incluido ghdl.
@BrianDrummond Acabo de simular y se produjo un error directo incluso con el reloj de sincronización. ¿Crees que podría ser porque el estado del que depende está en la "sentencia if" y luego se cambia si la "sentencia if" es verdadera? Podría ser una traducción extraña del código al esquema.
¿Puedes comprobar de nuevo? Si está cronometrado, cuando configura el interruptor en 1000... esperaría que pasara UN ciclo en el estado correcto, antes de fallar. Al menos, eso es lo que el código le pide que haga.
@BrianDrummond Está bien, cambia al estado correcto "01000000" para un ciclo de un reloj, pero ¿cómo es que no se queda allí?
Porque el código en esa declaración "si" le dice que no se quede allí.
@BrianDrummond ahhhhhhh, lo mencionaste en un comentario anterior, pero no tenía ningún sentido para mí en ese momento y especialmente porque no tenía el reloj antes de que marcara tanto el caso en lugar de solo los interruptores en la lista de sensibilidad. Publicaré el código actualizado en breve.
Está bien. El comentario anterior no tendría mucho sentido sin la capacidad de verlo en simulación. Creo que ha visto mucho más del proceso, en el camino para encontrar el problema, lo que será útil... Si observa otros SM, verá que el suyo está básicamente al revés: convencionalmente, el estado se prueba en la declaración del caso externo, pero es un código perfectamente válido para todo eso y muestra originalidad. Podría llevar a un uso más lógico (LUT, FF) en la síntesis, pero las herramientas de síntesis son bastante buenas en la optimización... ¡Sin embargo, todavía no he pensado en su inmunidad al rebote sw!

Quizás necesite un reloj para sincronizar los cambios de estado.

Podría ser un cambio de rebote, pero si el primero estuviera rebotando, parece que volvería a start_state, no a state_error, creo. ¿Estás usando interruptores físicos?

Reimprimo el código de ejemplo de aquí http://esd.cs.ucr.edu/labs/tutorial/fsm.vhd como ejemplo de sincronización con el reloj para los cambios de estado.

-----------------------------------------------------
-- VHDL FSM (Finite State Machine) modeling
-- (ESD book Figure 2.7)
-- by Weijun Zhang, 04/2001
--
-- FSM model consists of two concurrent processes
-- state_reg and comb_logic
-- we use case statement to describe the state 
-- transistion. All the inputs and signals are
-- put into the process sensitive list.  
-----------------------------------------------------

library ieee ;
use ieee.std_logic_1164.all;

-----------------------------------------------------

entity seq_design is
port(   a:      in std_logic;
    clock:      in std_logic;
    reset:      in std_logic;
    x:      out std_logic
);
end seq_design;

-----------------------------------------------------

architecture FSM of seq_design is

    -- define the states of FSM model

    type state_type is (S0, S1, S2, S3);
    signal next_state, current_state: state_type;

begin

    -- cocurrent process#1: state registers
    state_reg: process(clock, reset)
    begin

    if (reset='1') then
            current_state <= S0;
    elsif (clock'event and clock='1') then
        current_state <= next_state;
    end if;

    end process;                          

    -- cocurrent process#2: combinational logic
    comb_logic: process(current_state, a)
    begin

    -- use case statement to show the 
    -- state transistion

    case current_state is

        when S0 =>  x <= '0';
            if a='0' then
                next_state <= S0;
            elsif a ='1' then
                next_state <= S1;
            end if;

        when S1 =>  x <= '0';
            if a='0' then 
                next_state <= S1;
            elsif a='1' then 
                next_state <= S2;
            end if;

        when S2 =>  x <= '0';
            if a='0' then
                next_state <= S2;
            elsif a='1' then
                next_state <= S3;
            end if;

        when S3 =>  x <= '1';
            if a='0' then 
                next_state <= S3;
            elsif a='1' then 
                next_state <= S0;
            end if;

        when others =>
            x <= '0';
            next_state <= S0;

    end case;

    end process;

end FSM;

-----------------------------------------------------
Estoy usando interruptores físicos en el tablero. Buscaré agregar un reloj de sincronización, pero no estoy seguro de por qué lo necesitarías en esta situación.
Agregué un reloj y puse la declaración de borde ascendente alrededor de mi caso, pero no hay cambios.