如何在不从 Basysy3 FPGA 获取多个输入的情况下将有限状态机正确地实现到 VHDL 中

How do I correctly implement a Finite-State Machine into VHDL without taking in multiple inputs from Basysy3 FPGA

我是 VHDL 的新手,我正在尝试将以下状态机实现到 VHDL 中(下面提供的状态图)。当我按下 Basys3 FPGA 板上的按钮(P 输入)时,输出是随机状态。我怀疑这是因为时钟在一次按下时经历了很多周期,所以从一次按下中接收了不止 1 个输入,但我不确定。我能做些什么来解决这个问题。我希望能够按下按钮 P,状态一次改变一个。


library IEEE;
USE ieee.std_logic_1164.all;

ENTITY trasher is
PORT (
      clock :   IN STD_LOGIC;
      P     :   IN STD_LOGIC;
      reset :   IN STD_LOGIC;
      LED3, LED1,LED2,LED0     :    OUT STD_LOGIC);
END ENTITY;

-- Architecture definition for the SimpleFSM entity
Architecture RTL of trasher is
TYPE State_type IS (A, B, C, D);  -- Define the states
    SIGNAL State : State_Type;    -- Create a signal that uses 
                                  -- the different states

BEGIN 
  PROCESS (clock, reset) 
  BEGIN 
    IF (reset = '1') THEN            -- upon reset, set the state to A
        State <= A;
 
    ELSIF rising_edge(clock) THEN    -- if there is a rising edge of the
                             -- clock, then do the stuff below
 
        -- The CASE statement checks the value of the State variable,
        -- and based on the value and any other control signals, changes
        -- to a new state.
        CASE State IS
            -- If the current state is A and P is set to 1, then the
            -- next state is B
            WHEN A => 
                IF P='1' THEN 
                    State <= B; 
                END IF; 
            -- If the current state is B and P is set to 1, then the
            -- next state is C
            WHEN B => 
                IF P='1' THEN 
                    State <= C; 
                END IF; 
            -- If the current state is C and P is set to 1, then the
            -- next state is D
            WHEN C => 
                IF P='1' THEN 
                    State <= D; 
                END IF; 
            -- If the current state is D and P is set to 1, then the
            -- next state is B.
            -- If the current state is D and P is set to 0, then the
            -- next state is A.
            WHEN D=> 
                IF P='1' THEN 
                    State <= B; 
                ELSE 
                    State <= A; 
                END IF; 
            WHEN others =>
                State <= A;
        END CASE; 
    END IF; 
  END PROCESS;
  
  -- Decode the current state to create the output
  -- if the current state is D, R is 1 otherwise R is 0

  LED0 <= '1' WHEN State=A ELSE '0';
  LED1 <= '1' WHEN State=B ELSE '0';
  LED2 <= '1' WHEN State=C ELSE '0';
  LED3 <= '1' WHEN State=D ELSE '0';
END rtl;

不要直接使用按键输入。您需要为状态机提供的是 P 上升沿检测器的输出,而不是 P 本身。

此外 P 与您的主时钟不同步,因此存在元稳定性的风险。最后但并非最不重要的一点是,如果它反弹,您将获得多个值更改,而不仅仅是一个。要解决元稳定性问题,您需要一个重新同步器,它只是一个移位寄存器。您还可以使用它来生成一个中间信号,该信号在按下按钮时仅在一个时钟周期内被置为高电平,即您的状态机所需的上升沿检测器。 3 阶段示例:

signal sync: std_ulogic_vector(0 to 2);
signal button_pressed: std_ulogic;
...
process(clock, reset)
begin
  if reset = '1' then
    sync <= (others => '0');
  elsif rising_edge(clock) then
    sync <= P & sync(0 to 1);
  end if;
end process;
    
button_pressed <= sync(1) and (not sync(2));

sync 的第 1 和第 2 阶段可以安全使用,因为它们已经重新同步(假设 2 个阶段足以满足您的目标技术和平均故障间隔时间;阅读有关元稳定性的内容,也许,如果你不明白这一点)。

当按下按钮时,将移动 sync。在两个时钟周期后 sync = "110" 所以 button_pressed 被置为高电平。一个时钟周期后 sync = "111"button_pressed 被取消断言。 button_pressed 因此是按钮被按下的仅一个时钟周期的指示符。您可以将其用作状态机的输入。

第二个问题来自按钮的工作方式。如果您的原型板尚未对其按钮进行去抖动,则可能是,当按下按钮时,您的 P 输入在稳定为 1 之前在 0 和 1 之间振荡几次。释放按钮时也是如此。由于有时情况并非如此,因此在实施去抖动器之前进行一些测试。例如,计算 button_pressed 被断言为高电平的次数并将其发送到您的 LED:

signal cnt: u_unsigned(3 downto 0);
...
process(clock, reset)
begin
  if reset = '1' then
    cnt <= (others => '0');
  elsif rising_edge(clock) then
    cnt <= cnt + button_pressed;
  end if;
end process;

LED0 <= std_logic(cnt(0));
LED1 <= std_logic(cnt(1));
LED2 <= std_logic(cnt(2));
LED3 <= std_logic(cnt(3));

如果您的按钮弹起,您有时会在按下它时看到不止一个增量。是时候搜索一些关于去抖动的信息了,如果需要的话,再问一个新问题。