【问题标题】:verilog / systemverilog -- What is the behavior of blocking statements across two always blocks?verilog / systemverilog -- 跨两个 always 块的阻塞语句的行为是什么?
【发布时间】:2014-06-09 15:20:49
【问题描述】:

我想知道下面代码的行为。有两个always块,一个是组合计算next_state信号,另一个是顺序的,它将执行一些逻辑并确定是否关闭系统。它通过将shutdown_now 信号设置为高电平然后调用state <= next_state 来实现这一点。

我的问题是,如果shutdown_now 信号在state <= next_state 行之前以阻塞方式设置(在时钟周期n 期间)的条件变为真,那么时钟周期n+1 期间的状态是否为SHUTDOWN 或RUNNING?换句话说,由于state 信号通过next_state 确定依赖于它,所以shutdown_now = 1'b1 行是否跨越两个状态机?

 enum {IDLE, RUNNING, SHUTDOWN} state, next_state;
 logic shutdown_now;

 // State machine (combinational)
 always_comb begin
    case (state)
       IDLE: next_state <= RUNNING;
       RUNNING: next_state <= shutdown_now ? SHUTDOWN : RUNNING;
       SHUTDOWN: next_state <= SHUTDOWN;
       default: next_state <= SHUTDOWN;
    endcase
 end

 // Sequential Behavior
 always_ff @ (posedge clk) begin
    // Some code here
    if (/*some condition*/) begin
       shutdown_now = 1'b0;
    end else begin
       shutdown_now = 1'b1;
    end
    state <= next_state;
 end

【问题讨论】:

    标签: verilog blocking nonblocking system-verilog


    【解决方案1】:

    首先,您没有遵循属性编码。 always_comb 应该只使用阻塞 (=) 分配,绝不能使用非阻塞 (&lt;=)。而always_ff 则相反,只有非阻塞(&lt;=)赋值,从不阻塞(=)。

    使用原样的代码,state 将变为 RUNNING。这是因为分配给next_state 是非阻塞的,因此next_state 直到稍后在调度程序中才会更新。

    假设,如果 next_state 和 shutdown_now 都是阻塞分配,那么模拟器将有一个竞争条件。 next_state 都可以在评估 state 之前或之后评估和更新。这就是为什么在同一个 always 块中混合阻塞和非阻塞并不是一个好主意。

    如果编码正确,即next_state = ... 和shutdown_now &lt;= ...,那么状态也会转到RUNNING。这是因为shutdown_now 更新发生在所有计划的评估完成之后。所以next_state 在评估state 之前不会看到1'b1。

    【讨论】:

    • 嗨,格雷格,感谢您的回复。我看到了你所描述的比赛条件,我会谨慎对待它。我同意你的观点,always_comb 块应该总是阻塞的。但是,当您说 always_ff 块中的所有分配都应该始终是非阻塞的时,也许我误解了您?我经常在顺序块中同时使用阻塞和非阻塞语句来创建触发器之间的组合链。你介意澄清一下吗?
    • @Miles,虽然我认识一些人在顺序块中混合阻塞和非阻塞语句。这不是我做的事情。正如 Greg 所说,一般建议不要在一个流程中混合使用不同的分配类型。我倾向于在他们自己的(通常非常小的)过程中编写我的组合逻辑和顺序逻辑。对我来说,这似乎使代码更易于阅读,并且更易于合成工具处理。
    • @Ciano 这确实有道理。感谢您的意见
    • 在风格上,你是对的。但是,在 always_comb 块中使用非阻塞赋值或在 always_ff 块中使用阻塞赋值实际上并没有错。 LRM 定义了所有现代工具都实现的一致行为。
    • @JonathanMayer,阻塞与非阻塞仍然很重要,因为每个区域内的事件可以以不确定的顺序执行。每个区域的执行顺序是确定的。 IEEE1800-2012 第 4.6 节 确定性、4.7 非确定性 和 4.8 竞争条件。 LRM 允许 always 的所有形式的阻塞和 NBA,但如果您希望 RTL​​、门和电路匹配,请遵循阻塞/非阻塞准则。
    【解决方案2】:

    Miles,你真的只有一个状态机,那就是你的 always_comb 块中的代码。 always_ff 只是创建一个寄存器来保存你的状态。

    按照您现在编写代码的方式,您将按以下顺序执行关机:

    • 周期 n:always_ff 块中的逻辑将确定是否应该关闭,并将安排 shutdown_now 信号在下一个时钟滴答中断言。
    • 周期 n+1:shutdown_now 置位,状态机(当前处于 RUNNING)将 next_state 设置为 SHUTDOWN。
    • 周期 n+2:现在您的状态机将处于 SHUTDOWN 状态。

    不确定是否需要在 always_ff 块中包含关闭逻辑。如果将该代码移至 always_comb 块,则可以保持其余代码不变,并且您的状态机将在周期 n+1 中移至 SHUTDOWN 状态。

    【讨论】:

    • 正如 Greg 提到的,您确实需要修复您的阻塞/非阻塞分配。
    • 感谢 Ciano,非常感谢。我确实需要在周期 n+1 期间处于 SHUTDOWN 状态。我认为我应该能够将关闭逻辑移动到 always_comb 块中以确保这种行为。另外,请参阅我对 Greg 关于阻塞/非阻塞分配的帖子的评论。
    【解决方案3】:

    这是我希望代码执行的方式:

    您有两个寄存器:一个“shutdown_now”寄存器和一个“状态”寄存器。

    在 clk 的 posedge 上,这两个寄存器的状态都会自动更新。也就是说:当对 shutdown_now 的阻塞分配发生时,当前进程在 state_next 更新时不会中断。相反,对于 always_ff 进程而言,state_next 是它在 posedge clk 模拟“tick”开始时所持有的任何值。

    所以:

    1. 在第一个 posedge 时钟滴答声中:shutdown_now 将从 0 切换到 1。

    2. 一旦“posedge clk”进程完成,“always_comb”块会注意到它有工作要做,并会更新 state_next(即从 2 个寄存器的新状态进行组合解码)。

    3. 在第二个 posedge 时钟滴答声中:状态被更新的 state_next 锁定。

    因此,关闭系统需要 2 个周期,而不是 1 个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 2019-02-05
      • 1970-01-01
      • 2020-05-18
      • 1970-01-01
      相关资源
      最近更新 更多