具有多个延迟的 VHDL 状态机 - 最佳方法?

VHDL state machine with several delays - best approach?

自从我能够理解有限状态机的基础知识以来,这个问题一直困扰着我。假设我有四个状态 s0 - s3,其中 FSM 将在 's0' 上电后自动启动。在一些定义的延迟之后,FSM 应进入 's1' - 其他状态也是如此。 不同状态之间的延迟是不一样的。

例如:

上电 -> 's0' -> 100 毫秒 -> 's1' -> 50 微秒 -> 's2' -> 360 微秒 -> 's3' -> 's3'

在像 C 这样的过程语言中,我只需调用一个延迟例程,其中一个参数是所需的延迟,然后就可以完成了。

如何优雅地实现这种 FSM?

最好的, 克里斯

您可以结合使用时钟分频器和计数器。找出您设备上的时钟速度是多少。您提到的所有延迟都可以分解为 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。

(稍后我可以添加更多内容,但这应该可以帮助您入门。)

我的模式:一个延迟计数器,每个状态转换都可以根据需要进行编程,即在每个新延迟开始时。

虽然某些工具(特别是 Synplicity)在准确计算时间方面存在问题,但它们都是可综合的,除非您的时钟周期是整数纳秒。有关此错误的更多信息,请参阅 this Q&A。如果您 运行 遇到这种情况,幻数(32000 而不是 Synplicity 在该问题中计算出的 32258)可能是最简单的解决方法。

将其包裹在 entity/architecture 中作为(简单的)练习。

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

查看 "Finite State Machines in Hardware: Theory and Design (with VHDL and SystemVerilog)" MIT Press,2013 年的第 8-9 章,了解涵盖任何案例和许多完整示例的详细讨论。

Brian Drummond 精彩回答的小颂歌:谢谢 Brian! :)

主要区别在于删除了延迟长度的固定上限:现在仅受'delay'信号类型长度的限制——通常可以根据需要设置长度。

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;