You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Verilog中FSM的case变量是否需要原子递增?

关于Verilog FSM状态遍历中计数器更新的跳步问题分析

首先直接给结论:两种代码实现都不会导致FSM跳步,但片段2的非原子更新方式确实效率更高——每个状态只需要1个时钟周期,而片段1需要2个。下面具体拆解两种实现的行为逻辑:

片段1:双拍式的计数器更新

片段1通过ITERATE信号把“状态执行”和“计数器递增”拆分成了两个时钟周期:

  • 当ITERATE为低时,执行当前i对应的case状态逻辑;
  • 当ITERATE为高时,只执行i = i + 1的递增操作;
  • ITERATE在每个时钟沿翻转,所以每两个时钟周期才会推进一次状态(执行一个状态+更新计数器)。

这种“原子更新”的本质是双拍控制,确实能保证状态不会跳步,但代价是效率减半——因为一半的时钟周期都用来做计数器更新,没有执行状态逻辑。它的适用场景是需要在状态执行和计数器更新之间插入额外控制(比如等待外部信号、插入延迟)的情况,但对于单纯的顺序状态遍历来说,这种设计是冗余的。

片段2:单周期的计数器更新

片段2的逻辑是在同一个时钟周期内,先执行当前i对应的case逻辑,再递增i。这里的关键是理解Verilog中always @(posedge clk)块内阻塞赋值(=)的执行顺序:

  • 在同一个时钟沿触发时,块内的语句是按代码顺序执行的:先执行case逻辑,此时i还是上一个周期的旧值,对应当前要执行的状态;
  • 执行完case后,才会执行i = i + 1,把i更新为下一个状态的索引;
  • 更新后的i会在下一个时钟沿生效,作为下一次case的判断变量。

这种方式下,每个时钟周期对应一个状态的完整执行+计数器更新,完全不会出现跳步——因为当前周期的状态逻辑永远基于更新前的i,下一个周期才会使用新的i。这也是它效率是片段1两倍的原因:每个状态只需要1个时钟周期。

额外注意点

如果把片段2中的阻塞赋值(=)换成非阻塞赋值(<=),结果也是一样安全的。因为非阻塞赋值是在时钟沿结束后才完成变量更新,case逻辑依然会使用i的旧值,不会影响状态的逐次推进。

总结建议

如果你的应用只是需要按顺序遍历状态,没有额外的控制需求,片段2的实现是更优的选择——它既保证了FSM不会跳步,又能把执行效率提升一倍。片段1的双拍设计只适合需要拆分操作步骤的特殊场景。

内容的提问来源于stack exchange,提问作者Magnus Øverbø

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:24:20