VHDL state machine is skipping states

时间:2016-07-11 21:13:40

标签: vhdl state-machine

I am developing a state machine in VHDL and it doesn't' seem to be functioning properly. The design is shown below:

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;

The state machine is being clocked at 25 MHz. Per my understanding, my state machine should progress between the states as follows:

enter image description here

However, what I see when I hook up my logic analyzer is the following:

enter image description here

It seems as if the state machine is only alternating between the transmit and transmit confirm states once, as opposed to the 11 times that is should, and I cannot figure out why.

2 个答案:

答案 0 :(得分:1)

If you make XM_INDEX a signal have an XM_INDEX_NEXT that is latched in your XMStateMachineClock process and then change XM_INDEX := XM_INDEX + 1 to XM_INDEX_NEXT <= XM_INDEX + 1. I believe that this will fix your issue. XMStateMachine will also need to be sensitive to XM_INDEX.

答案 1 :(得分:1)

示例代码没有竞争,并且有一些机会从共享变量中查找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案例语句的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_sm_tb_fixed.png

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

原始代码已经成功模拟,但由于组合循环似乎没有合成到工作设计中:

XM_INDEX := XM_INDEX + 1;

其中xm_loop被一个可能的状态TRANSMIT_CHAR_CONFIRM的热状态表示锁存为锁存器使能。

在模拟中,没有xm_index的灵敏度列表会阻止加法器增加xm_index的纹波。如果xm_index已经在进程敏感性列表中,那么在达到100之后它将导致分配上的边界检查违规。(整数类型不是模块化的,它们不会包装并且不会被证明不会溢出)。

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