"Current Simulation Time" 和 Verilog 中的事件队列到底是什么?
What exactly is "Current Simulation Time" and Event Queue in Verilog?
考虑以下示例:
module test;
reg a;
initial begin
a = 1'b0;
a <= 1'b1;
$display(a);
end
endmodule
上面的例子显示0。我的原因是因为非阻塞赋值将在"Stratified Event Queue"的第3步赋值,而阻塞赋值和$display在第1步完成。如果我修改例如:
module test;
reg a;
initial begin
a = 1'b0;
a <= 1'b1;
$display(a);
$monitor(a);
end
endmodule
然后打印 0 和 1,因为我假设 $monitor 在事件队列的第 4 步执行(?)。但是如果我进一步修改示例:
module test;
reg a;
initial begin
a = 1'b0;
a <= 1'b1;
$monitor(a);
$display(a);
end
endmodule
再次输出:0 和 1——这是我没有预料到的。我希望打印 1 和 1,因为 $monitor 将在事件队列的第 4 步进行评估,到那时,"a" 已经是 1。之后我们有 $display,它应该打印 1.
参考资料 我可以找到关于 "current simulation time" 和 "stratified event queue" 的讨论,但我不确定它是如何工作的。
感谢您的解释!
谢谢
verilog 仿真周期比您当前的解释描述的更复杂。对您的问题的简短回答是 $monitor
不会等待当前周期结束,显示其消息然后继续执行该过程(从而触发 $display
之后),而是简单地安排消息在任何因变量(在本例中只是 a
)发生变化的任何模拟周期结束时显示;这是一种非常特殊(而且相当过时)的方式来监控模拟过程中信号的变化。 $display
但是会立即执行,因此会打印当时 a
的内容。因此,另一种思考它的方式就像非阻塞赋值 (<=
),$monitor
只是设置了一些稍后发生的事情,并且执行继续到下一条语句而不是内联发生。
您应该考虑了解 systemverilogs 仿真模型,注意活动区域、nba 区域和延迟区域,因为这些区域在 blocking-assignments(=
)、非阻塞分配(<=
)时和 $monitor
分别是 运行。
Verilog 仿真是事件驱动的。事件是 verilog 变量(或命名事件)值的变化。模拟是分步进行的。
一个步骤从放入事件队列的输入事件开始。由于评估而导致的每个新值更改都会创建新事件,这些事件会添加到队列中。当队列为空时模拟结束(没有更多活动事件)。每一个这样的步骤都会增加模拟时间。
步骤本身分为多个区域,这些区域使用标准中定义的算法执行。
对于verilog 2K,大致有3个主要区域:
阻塞分配区。 Verilog 执行事件队列调度的所有程序块并对新的阻塞分配事件做出反应。它只是安排稍后执行的 nbas 事件。完成所有阻塞事件后,它会到达下一个区域。
non-blocking分配区。在这里它执行所有对 nba 事件计划做出反应的块。它会将 ba 和 nba 事件都放在队列中。当所有 nba 完成后,如果有 ba 事件,它可能会回到区域“1”并重新开始。
monitor/strobe 区域 -- 这是 $monitor(和 $strob)工作时的区域。它在 ba 和 nba 区域完成后执行(没有更多事件)。
在你的情况下 a = 1
在阻塞分配区中执行。该值一直持续到该区域结束。 $display
也将在此区域执行。因此,它将看到 'a == 0` 的值。
a <= 1
将在 $dislpay 完成后安排在 non-blocking 区域执行。
$monitor
将在 non-blocking 完成后选择监控区域中的事件。因此,它将显示 1
.
的值
您的语句在 initial
块中执行。因此,没有事件传播。事件仅由 always
和 assign
语句选取。如果将 $display
放在 always 块中,您会看到更有趣的结果。 always @* $display(a);
您应该阅读 verilog 中的标准仿真语义以获取更多信息。
考虑以下示例:
module test;
reg a;
initial begin
a = 1'b0;
a <= 1'b1;
$display(a);
end
endmodule
上面的例子显示0。我的原因是因为非阻塞赋值将在"Stratified Event Queue"的第3步赋值,而阻塞赋值和$display在第1步完成。如果我修改例如:
module test;
reg a;
initial begin
a = 1'b0;
a <= 1'b1;
$display(a);
$monitor(a);
end
endmodule
然后打印 0 和 1,因为我假设 $monitor 在事件队列的第 4 步执行(?)。但是如果我进一步修改示例:
module test;
reg a;
initial begin
a = 1'b0;
a <= 1'b1;
$monitor(a);
$display(a);
end
endmodule
再次输出:0 和 1——这是我没有预料到的。我希望打印 1 和 1,因为 $monitor 将在事件队列的第 4 步进行评估,到那时,"a" 已经是 1。之后我们有 $display,它应该打印 1.
参考资料 我可以找到关于 "current simulation time" 和 "stratified event queue" 的讨论,但我不确定它是如何工作的。
感谢您的解释! 谢谢
verilog 仿真周期比您当前的解释描述的更复杂。对您的问题的简短回答是 $monitor
不会等待当前周期结束,显示其消息然后继续执行该过程(从而触发 $display
之后),而是简单地安排消息在任何因变量(在本例中只是 a
)发生变化的任何模拟周期结束时显示;这是一种非常特殊(而且相当过时)的方式来监控模拟过程中信号的变化。 $display
但是会立即执行,因此会打印当时 a
的内容。因此,另一种思考它的方式就像非阻塞赋值 (<=
),$monitor
只是设置了一些稍后发生的事情,并且执行继续到下一条语句而不是内联发生。
您应该考虑了解 systemverilogs 仿真模型,注意活动区域、nba 区域和延迟区域,因为这些区域在 blocking-assignments(=
)、非阻塞分配(<=
)时和 $monitor
分别是 运行。
Verilog 仿真是事件驱动的。事件是 verilog 变量(或命名事件)值的变化。模拟是分步进行的。
一个步骤从放入事件队列的输入事件开始。由于评估而导致的每个新值更改都会创建新事件,这些事件会添加到队列中。当队列为空时模拟结束(没有更多活动事件)。每一个这样的步骤都会增加模拟时间。
步骤本身分为多个区域,这些区域使用标准中定义的算法执行。
对于verilog 2K,大致有3个主要区域:
阻塞分配区。 Verilog 执行事件队列调度的所有程序块并对新的阻塞分配事件做出反应。它只是安排稍后执行的 nbas 事件。完成所有阻塞事件后,它会到达下一个区域。
non-blocking分配区。在这里它执行所有对 nba 事件计划做出反应的块。它会将 ba 和 nba 事件都放在队列中。当所有 nba 完成后,如果有 ba 事件,它可能会回到区域“1”并重新开始。
monitor/strobe 区域 -- 这是 $monitor(和 $strob)工作时的区域。它在 ba 和 nba 区域完成后执行(没有更多事件)。
在你的情况下 a = 1
在阻塞分配区中执行。该值一直持续到该区域结束。 $display
也将在此区域执行。因此,它将看到 'a == 0` 的值。
a <= 1
将在 $dislpay 完成后安排在 non-blocking 区域执行。
$monitor
将在 non-blocking 完成后选择监控区域中的事件。因此,它将显示 1
.
您的语句在 initial
块中执行。因此,没有事件传播。事件仅由 always
和 assign
语句选取。如果将 $display
放在 always 块中,您会看到更有趣的结果。 always @* $display(a);
您应该阅读 verilog 中的标准仿真语义以获取更多信息。