【问题标题】:State management in VHDL FSMsVHDL FSM 中的状态管理
【发布时间】:2013-01-10 10:46:18
【问题描述】:

我在 VHDL 中看到的许多 FSM 都是通过在 FSM 逻辑中设置变量“next_state”来工作的,然后将其单独分配给进程外的状态变量。

如果简单地写“state <= state_five;”有什么问题改为设置下一个状态? 我假设 is 有这么多人使用单独的下一个状态变量而不是直接分配给我一直看到的状态的原因,但据我所知,没有不同之处在于它使代码更长,看起来更复杂。

我错过了什么吗?还是只是风格问题?如果是这样,为什么这种风格更好,对我来说似乎没有必要。

【问题讨论】:

    标签: vhdl fsm


    【解决方案1】:

    “简单写state <= state_five;有什么问题吗?”

    什么都没有 - 前提是状态分配是在时钟进程中完成的。

    这导致了简洁可靠的“单进程状态机”风格,而不是在太多的教科书和在线教程中教授的不可靠(因为很容易弄错敏感度列表)的双进程风格。

    搜索“单进程状态机”,你应该可以找到很好的示例材料和进一步的讨论。

    历史记录:上个世纪可能有一些合成工具存在单进程风格的问题;但现在没有理由避免它。

    【讨论】:

      【解决方案2】:

      之所以有些人总是写两进程状态机(即一个同步进程和一个并发进程),可能主要是因为他们在学校就学会了这样做,就像其他人说的那样。

      但是,在某些情况下,这种编码风格实际上比使用单个同步进程要好。简而言之,它允许您在同一上下文中混合同步和并发分配。考虑以下两段代码,它们都完成了同样的事情:

      双进程状态机:

      process (clk)
      begin
        if rising_edge(clk) then
          state <= state_next;
        end if;
      end process;  
      
      process (all)
      begin 
        state_next <= state;
        case state is 
          when s_idle => 
            if req_i = '1' then 
              fifo_read <= '1'; -- Concurrent assignment
              state_next <= s_check_data; -- "Synchronous" assignment 
            end if; 
          when s_check_data =>
            if fifo_out = x"1234" then
              (...)
            end if;
          (...) 
        end case; 
      end process;
      

      单进程状态机:

      process (clk)
      begin 
        if rising_edge(clk) then
          case state is 
            when s_idle => 
              if req_i = '1' then 
                fifo_read <= '1';
                state <= s_wait_for_data;
              end if;
            when s_wait_for_data =>
              state <= s_check_data;
            when s_check_data =>
              -- Data word from FIFO now available.
              if fifo_out = x"1234" then
                (...)
              end if;
            (...) 
          end case;
        end if;
      end process;
      

      请注意第二个示例中的额外状态。因为没有办法在 VHDL 中的同步进程中进行并发分配(我希望有!),一个寄存器将被添加到 fifo_read 信号中,将其延迟一个周期。虽然这个例子很简单,但始终坚持使用单进程状态机有时会导致代码变得非常混乱且难以理解。它还可能导致您的状态机在硬件上花费更多资源,甚至使代码更长。这两种风格都不是正确的选择,但我通常更喜欢单进程变体。

      【讨论】:

        【解决方案3】:

        甚至使用变量:

        state := state_five
        

        使用变量存储状态意味着它对进程保持完全本地化,不会污染整个architecture的命名空间。

        这也意味着如果你在调试时习惯使用printf(对不起report)来跟踪状态机的进度(有时比波形查看器更可取),你可以报告预期的 next 状态机进程结束时的状态,可以很方便。

        添加: 如 cmets 中所述,变量赋值“立即”发生(要清楚 - 这不会导致 case 语句立即切换到下一个状态!)

        如果您需要提前一个周期输出,则可以(偶尔)使用此效果,方法是根据过程结束时的下一个状态分配给所述信号。如果我最终需要这样做,我倾向于有一个显式的next_state 变量,除了case 语句和state := next_state;(正好在时钟进程结束时)之外,我将其用于所有内容。它向代码审查者展示了您打算这样做!

        【讨论】:

        • 也许是个好主意 :) 我的理解是变量的赋值发生在它们被写入的地方,而信号的变化发生在过程结束时。我想在实践中你不会查看状态,除非在 switch 语句中,所以它没有什么区别。
        • 关于你的 next_state 变量技术;有趣的想法,但是您在代码中的不同位置的每个州都会发生一些事情。 (尽管在您的情况下,这两个地方在同一个进程中。)带有下一个信号的双进程状态机的好处是您有一个单独的 case 语句,其中每个状态(同步和并发)的所有分配都在一样的地方。这可以说更容易阅读。 (并不是说我非常喜欢这种风格,只是想让讨论更加平衡。)
        • @pc3 - 如果您使用 next_state 变量,它与双进程系统并没有太大的不同。但是它仍然具有单个进程的特性(例如,没有锁存器,所有输出都有一个 DFF,所有内容都保存在一起,状态对于单个进程是本地的,更少的信号混乱)。正如您在回答中所说的那样,如果您绝对必须减少延迟,仍然有一些您无法通过双进程方法可以实现的事情。
        • 我同意这一切。总而言之,我尽量不要对此有太强烈的意见,因为所有不同的编码风格都有优点和缺点。有些人喜欢写双进程状态机,我不鼓励他们停下来。
        • @pc3 - 我认为我们当时的协议很激烈 :) 我也不想鼓励人们停下来。只是停下来思考相对的优点,而不是盲目地耕耘。多年来,相对优点发生了变化,但我看到的很多建议(不是暗示你的!)似乎都是基于“旧知识”。 (这让我想知道 - 我想知道我应该重新考虑我的传统做法:)
        猜你喜欢
        • 2014-10-03
        • 2017-05-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-16
        • 2011-08-25
        • 1970-01-01
        • 2012-10-11
        • 2019-02-12
        相关资源
        最近更新 更多