这是一个通用的问题,因为我能够理解有限状态机的基础知识。假设我有四个状态s0-s3,其中 FSM将自动从' s0开始。通电后。经过一段确定的延迟后,FSM应进入“s1' - 其他州也一样。 不同状态之间的延迟是不一样的。
例如:
加电 - > ' S0' - > 100毫秒 - > ' S1' - > 50我们 - > ' S2' - > 360 us - > ' S3' - > ' S3'
在C语言的过程语言中,我只是调用一个延迟例程,其中一个参数是所需的延迟并且用它来完成。
如何优雅地实现这种FSM?
最好的, 克里斯
答案 0 :(得分:3)
我的模式:一个延迟计数器,每个状态转换可以在需要时编程,即在每个新延迟开始时编程。
尽管有些工具(特别是Synplicity)在准确的时间计算方面存在问题,除非你的时钟周期是整数个纳秒,否则这一切都是可以合成的。有关此错误的更多信息,请参阅this Q&A。如果你遇到这种情况,那么神奇的数字(32000而不是Synplicity在该问题中计算的32258)可能是最简单的解决方法。将它作为(简单)练习留在实体/架构中。
-- first, some declarations for readability instead of magic numbers
constant clock_period : time := 10 ns;
--WARNING : Synplicity has a bug : by default it rounds to nanoseconds!
constant longest_delay : time := 100 ms;
subtype delay_type is natural range 0 to longest_delay / clock_period;
constant reset_delay : delay_type := 100 ms / clock_period - 1;
constant s1_delay : delay_type := 50 us / clock_period - 1;
constant s2_delay : delay_type := 360 us / clock_period - 1;
-- NB take care to avoid off-by-1 error!
type state_type is (s0, s1, s2, s3);
-- now the state machine declarations:
signal state : state_type;
signal delay : delay_type;
-- now the state machine itself:
process(clock, reset) is
begin
if reset = '1' then
state <= s0;
delay <= reset_delay;
elsif rising_edge(clock) then
-- default actions such as default outputs first
-- operate the delay counter
if delay > 0 then
delay <= delay - 1;
end if;
-- state machine proper
case state is
when s0 =>
-- do nothing while delay counts down
if delay = 0 then
--start 50us delay when entering S1
delay <= s1_delay;
state <= s1;
end if;
when s1 =>
if delay = 0 then
delay <= s2_delay;
state <= s2;
end if;
when s2 =>
if delay = 0 then
state <= s3;
end if;
when others =>
null;
end case;
end if;
end process;
答案 1 :(得分:1)
您可以使用时钟分频器和计数器的组合。了解设备的时钟速度。你提到的所有延迟都是10us的因素,所以我将使用时钟分频器来达到这个速度。我们假设您设备的原始时钟速度为50MHz。你需要找出你需要计算到10us的循环次数。以下计算是这样的:
# of cycles = 10ms * 50MHz = 5000 cycles
所以你需要一个计数到5000的计数器。一个粗略的例子如下:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity new_clk is
Port (
clk_in : in STD_LOGIC; -- your 50MHZ device clock
reset : in STD_LOGIC;
clk_out: out STD_LOGIC -- your new clock with a 10us period
);
end clk200Hz;
architecture Behavioral of new_clk is
signal temporal: STD_LOGIC;
signal counter : integer range 0 to 4999 := 0;
begin
clk_div: process (reset, clk_in) begin
if (reset = '1') then
temporal <= '0';
counter <= 0;
elsif rising_edge(clk_in) then
if (counter = 4999) then
temporal <= NOT(temporal);
counter <= 0;
else
counter <= counter + 1;
end if;
end if;
end process;
clk_out <= temporal;
end Behavioral;
注意计数器如何从0到4999.信号clk_out现在的周期为10us。您现在可以使用它来生成延迟。
例如,对于360us延迟,计算clk_out信号的36个周期。代码将与上面的代码大致相似,但这次你计算的是clk_out而你的计数器只是从0到35。
(我可以稍后再添加,但这应该让你开始。)
答案 2 :(得分:0)
检查“硬件中的有限状态机:理论与设计(使用VHDL和SystemVerilog)”的第8-9章麻省理工学院出版社,2013年,详细讨论了任何案例和许多完整的例子。
答案 3 :(得分:-1)
主要区别是将固定上限移除到延迟长度:它现在仅受“延迟”SIGNAL类型的长度限制 - 通常可以根据需要进行。
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
ENTITY Test123 IS
PORT (
clk_in1 : IN std_logic := '0';
rst1, en1 : IN std_logic;
);
END ENTITY Test123;
ARCHITECTURE Test123_Arch OF Test123 IS
-- first, some declarations for readability instead of magic numbers
CONSTANT clock_period : TIME := 20 ns; -- 50 MHz
--WARNING : Synplicity has a bug : by default it rounds to nanoseconds!
CONSTANT reset_delay : TIME := 100 ms - clock_period;
CONSTANT s1_delay : TIME := 50 us - clock_period;
CONSTANT s2_delay : TIME := 360 us - clock_period;
-- NB take care to avoid off-by-1 error!
-- now the state machine declarations:
TYPE state_type IS (s0, s1, s2, s3);
SIGNAL state : state_type;
--
--signal delay : unsigned(47 downto 0) := (others => '0'); -- a 48-Bit 'unsigned' Type, Along a 50-MHz Clock, Evaluates To an Upper-Limit of ~90,071,992.5474 Seconds.
SIGNAL delay : NATURAL := 0; -- a 'natural' Type, Along a 50-MHz Clock, Evaluates To an Upper-Limit of ~85.8993459 Seconds.
--
FUNCTION time_to_cycles(time_value : TIME; clk_period : TIME) RETURN NATURAL IS
BEGIN
-- RETURN TO_UNSIGNED((time_value / clk_period), 48); -- Return a 48-Bit 'unsigned'
RETURN (time_value / clk_period); -- Return a 32-Bit 'natural'
END time_to_cycles;
--
BEGIN
-- now the state machine itself:
sm0 : PROCESS (clk_in1, rst1)
BEGIN
IF (rst1 = '1') THEN
state <= s0;
delay <= time_to_cycles(reset_delay, clock_period);
ELSIF rising_edge(clk_in1) THEN
-- default actions such as default outputs first
-- operate the delay counter
IF (delay > 0) THEN
delay <= delay - 1;
END IF;
-- state machine proper
CASE state IS
WHEN s0 =>
-- do nothing while delay counts down
IF (delay = 0) THEN
--start 50us delay when entering S1
delay <= time_to_cycles(s1_delay, clock_period);
state <= s1;
END IF;
WHEN s1 =>
IF (delay = 0) THEN
delay <= time_to_cycles(s2_delay, clock_period);
state <= s2;
END IF;
WHEN s2 =>
IF (delay = 0) THEN
state <= s3;
END IF;
WHEN OTHERS =>
NULL;
END CASE;
END IF;
END PROCESS;
END ARCHITECTURE Test123_Arch;