问题描述
我正在尝试在Python中实现一个简单的基于事件的Verilog模拟器,但是我实际上很难在规范中找到一些细节(IEEE 1364-2005的第11节).
假设我只是在clk上完善了一个更新事件,该事件现在获得了新值1(前0).根据规范,这要求我安排敏感过程的"评估事件".
我是否必须安排对always @(posedge clk)块作为活动或不活动事件的评估?我猜后者是正确的吗?
或实际上说更一般.基本上所有事件都安排为无效事件,但有以下例外:
- 非阻止分配更新作为非阻滞作业事件
- 连续分配为活动事件
非常感谢!
推荐答案
默认情况下,一切都在活动区域中运行.例外是:
- #0阻止分配在非活动区域
- 非阻滞作业在 nba 区域
- $ MONITOR,$ Strobe和PLI/VPI在 Monitor region
我们可以通过在任何现有模拟器中运行以下内容来证明@发生在活动区域中:
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
显示报告更多次,然后监视.这排除了在监视器或将来的区域中累积的@.显示正在报告每个事件区域.每个事件区域只能循环回到活动区域.因此,必须在活动区域中处理@,每个更新变量x的区域都触发了新的活动区域事件.
这也可以通过了解Verilog的历史来证明这一点.不幸的是,这还没有得到充分记录.我通过在90年代初在80年代末使用/开发Verilog的人们了解了这一点.总体上的解释是:在IEEE STD 1364-1995,@之前,将非活动和NBA区域添加到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 is an update event),修改对象的真实条件,并安排新的评估事件(下次输入 Active 输入的区域).一旦所有激活的事件都完成后,时间表将返回到While-Loop的顶部. 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
问题描述
I am trying to implement a simple event based Verilog simulator in Python, but I actually struggle to find some details in the specification (section 11 of IEEE 1364-2005).
Let's say I just perfomed an update event on clk which now obtained the new value 1 (0 before). According to the specification this requires me to schedule 'evaluation events' for sensitive processes.
Do I have to schedule the evaluation of an always @(posedge clk) block as active or inactive event? I'm guessing that the latter is correct?
Or actually, to speak more general. Are basically all events scheduled as inactive events with the following exceptions:
- Non blocking assignment updates as non-blocking assignment events
- continuous assignments as active events
Thanks a lot!
推荐答案
By default everything runs in the Active region. The exceptions being:
- #0 blocking assignments are in the Inactive region
- Non-blocking assignments are in the NBA region
- $monitor, $strobe, and PLI/VPI are in the Monitor region
We can prove that @ happens in the Active region, by running the following in any existing simulator:
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);
Outputs:
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 reports more times then monitor. This rules out @ accruing in the Monitor or Future region. Display is is reporting on every event region. Each event region can only loop back to the Active region. Therefor, the @ must be handled in the Active region and each region that updates the variable x is triggering a new Active region event.
This can also be proved by knowing looking at the history of Verilog. Unfortunately, this is not well documented. I leaned learned about it through people that were using/developing verilog in the late '80s early '90s. The over all explanation is: The Inactive and NBA regions were added to Verilog before IEEE Std 1364-1995, @ predates these two region. The regions were added to add determinism to a non-deterministic simulator.
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
clarification based on feedback from comments
Events are activated, not moved. Activated NBA events enter the true condition of if (E is an update event), modified object and schedule new evaluation events (handled the next time Active region entered). Once all the activated events are completed, the schedule goes back to the top of the while-loop. The NBA region only assigns values, the evaluation was actually done in an earlier Active region stage.From your example:
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
Each iteration of the while loop would look something like this:
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