如何在不接受来自 Basysy3 FPGA 的多个输入的情况下将有限状态机正确实现到 VHDL 中 [英] How do I correctly implement a Finite-State Machine into VHDL without taking in multiple inputs from Basysy3 FPGA

查看:32
本文介绍了如何在不接受来自 Basysy3 FPGA 的多个输入的情况下将有限状态机正确实现到 VHDL 中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<预><代码>图书馆 IEEE;使用 ieee.std_logic_1164.all;实体垃圾桶是港口 (时钟:在 STD_LOGIC 中;P : IN STD_LOGIC;重置:在 STD_LOGIC 中;LED3, LED1,LED2,LED0 : OUT STD_LOGIC);结束实体;-- SimpleFSM 实体的架构定义Trasher 的架构 RTL 是TYPE State_type IS (A, B, C, D);-- 定义状态信号状态:State_Type;-- 创建一个使用的信号——不同的状态开始进程(时钟,复位)开始IF (reset = '1') THEN -- 重置时,将状态设置为 A状态 <= A;ELSIFrising_edge(clock) THEN -- 如果有上升沿-- 时钟,然后做下面的事情-- CASE 语句检查 State 变量的值,-- 并基于值和任何其他控制信号,更改——进入一个新的状态.案例状态是-- 如果当前状态为 A 且 P 设置为 1,则-- 下一个状态是 B当 A =>如果 P='1' 那么状态 <= B;万一;-- 如果当前状态为 B 且 P 设置为 1,则-- 下一个状态是 C当 B =>如果 P='1' 那么状态 <= C;万一;-- 如果当前状态为 C 且 P 设置为 1,则-- 下一个状态是 D当 C =>如果 P='1' 那么状态 <= D;万一;-- 如果当前状态为 D 且 P 设置为 1,则-- 下一个状态是 B.-- 如果当前状态为 D 且 P 设置为 0,则——下一个状态是A.当 D=>时如果 P='1' 那么状态 <= B;别的状态 <= A;万一;当其他人 =>状态 <= A;结束案例;万一;结束过程;-- 解码当前状态以创建输出-- 如果当前状态为 D,则 R 为 1,否则 R 为 0LED0 <= '1' 当状态 = A ELSE '0';LED1 <= '1' 时状态 = B ELSE '0';LED2 <= '1' 时状态 = C ELSE '0';LED3 <= '1' 当状态 = D ELSE '0';结束 rtl;

解决方案

不要直接使用来自按钮的输入.您需要为状态机提供信号的是 P 上升沿检测器的输出,而不是 P 本身.

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

信号同步:std_ulogic_vector(0 to 2);信号 button_pressed: std_ulogic;...过程(时钟,重置)开始如果重置 = '1' 那么同步 <= (其他 => '0');elsif 上升沿(时钟)然后同步 <= P &同步(0 到 1);万一;结束过程;button_pressed <= sync(1) and (not sync(2));

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

当按下按钮时,它们会在 sync 中移动.在两个时钟周期后 sync = "110" 所以 button_pressed 被置为高电平.一个时钟周期后 sync = "111"button_pressed 被取消断言.button_pressed 因此是一个只有一个时钟周期的指示器,表明按钮被按下.您可以将其用作状态机的输入.

第二个问题来自按钮的工作方式.如果您的原型板还没有消除按钮的抖动,那么当按下按钮时,您的 P 输入会在 0 和 1 之间多次振荡,然后稳定到 1.释放.由于有时情况并非如此,因此在实施去抖器之前进行一些测试.例如,计算 button_pressed 被置为高电平的次数并将其发送到您的 LED:

signal cnt: u_unsigned(3 downto 0);...过程(时钟,重置)开始如果重置 = '1' 那么cnt <= (其他 => '0');elsif 上升沿(时钟)然后cnt <= cnt + button_pressed;万一;结束过程;LED0 <= std_logic(cnt(0));LED1 <= std_logic(cnt(1));LED2 <= std_logic(cnt(2));LED3 <= std_logic(cnt(3));

如果您的按钮弹跳,您有时会在按下它时看到不止一个增量.是时候搜索一些关于去抖动的信息,并在需要时提出一个新问题.

I am new to VHDL and I am attempting to implement the following state machine into VHDL (state diagram provided below). When I press a button on my Basys3 FPGA board( P input) the output is a random state. I suspect this is because the clock is going through many cycles during a single press so more than 1 input is being taken in from a single press but I am unsure. Is there anything I can do to fix this. I want to be able to press button P and the states change one at a time.


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;

解决方案

Do not use directly the input from your press-button. What you need to feed your state machine is the output of a rising edge detector of P, not P itself.

Moreover P is not synchronous with your master clock and there is thus a risk of meta-stability. Last but not least, if it bounces, you will get several value changes instead of just one. To solve the meta-stability issue you need a re-synchronizer, which is just a shift register. And you can also use it to generate an intermediate signal that is asserted high during only one clock period when the button is pressed, that is, the rising edge detector you need for your state machine. Example with 3-stages:

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));

Stages 1 and 2 of sync are safe to use because they have already been resynchronized (assuming 2 stages are enough for your target technology and mean time between failures; read something about meta-stability, maybe, if you don't understand this).

When the button is pressed, ones are shifted in sync. After two clock periods sync = "110" so button_pressed is asserted high. One clock period later sync = "111" and button_pressed is de-asserted. button_pressed is thus a one-clock-period-only indicator that the button was pressed. You can use it as an input of your state machine.

The second problem comes from the way press-buttons work. If your prototyping board does not already debounce its press-buttons it can be that, when the button is pressed, your P input oscillates several times between 0 and 1 before stabilizing to 1. Same when the button is released. As this is sometimes not the case do some tests before implementing a debouncer. For instance, count the number of times button_pressed is asserted high and send this to your LEDs:

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));

If your button bounces you should sometimes see more than one increment when you press it. It will be time to search a bit about debouncing and, if needed, to ask a new question.

这篇关于如何在不接受来自 Basysy3 FPGA 的多个输入的情况下将有限状态机正确实现到 VHDL 中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆