关于触发always块的问题

Question about triggering of always blocks

我知道 always 块将在其敏感列表中的任何元素发生变化时触发,但是,我的问题是,如果在 always 块内的语句仍在运行时敏感列表发生变化,会发生什么情况已执行(由于先前的触发器)。 always 块是在并行线程中再次开始执行,还是在执行先完成之前触发阻塞?

每个 always 块都是一个线程(除非它包含 fork-join)。因此,如果它正在执行,如果敏感列表中的某些内容发生变化,它不会重新触发。 (如果它确实包含 fork-join,那么它会在触发后分成多个线程,但不会再次触发)。

一触发always块,就会从头执行到尾。 Verilog 是一种单线程仿真引擎,因此一次只能执行一个块。执行 always 块时不会发生任何其他事情,除非它包含延迟语句或等待事件。在后一种情况下,它只会产生 允许其他块被执行,然后它继续。

如果 always 块在执行结束时更改其输入,则下一个模拟行为取决于块的类型:

  • 所有v95/v2k总是块(例如总是@*)将被重新触发,
  • system verilog 块(例如,always_comb)将不会在当前增量循环中被重新触发。

使用 v2k/v95 always 块,如果您不小心,可能会以零延迟循环结束。换句话说模拟可以挂起。

借助 SystemVerilog 块,您可以获得一个有趣的先读后写条件(当变量在写入同一块之前被读取)。这可能会造成 simulation/synthesis 不匹配。

很多人没有意识到@是一个语句修饰符,不是自己构造的。它说延迟后面的语句直到有事件发生。 @(A or B) 表示等到 A 或 B 的值发生变化(不要与 A|B 结果的变化相混淆)。 @* 表示查看后面的语句,并构建一个隐含的敏感信号列表以等待更改。

always @(A or B) C = A + B;
always begin
         @(A or B) C = A + B;
       end
always begin
         @* C = A + B;
       end
always_comb
       begin
         C = A + B;
       end

这 4 个 always 块具有相同的行为,除了最后一个 always_comb 也会在时间 0 触发,无论 A 或 B 有任何变化。

如果您将 always 之后的代码视为执行语句的过程序列,则可能更容易将 @ 构造视为该过程序列的一部分,并且更改必须在您执行构造时发生。只需添加另一个延迟即可显示此概念(不可综合)

always @(A or B) begin
         #10 C = A + B;
       end

这表示 "wait for a change on A or B, then wait 10 more time units, then make an assignment to C with the current values of A + B"。如果在 10 个时间单位等待期间 A 或 B 发生变化,则该变化将被遗漏。在这 10 个时间单位之后,您必须等待 A 或 B 的另一次更改。