【问题标题】:VHDL state machine is skipping statesVHDL 状态机正在跳过状态
【发布时间】:2016-07-11 21:13:39
【问题描述】:

我正在用 VHDL 开发一个状态机,但它似乎无法正常运行。设计如下图:

SHARED VARIABLE XM_INDEX : NATURAL RANGE 0 TO 99 := 0;
SIGNAL XM_STATE_INDICATOR : STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000000";
TYPE XM_STATE_TYPE IS (EMPTY, IDLE, POWER_UP, POWER_UP_CONFIRM, 
                       CHANNEL_SELECT, CHANNEL_SELECT_CONFIRM, VOLUME_CHANGE, 
                       VOLUME_CHANGE_CONFIRM, TRANSMIT_CHAR, TRANSMIT_CHAR_CONFIRM,
                       COMPLETED);
SIGNAL XM_CURRENT_STATE : XM_STATE_TYPE := EMPTY;
SIGNAL XM_NEXT_STATE : XM_STATE_TYPE := EMPTY;

XMStateMachineClock: PROCESS (CLK25, SYS_RST) IS
BEGIN
   IF (SYS_RST = '1') THEN
      XM_CURRENT_STATE <= EMPTY;
   ELSIF (RISING_EDGE(CLK25)) THEN
      XM_CURRENT_STATE <= XM_NEXT_STATE;
   END IF;               
END PROCESS XMStateMachineClock;

XMStateMachine: PROCESS (XM_CURRENT_STATE) IS
BEGIN
   -- Pend on current XM state
   CASE XM_CURRENT_STATE IS

      -- Empty: Debug only
      WHEN EMPTY =>
         XM_NEXT_STATE <= IDLE;
         XM_STATE_INDICATOR <= "00000001";

      -- Idle: Idle state
      WHEN IDLE =>
         IF XM_POWER_UP = '1' THEN
            XM_INDEX := 0;
            XM_NEXT_STATE <= POWER_UP;
            XM_STATE_INDICATOR <= "00000010";
         ELSE
            -- Remain in idle
            XM_NEXT_STATE <= IDLE;
            XM_STATE_INDICATOR <= "00000001";
         END IF;

      WHEN POWER_UP =>
         XM_NEXT_STATE <= TRANSMIT_CHAR;
         XM_STATE_INDICATOR <= "00000100";

      WHEN TRANSMIT_CHAR =>
         IF (XM_INDEX < 11) THEN
            XM_NEXT_STATE <= TRANSMIT_CHAR_CONFIRM;
            XM_STATE_INDICATOR <= "00001000";
         ELSE
            XM_NEXT_STATE <= COMPLETED;
            XM_STATE_INDICATOR <= "00000000";
         END IF;

      WHEN TRANSMIT_CHAR_CONFIRM =>
         XM_INDEX := XM_INDEX + 1;
         XM_NEXT_STATE <= TRANSMIT_CHAR;
         XM_STATE_INDICATOR <= "00000100";

      WHEN COMPLETED =>
         XM_NEXT_STATE <= COMPLETED;
         XM_STATE_INDICATOR <= "00000000";

      -- Default
      WHEN OTHERS =>

   END CASE;
END PROCESS XMStateMachine;

状态机的时钟频率为 25 MHz。根据我的理解,我的状态机应该在状态之间进行如下:

但是,当我连接逻辑分析仪时,我看到的是以下内容:

似乎状态机只在传输和传输确认状态之间交替一次,而不是应该的 11 次,我不知道为什么。

【问题讨论】:

  • 你应该让XMStateMachine成为一个时钟进程。

标签: vhdl state-machine


【解决方案1】:

如果你让XM_INDEX 一个信号有一个XM_INDEX_NEXT 被锁在你的XMStateMachineClock 进程中,然后将XM_INDEX := XM_INDEX + 1 更改为XM_INDEX_NEXT &lt;= XM_INDEX + 1。我相信这将解决您的问题。 XMStateMachine 还需要对XM_INDEX 敏感。

【讨论】:

  • 缺少MCVE,一个解释。如果 TRANSMIT_CHAR_CONFIRM 可以安全地用作锁存使能,则 xm_index := xm_index + 1;当 TRANSMIT_CHAR_CONFIRM 为真时将连续改变值存在组合循环。 xm_index 成为一个门控振荡器,通过 xm_index 的可能值波动。加法器延迟似乎在一个 clk25 中为您提供 xm_index > 11。如果敏感度列表中没有 xm_index,它甚至会明显正确地模拟。综合工具通常会报告组合循环。
  • 您也可以让 xm_index 成为时钟进程中分配的信号if xm_current_state = TRANSMIT_CHAR_CONFIRM then xm_index &lt;= xm_index + 1; end if; 请注意,除了 xm_index
  • 对于模拟,xm_index 应防止溢出(或将其设为无符号数组类型值并转换为未显示的其他用途 - 缺少 MCVE 的诅咒)。
  • xm_power_up 也应该在用于模拟的 xmstatemachine 进程的敏感度列表中。
【解决方案2】:

示例代码没有竞争,如果有多个进程写入它,从共享变量更改 xm_index 可能会打乱一些使用计划。您可以注意到,用户负责控制 -1993 共享变量中的独占访问。

通过提供完整的实体和架构对来创建MCVE

library ieee;
use ieee.std_logic_1164.all;

entity xm_sm is
    port (
        clk25:              in  std_logic;
        sys_rst:            in  std_logic;
        xm_power_up:        in  std_logic
    );
end entity;

architecture foo of xm_sm is

    -- shared variable xm_index:  natural range 0 to 99 := 0;
    signal xm_index:            natural range 0 to 99 := 0; -- CHANGED to SIGNAL
    signal xm_index_nxt:        natural range 0 to 99;  -- ADDED
    signal xm_state_indicator: std_logic_vector (7 downto 0) := "00000000";

    type xm_state_type is     (EMPTY, IDLE, POWER_UP, POWER_UP_CONFIRM, 
                               CHANNEL_SELECT, CHANNEL_SELECT_CONFIRM, 
                               VOLUME_CHANGE, VOLUME_CHANGE_CONFIRM, 
                               TRANSMIT_CHAR, TRANSMIT_CHAR_CONFIRM,
                               COMPLETED);
    signal xm_current_state:   xm_state_type := EMPTY;
    signal xm_next_state:      xm_state_type := EMPTY;

begin

xmstatemachineclock: 
    process (clk25, sys_rst) is
    begin
        if sys_rst = '1' then
            xm_current_state <= EMPTY;
            xm_index <= 0;  -- ADDED
        elsif rising_edge(clk25) then
            xm_current_state <= xm_next_state;
            xm_index <= xm_index_nxt;  -- ADDED
        end if;               
    end process xmstatemachineclock;

xmstatemachine: 
    process (xm_current_state, xm_power_up) is
    begin
       -- pend on current xm state
        case xm_current_state is

            -- empty: debug only
            when EMPTY =>
                xm_next_state <= IDLE;
                xm_state_indicator <= "00000001";

            -- idle: idle state
            when IDLE =>
                if xm_power_up = '1' then
                    xm_index_nxt <= 0;
                    xm_next_state <= POWER_UP;
                    xm_state_indicator <= "00000010";
                else
                    -- remain in idle
                    xm_next_state <= IDLE;
                    xm_state_indicator <= "00000001";
                end if;

            when POWER_UP =>
                xm_next_state <= TRANSMIT_CHAR;
                xm_state_indicator <= "00000100";

            when TRANSMIT_CHAR =>
                if xm_index < 11 then
                    xm_next_state <= TRANSMIT_CHAR_CONFIRM;
                    xm_state_indicator <= "00001000";
                else
                    xm_next_state <= COMPLETED;
                    xm_state_indicator <= "00000000";
                end if;

            when TRANSMIT_CHAR_CONFIRM =>
                if xm_index = 99 then   -- protect again overflow -- ADDED
                    xm_index_nxt <= 0;
                else
                    xm_index_nxt <= xm_index + 1;   -- CHANGED
                end if;
                -- xm_index_nxt <= xm_index + 1;
                xm_next_state <= TRANSMIT_CHAR;
                xm_state_indicator <= "00000100";

            when COMPLETED =>
                xm_next_state <= COMPLETED;
                xm_state_indicator <= "00000000";

            -- default
            when others =>

        end case;
    end process xmstatemachine;
end architecture;

这会将 xm_index 更改为一个信号,并包括 Alden 在他的回答中建议的下一个值。只要只有一个进程写入它,它就可以工作。 xm_index 现在在重置期间也设置为 0。此外,在 xm_currrent_state case 语句的 TRANSMIT_CHAR_CONFIRM 中,xm_index 理所当然地受到保护以防溢出。 xm_index 的范围(0 到 99)可以限制为最大值(11)。这让人怀疑我们没有看到所有的设计。

添加测试台:

library ieee;
use ieee.std_logic_1164.all;

entity xm_sm_tb is
end entity;

architecture foo of xm_sm_tb is
    signal clk25:       std_logic := '0';
    signal sys_rst:     std_logic := '0';
    signal xm_power_up: std_logic := '0';
begin
DUT:
    entity work.xm_sm
        port map (
            clk25 => clk25,
            sys_rst => sys_rst,
            xm_power_up => xm_power_up
        );
CLOCK:
    process 
    begin
        wait for 50 ns;
        clk25 <= not clk25;
        if now > 3.1 us then
            wait;
        end if;
    end process;
STIMULI:
    process
    begin
        wait for 100 ns;
        sys_rst <= '1';
        wait for 100 ns;
        sys_rst <= '0';
        wait for 200 ns;
        xm_power_up <= '1';
        wait for 100 ns;
        xm_power_up <= '0';
        wait;
    end process;
end architecture;

我们得到:

我们在完成之前查看所有索引值。

原始代码已成功模拟,但由于组合循环,似乎尚未合成为工作设计:

XM_INDEX := XM_INDEX + 1;

其中 xm_loop 由状态 TRANSMIT_CHAR_CONFIRM 的大概一个热状态表示锁存,作为锁存使能。

在模拟中,没有 xm_index 的敏感度列表会阻止加法器波纹递增 xm_index。如果 xm_index 已在进程敏感度列表中,它会在达到 100 后导致分配的边界检查违规。(整数类型不是模块化的,它们不会包装,也无法防止溢出)。

在没有看到控制台输出的综合中,我们可能会假设 ripple 时间足以在一个时钟时间内将 xm_index 的值可靠地推到 11 以上,而不会回绕到小于 11。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-23
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 2015-08-10
    相关资源
    最近更新 更多