如何在不从 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));
如果您的按钮弹起,您有时会在按下它时看到不止一个增量。是时候搜索一些关于去抖动的信息了,如果需要的话,再问一个新问题。
我是 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));
如果您的按钮弹起,您有时会在按下它时看到不止一个增量。是时候搜索一些关于去抖动的信息了,如果需要的话,再问一个新问题。