Проблема создания конечного автомата - PullRequest
0 голосов
/ 11 февраля 2020

Я работаю над созданием конечного автомата и кода VHDL для задачи, которую мне дали. Он включает в себя машину, которая использует ПЛИС для управления своими функциями. Он имеет тактовую частоту 100 МГц с коэффициентом заполнения 50%. Пользователь нажимает кнопку на машине, и светодиод включается. Как только это происходит, машина начинает искать последовательность данных (преамбулу) в строке данных. Как только преамбула обнаружена, сервопривод вращается по часовой стрелке до положения 90 градусов, остается в течение 10 сек c, после чего вся система перезагружается. Серво имеет период 20 мс, начальное положение имеет рабочий цикл 1,0 мс, а вертикальное положение имеет рабочий цикл 1,5 мс. Последовательность преамбулы составляет 1-0-1-0-0-0-0-1-0-1-0-0-0-0-0-0-1 с битовым периодом 0,5 микросекунды.

Прошло несколько лет с тех пор, как я что-то делал с FPGA или VHDL, так что сейчас это немного сложно для меня. В настоящее время я работаю над конечным автоматом, но у меня возникли некоторые трудности из-за количества штатов. У меня 20 состояний в моей государственной машине. S0 - это начальное состояние, S1 - это состояние с включенным светодиодом, S2-S18 - это состояния, в которых я обнаруживаю преамбулу, S19 - это состояние, в котором сервопривод вращается, и затем конечный автомат возвращается в исходное состояние. Я не уверен, правильно ли я поступаю по этому поводу, но любая помощь или предложения будут оценены.

Picture of FSM

1 Ответ

0 голосов
/ 12 февраля 2020

Конечный автомат - без регистра сдвига

Вот что я получил. Я не понял ваш переход с S18 на S19. Мне кажется, что преамбула была обнаружена, когда она находится на этапе S18, поэтому нет необходимости в дополнительном переходе обнаружения. Я также разделил ваш S19 на 3 состояния, то есть S18, S19 и S20.

State diagram without shift register

Конечный автомат - с регистром сдвига

Вот диаграмма состояний для сдвигового регистра, используемого для обнаружения последовательности преамбулы. S1 ожидает сдвиговый регистр для определения последовательности преамбулы.

State diagram without shift register

VHDL Реализация FSM

Synchronous State Machine

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;

Синхронизация часов

Clock Timings

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 МГц. Simulation showing 2 MHz strobe

...