0

考虑下面的例子:

module test;
 reg a;
 initial begin
  a = 1'b0;
  a <= 1'b1;
  $display(a);
 end
endmodule

上面的例子显示为 0。我的原因是因为非阻塞赋值将在“分层事件队列”的第 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。

我可以找到有关“当前模拟时间”和“分层事件队列”的参考资料,但我不确定它是如何工作的。

我很欣赏你的解释!谢谢

4

2 回答 2

1

verilog 模拟周期比您当前的解释描述的更复杂。对您的问题的简短回答是,$monitor不等待当前周期结束,显示其消息然后继续执行该过程(并因此触发$display之后),而是简单地安排消息在任何模拟周期结束时显示其中任何因变量(在这种情况下只是a)发生变化;这是一种非常特殊(而且相当过时)的方式来监控模拟中的信号变化。$display然而立即执行,从而打印a当时的任何内容。所以,另一种思考方式就像非阻塞赋值(<=),$monitor只是设置稍后发生的事情,然后继续执行下一条语句,而不是内联发生。

您应该考虑学习 systemverilogs 模拟模型,注意活动、nba 和延迟区域,因为它们分别在阻塞分配 ( =)、非阻塞分配 ( <=) 和$monitor运行时。

于 2019-06-09T23:19:44.043 回答
1

Verilog 仿真是事件驱动的。事件是 Verilog 变量(或命名事件)值的变化。模拟分步进行。

一个步骤从放入事件队列的输入事件开始。由于评估而导致的值的每一个新变化都会创建新的事件,这些事件会添加到队列中。当队列为空(没有更多活动事件)时模拟结束。每一个这样的步骤都会增加模拟时间。

步骤本身分为多个区域,这些区域使用标准中定义的算法执行。

对于 verilog 2K,大约有 3 个主要区域:

  1. 阻塞分配区。Verilog 执行事件队列调度的所有程序块并对新的阻塞分配事件做出反应。它只是安排稍后执行的 nbas 事件。当所有阻塞事件都完成后,它会进入下一个区域。

  2. 非阻塞分配区。在这里,它执行对日程安排 nba 事件作出反应的所有块。它会将 ba 和 nba 事件都放在队列中。当所有 nba 完成后,如果有 ba 事件,它可能会回到区域“1”并重新开始。

  3. monitor/strobe zone——这是$monitor(和$strob)工作的区域。它在 ba 和 nba 区域都完成后执行(没有更多事件)。

在您的情况下a = 1,在阻塞分配区域执行。该值一直持续到该区域结束。$display也将在该区域执行。因此,它将看到“a == 0”的值。

a <= 1$dislpay 完成后,将安排在非阻塞区域执行。 $monitor在非阻塞完成后,将在监控区域中选择事件。因此,它将向您显示1.

您的语句在initial块中执行。结果,没有事件传播。事件仅由alwaysandassign语句选择。如果你把你的$display放在一个 always 块中,你会看到更有趣的结果。 always @* $display(a);

您应该阅读 verilog 中的标准模拟语义以获取更多信息。

于 2019-06-10T00:50:29.033 回答