事件调度评估 - Verilog分层事件队列
我正在尝试用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