事件调度评估 - Verilog分层事件队列

3 投票
1 回答
1572 浏览
提问于 2025-04-18 01:01

我正在尝试用Python实现一个简单的基于事件的Verilog模拟器,但我在查找一些细节时遇到了困难,这些细节在IEEE 1364-2005的第11节中有说明。

假设我刚刚对clk进行了更新,现在它的值变成了1(之前是0)。根据规范,这要求我为敏感的进程安排“评估事件”。

我需要把always @(posedge clk)这个块的评估安排为活跃事件还是非活跃事件呢?我猜应该是非活跃事件,对吧?

或者说得更一般一点,基本上所有事件都是作为非活跃事件安排的,只有以下几种例外:

  • 非阻塞赋值更新作为非阻塞赋值事件
  • 连续赋值作为活跃事件

非常感谢!

1 个回答

3

默认情况下,所有操作都在活动区域进行。例外情况有:

  • #0 阻塞赋值在非活动区域
  • 非阻塞赋值在NBA区域
  • $monitor、$strobe 和 PLI/VPI 在监视区域

我们可以通过在任何现有的模拟器中运行以下代码来证明@是在活动区域发生的:

int x; 
initial begin 
   $monitor("From    $monitor: x is %0d",x); 
   #0 x = 5; // inactive event 
   x = 3; // active event (after inactive event)
   #1; // go to next time stamp
   fork
     #0 x = 7; // inactive event 
     x = 11; // active event 
   join
   #0 fork // inactive region
     #0 x = 2; // inactive event 
     x = 5; // active event 
     x <= 4; // NBA event 
   join
end 
// active event region
always @* $display("From @* $display: x is %0d",x); 

输出:

 From @* $display: x is 3
 From $monitor: x is 3
 From @* $display: x is 11
 From @* $display: x is 7
 From @* $display: x is 5
 From @* $display: x is 2
 From @* $display: x is 4
 From $monitor: x is 4

Display的报告次数比monitor多。这就排除了@发生在监视或未来区域的可能性。Display是在每个事件区域都在报告。每个事件区域只能回到活动区域。因此,@必须在活动区域处理,而每个更新变量x的区域都会触发一个新的活动区域事件。

这也可以通过了解Verilog的历史来证明。不幸的是,这方面的文档并不充分。我是通过一些在80年代末和90年代初使用或开发Verilog的人了解到这些的。总体来说,解释是:非活动和NBA区域是在IEEE Std 1364-1995之前添加到Verilog中的,@的出现早于这两个区域。这些区域的添加是为了给一个非确定性的模拟器增加确定性。

always @(posedge clk) pipe0 = in;
always @(posedge clk) pipe1 = pipe0; // unpredictable, non-deterministic order
always @(posedge clk) #0 pipe0 = in; // Inactive region added some determinism
always @(posedge clk) pipe1 = pipe0; 
always @(posedge clk) #0 pipe0 = in; // But was fragile
always @(posedge clk) #0 pipe1 = pipe0; // unpredictable order again
always @(posedge clk) pipe2 = pipe1; 
always @(posedge clk) pipe0 <= in; 
always @(posedge clk) pipe1 <= pipe0; 
always @(posedge clk) pipe2 <= pipe1; // NBA region fixed it 
 ...
always @(posedge clk) pipeN <= pipeM; //             and made it scalable



根据评论反馈的澄清

事件是激活的,而不是移动的。激活的NBA事件进入if (E是一个更新事件)的真实条件,修改对象并安排新的评估事件(在下次进入活动区域时处理)。一旦所有激活的事件完成,调度就会回到while循环的顶部。NBA区域只负责赋值,评估实际上是在之前的活动区域阶段完成的。
根据你的例子:

module TEST;
   reg test = 0;
   reg test2 = 0;
   reg clk = 0;

   initial begin
      clk <= 1;
      test <= 1;
   end

   always @(posedge clk) begin
      test2 <= test;
   end
endmodule

while循环的每次迭代大致如下:

Iteration:0
  Active: <----- This is region is executing
      clk$tmp = eval(1)
      test$tmp = eval(1)
  Inactive:
  NBA:
      clk = clk$tmp
      test = test$tmp
Iteration:1
  Active:
  Inactive:
  NBA: <----- This is region is executing
      clk = clk$tmp
      test = test$tmp
      Active.schedule( eval( "@(posedge clk)" )
Iteration:2
  Active: <----- This is region is executing
      eval( "@(posedge clk)" )
      Active.schedule( "test2$tmp = eval(test)" )
      NBA.schedule( "test2 = test2$tmp" )
  Inactive:
  NBA:
Iteration:3
  Active: <----- This is region is executing
      test2$tmp = eval(test)
  Inactive:
  NBA:
      test2 = test2$tmp
Iteration:4
  Active:
  Inactive:
  NBA: <----- This is region is executing
      test2 = test2$tmp
Iteration:5 --> next simulation cycle

撰写回答