首页 > 解决方案 > 创建有限状态机的麻烦

问题描述

我正在为我收到的任务创建状态机和 vhdl 代码。它涉及使用FPGA来控制其功能的机器。它有一个 100 MHZ 时钟,占空比为 50%。用户按下机器上的一个按钮,一个 LED 就会打开。一旦发生这种情况,机器就会开始在数据线上寻找数据序列(前导码)。一旦检测到前导码,伺服系统顺时针旋转到 90 度位置,保持向上 10 秒,之后整个系统复位。舵机的周期为 20ms,起始位置的占空比为 1.0ms,垂直位置的占空比为 1.5ms。前导序列为 1-0-1-0-0-0-0-1-0-1-0-0-0-0-0-0-1,位周期为 0.5 微秒。

自从我用 FPGA 或 VHDL 做任何事情以来已经有好几年了,所以目前这对我来说有点困难。我目前正在使用状态机,但由于状态的数量,我遇到了一些困难。我的状态机中有 20 个状态。S0 是初始状态,S1 是 LED 亮的状态,S2-S18 代表我正在检测前导码的状态,S19 是伺服旋转的状态,然后状态机回到初始状态。我不确定我是否正确地解决了这个问题,但我们将不胜感激任何帮助或建议。

FSM的图片

标签: vhdlfsmdigital-logic

解决方案


有限状态机 - 无移位寄存器

这就是我得到的。我不明白你从 S18 到 S19 的过渡。在我看来,前导码在 S18 中时已被检测到,因此不需要额外的检测转换。我还将您的 S19 分为 3 个状态,即 S18、S19 和 S20。

不带移位寄存器的状态图

有限状态机 - 带移位寄存器

这是用于检测前导序列的移位寄存器的状态图。S1 等待移位寄存器检测前导序列。

不带移位寄存器的状态图

FSM的VHDL实现

同步状态机

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity StateMachineChallenge is
    generic
    (
        NUM_STATES: natural := 5;
        NUM_OUTPUTS: natural := 6
    );
    port
    (
        clock: in std_logic;
        reset: in std_logic;
        button: in std_logic;
        found_preamble: in std_logic;
        timer_a_timeout: in std_logic;
        timer_b_timeout: in std_logic;
        timer_c_timeout: in std_logic;
        state: out natural range 0 to NUM_STATES := 0;
        outputs: out std_logic_vector(NUM_OUTPUTS - 1 downto 0) := (others => '0')
    );
end entity;

architecture V1 of StateMachineChallenge is

    constant S0: natural range 0 to NUM_STATES := 0;
    constant S1: natural range 0 to NUM_STATES := 1;
    constant S2: natural range 0 to NUM_STATES := 2;
    constant S3: natural range 0 to NUM_STATES := 3;
    constant S4: natural range 0 to NUM_STATES := 4;

    constant LED_OFF, FIND_PREAMBLE_OFF, TIMER_A_OFF, TIMER_B_OFF, TIMER_C_OFF: std_logic := '0';
    constant LED_ON, FIND_PREAMBLE_ON, TIMER_A_ON, TIMER_B_ON, TIMER_C_ON: std_logic := '1';

    constant SERVO_POSITION_START: std_logic := '0';  -- 1.0 ms
    constant SERVO_POSITION_90DEG: std_logic := '1';  -- 1.5 ms

    type TOutputsTable is array(0 to NUM_STATES - 1) of std_logic_vector(NUM_OUTPUTS - 1 downto 0);
    constant OUTPUTS_TABLE: TOutputsTable :=
    (
        -- LED, Find preamble, Servo position, Timer A run, Timer B run, Timer C run
        (LED_OFF, FIND_PREAMBLE_OFF, SERVO_POSITION_START, TIMER_A_OFF, TIMER_B_OFF, TIMER_C_OFF),
        (LED_ON, FIND_PREAMBLE_ON, SERVO_POSITION_START, TIMER_A_OFF, TIMER_B_OFF, TIMER_C_OFF),
        (LED_ON, FIND_PREAMBLE_OFF, SERVO_POSITION_90DEG, TIMER_A_ON, TIMER_B_OFF, TIMER_C_OFF),
        (LED_ON, FIND_PREAMBLE_OFF, SERVO_POSITION_90DEG, TIMER_A_OFF, TIMER_B_ON, TIMER_C_OFF),
        (LED_ON, FIND_PREAMBLE_OFF, SERVO_POSITION_START, TIMER_A_OFF, TIMER_B_OFF, TIMER_C_ON)
    );
    signal next_state: natural range 0 to NUM_STATES := S0;
    signal next_outputs: std_logic_vector(NUM_OUTPUTS - 1 downto 0) := OUTPUTS_TABLE(S0);

begin
    --
    -- State register and outputs register.
    --
    process(clock, reset)
    begin
        if reset = '1' then
            state <= S0;
            outputs <= OUTPUTS_TABLE(S0);
        elsif rising_edge(clock) then
            state <= next_state;
            outputs <= next_outputs;
        end if;
    end process;

    --
    -- Next state logic
    --
    process(reset, state, button, found_preamble, timer_a_timeout, timer_b_timeout, timer_c_timeout)
    begin
        if reset = '1' then
            next_state <= S0;
        else
            case state is
                when S0 =>  -- Reset
                    if button = '1' then
                        next_state <= S1;
                    else
                        next_state <= S0;
                    end if;
                when S1 =>  -- Looking for preamble
                    if found_preamble then
                        next_state <= S2;
                    else
                        next_state <= S1;
                    end if;
                when S2 =>  -- Moving servo to 90 degrees
                    if timer_a_timeout then
                        next_state <= S3;
                    else
                        next_state <= S2;
                    end if;
                when S3 =>  -- Waiting for 10 seconds
                    if timer_b_timeout then
                        next_state <= S4;
                    else
                        next_state <= S3;
                    end if;
                when S4 =>  -- Moving servo to start position
                    if timer_c_timeout then
                        next_state <= S0;
                    else
                        next_state <= S4;
                    end if;
                when others =>
                    next_state <= S0;
            end case;
        end if;
    end process;

    --
    -- Next outputs logic
    --
    process(reset, next_state)
    begin
        if reset = '1' then
            next_outputs <= OUTPUTS_TABLE(S0);
        else
            next_outputs <= OUTPUTS_TABLE(next_state);
        end if;
    end process;

end architecture;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity TestBench is
end entity;

architecture V1 of TestBench is

    constant SYS_CLOCK_FREQ: real := 100000000.0;  -- Hz
    constant SYS_CLOCK_PERIOD: time := 1.0 sec / SYS_CLOCK_FREQ;

    signal halt_sys_clock: boolean := false;
    signal sys_clock: std_logic;
    signal reset: std_logic;

    signal button: std_logic;
    signal found_preamble:  std_logic;
    signal timer_a_timeout: std_logic;
    signal timer_b_timeout: std_logic;
    signal timer_c_timeout: std_logic;
    
    constant NUM_STATES: natural := 5;
    constant NUM_OUTPUTS: natural := 6;
    
    signal state: natural range 0 to NUM_STATES;

    signal outputs: std_logic_vector(NUM_OUTPUTS - 1 downto 0) := (others => '0');

    signal led: std_logic;
    signal find_preamble: std_logic;
    signal servo_position: std_logic;
    signal timer_a_enable: std_logic;
    signal timer_b_enable: std_logic;
    signal timer_c_enable: std_logic;

    component StateMachineChallenge is
        generic
        (
            NUM_STATES: natural := NUM_STATES;
            NUM_OUTPUTS: natural := NUM_OUTPUTS
        );
        port
        (
            clock: in std_logic;
            reset: in std_logic;
            button: in std_logic;
            found_preamble: in std_logic;
            timer_a_timeout: in std_logic;
            timer_b_timeout: in std_logic;
            timer_c_timeout: in std_logic;
            state: out natural range 0 to NUM_STATES := 0;
            outputs: out std_logic_vector(NUM_OUTPUTS - 1 downto 0) := (others => '0')
        );
    end component;
    
begin

    SysClockGenerator: process
    begin
        while not halt_sys_clock loop
            sys_clock <= '1';
            wait for SYS_CLOCK_PERIOD / 2.0;
            sys_clock <= '0';
            wait for SYS_CLOCK_PERIOD / 2.0;
        end loop;
        wait;
    end process SysClockGenerator;
    
    ResetProcess: process
    begin
        reset <= '0';
        wait for 1 ns;
        reset <= '1';
        wait for 10 ns;
        reset <= '0';
        --wait for 21 ms;
        wait for 1 ms;
        halt_sys_clock <= true;
        wait;
    end process ResetProcess;
    
    ButtonPress: process
    begin
        button <= '0';
        wait for 1 us;
        button <= '1';
        wait for 1 us;
        button <= '0';
        wait;
    end process ButtonPress;
    
    FindPreamble: process(sys_clock)
        variable count: natural := 0;
    begin
        if rising_edge(sys_clock) and find_preamble = '1' then
            count := count + 1;
        end if;
        if count = 17 * 50 then
            found_preamble <= '1';
        else
            found_preamble <= '0';
        end if;
    end process FindPreamble;
    
    TimerA: process(sys_clock)
        variable count: natural := 0;
    begin
        if rising_edge(sys_clock) and timer_a_enable = '1' then
            count := count + 1;
        end if;
        if count = 200 then
            timer_a_timeout <= '1';
        else
            timer_a_timeout <= '0';
        end if;
    end process TimerA;
    
    TimerB: process(sys_clock)
        variable count: natural := 0;
    begin
        if rising_edge(sys_clock) and timer_b_enable = '1' then
            count := count + 1;
        end if;
        if count = 1000 then
            timer_b_timeout <= '1';
        else
            timer_b_timeout <= '0';
        end if;
    end process TimerB;
    
    TimerC: process(sys_clock)
        variable count: natural := 0;
    begin
        if rising_edge(sys_clock) and timer_c_enable = '1' then
            count := count + 1;
        end if;
        if count = 200 then
            timer_c_timeout <= '1';
        else
            timer_c_timeout <= '0';
        end if;
    end process TimerC;
    
    DUT: StateMachineChallenge
        generic map
        (
            NUM_STATES => NUM_STATES,
            NUM_OUTPUTS => NUM_OUTPUTS
        )
        port map
        (
            clock => sys_clock,
            reset => reset,
            button => button,
            found_preamble => found_preamble,
            timer_a_timeout => timer_a_timeout,
            timer_b_timeout => timer_b_timeout,
            timer_c_timeout => timer_c_timeout,
            state => state,
            outputs => outputs
        );
    
    (led, find_preamble, servo_position, timer_a_enable, timer_b_enable, timer_c_enable) <= outputs;
        
end architecture;

时钟时序

时钟时序

VHDL 生成时钟选通信号

--
-- Clock Strobe
--
-- Generates a slow clock strobe from a fast clock.
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ClockStrobe is
    generic
    (
        max_count: natural
    );
    port
    (
        clock: in std_logic;
        clock_strobe: out std_logic
    );
end;

architecture V1 of ClockStrobe is
begin
    -- Create clock strobe.
    process(clock)
        variable counter: natural range 0 to max_count := 0;
    begin
        if rising_edge(clock) then
            counter := counter + 1;
            if counter = max_count then
                clock_strobe <= '1';
                counter := 0;
            else
                clock_strobe <= '0';
            end if;
        end if;
    end process;

end architecture;

--
-- Test Bench
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity TestBench is
end entity;

architecture V1 of TestBench is

    constant SYS_CLOCK_FREQ: real := 100000000.0;  -- Hz
    constant SYS_CLOCK_PERIOD: time := 1.0 sec / SYS_CLOCK_FREQ;

    constant MAX_COUNT_2MHz: natural := 50;
    constant MAX_COUNT_50Hz: natural := 2000000;

    signal stop_clock: boolean := false;
    signal clock: std_logic;
    signal clock_strobe_2MHz: std_logic;
    signal clock_strobe_50Hz: std_logic;

    component ClockStrobe is
        generic
        (
            max_count: natural
        );
        port
        (
            clock: in std_logic;
            clock_strobe: out std_logic
        );
    end component;
    
begin

    ClockGenerator: process
    begin
        while not stop_clock loop
            clock <= '0';
            wait for SYS_CLOCK_PERIOD / 2.0;
            clock <= '1';
            wait for SYS_CLOCK_PERIOD / 2.0;
        end loop;
        wait;
    end process ClockGenerator;

    ClockStrobe2MHz: ClockStrobe
        generic map
        (
            max_count => MAX_COUNT_2MHz
        )
        port map
        (
            clock => clock,
            clock_strobe => clock_strobe_2MHz
        );

    ClockStrobe50Hz: ClockStrobe
        generic map
        (
            max_count => MAX_COUNT_50Hz
        )
        port map
        (
            clock => clock,
            clock_strobe => clock_strobe_50Hz
        );

    -- Preamble process.
    process(clock)
    begin
        if rising_edge(clock) then
            if clock_strobe_2MHz then
                -- Process the next preamble bit.
            end if;
        end if;
    end process;

    -- Servo process.
    process(clock)
    begin
        if rising_edge(clock) then
            if clock_strobe_50Hz then
                -- Process the servo.
            end if;
        end if;
    end process;

end architecture;

模拟

仅显示 2 MHz 频闪。 模拟显示 2 MHz 频闪


推荐阅读