vhdl - 创建有限状态机的麻烦
问题描述
我正在为我收到的任务创建状态机和 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 是伺服旋转的状态,然后状态机回到初始状态。我不确定我是否正确地解决了这个问题,但我们将不胜感激任何帮助或建议。
解决方案
有限状态机 - 无移位寄存器
这就是我得到的。我不明白你从 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;
模拟
推荐阅读
- java - JavaFX 2 媒体播放器 - 搜索功能不准确或根本不起作用
- javascript - 每次加载适当的页面时,如何使脚本仅重定向一次?
- git - 是否存在您将重新设置本地仓库然后推送而不是推送力的情况?
- node.js - Azure Web App Node 部署,更改应用根目录?
- java - 有没有更好的方法来定期更新 UI
- android - 如何用 mockk 模拟密封类?
- xslt - 如何使用变量遍历节点并跳过具有相同值的重复节点
- cluster-computing - SLURM - 我如何确定作业正在使用哪些特定 CPU?
- c++ - 制作文本文件,逐个字符串输入
- c# - 将pdf转换为jpeg时出现PDFClown解析错误