为什么我得到如下所示的结果?我希望update_ev事件的多个触发器应该导致显示"主循环,..."被执行两次。但它只执行一次。
program multiple_trigger();
initial begin
event update_ev;
bit orResult, prev_orResult, A, B;
// Multiple trigg logic
fork
begin : threadDisplay
forever begin
prev_orResult = orResult;
// Update status
@(update_ev);
// Compute A OR B
orResult = A | B;
$display("\n Main Loop , %0t A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);
if (prev_orResult != orResult) begin
$display("\n In the IF condition, %0t A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);
end
end // forever
end : threadDisplay
// 10 A=0
begin : threadA
#10;
A = 1'b0;
->update_ev;
end : threadA
// 10 B=1'b1
begin : threadB
#10;
B = 1'b1;
->update_ev;
end : threadB
join_none
#100;
end
endprogram
// Actual Result----------------------------------------
Main Loop , 10 A=0, B=1 orResult=1
In the IF condition , 10 A=0, B=1 orResult=1
//-----------------------------------------------------
// Expected Result----------------------------------------
Main Loop , 10 A=0, B=0 orResult=0
Main Loop , 10 A=0, B=1 orResult=1
In the IF condition , 10 A=0, B=1 orResult=1
// -------------------------------------------------------
答案 0 :(得分:0)
实际和预期的结果都是可能的,因为它是一种竞争条件。在@(update_ev)响应之前,没有什么可以阻止第二个> update_ev触发。
我通常建议人们避免使用SV事件,在这种情况下,函数调用可以达到预期目的。
答案 1 :(得分:0)
事件触发器->
可能看起来是堆栈,具体取决于调度顺序。 LRM声明调度程序可以以不确定的顺序评估同一区域中的事件。我使用的每个模拟器似乎都没有随机化事件顺序;他们倾向于优先编制订单。
让我们给fork中的线程赋予标签名称。然后介绍一个可能的场景。
forever
A = 1'b0;
B = 1'b1;
模拟器首先执行 disp_thread ,并在到达@(update_ev);
时停止。然后 thread_A 将启动并立即进入睡眠状态(将在#10
中向上走)。 thread_B 统计信息并且还为#10
休眠。如果没有安排任何操作,模拟将不正确的时间步长。唤醒 thread_A 和 thread_B , thread_A 首先执行并触发->update_ev
并完成。 disp_thread 现已取消屏蔽,模拟器可以选择:继续 disp_thread 或 thread_B < / EM> 即可。通常,它会选择继续 disp_thread 并启动其第二个循环(因为永远在同一个进程线程中)并再次在@(update_ev);
暂停。 thread_B 现在可以更改运行并触发->update_ev
并完成。再次 disp_thread 取消阻止,以便它可以再次运行。
如果您将事件触发器移动到事件等待语句之上,则可能仅查看仅发出一次的$display
消息。然而,这是一个脆弱的解决方案,并没有重新开始。
而是使用非阻塞事件触发器(->>
)。这将移动事件触发调度程序的NBA区域。在取消阻止 disp_thread 之前,允许两个触发线程都运行并安排更新。通过此更改,计划将如下:
模拟器首先执行 disp_thread ,并在到达@(update_ev);
时停止。然后 thread_A 将启动并立即进入睡眠状态(将在#10
中向上走)。 thread_B 统计信息并且还为#10
休眠。如果没有安排任何操作,模拟将不正确的时间步长。唤醒 thread_A 和 thread_B , thread_A 首先执行计划 ->>update_ev
NBA 并完成。此时调度程序仍处于活动区域; disp_thread 仍然被屏蔽。 thread_B (唯一可以运行的东西)将运行并为 NBA 区域安排->>update_ev
并完成。注意到可以在当前区域中运行,调度程序继续到下一个非空区域; NBA。事件触发器update_ev
都发生了。调度程序重新进入活动区域, disp_thread 现在已取消阻止。
注意:如果您计划使用它,请保持简单并使自己对调度语义非常了解(IEEE std 1800-2012§4)。可预测性将变得脆弱,增加了复杂性(例如触发新事件的未阻塞阻塞事件)。当可预测性中断时,它可能会随机形成不同的模拟器,版本,机器,操作系统,系统时间,系统负载等。
答案 2 :(得分:0)
program multiple_trigger();
initial begin
event update_ev1, update_ev2;
bit orResult, prev_orResult, A, B;
// Multiple trigg logic
fork
begin : threadDisplay
forever begin
prev_orResult = orResult;
// Update status
@(update_ev1, update_ev2);
// Compute A OR B
orResult = A | B;
$display("\n Main Loop , %0t A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);
if (prev_orResult != orResult) begin
$display("\n In the IF condition, %0t A=%0b, B=%0b orResult=%0b",$time(), A, B, orResult);
end
end // forever
end : threadDisplay
// 10 A=0
begin : threadA
#10;
A = 1'b0;
->>update_ev1;
end : threadA
// 10 B=1'b1
begin : threadB
#10;
wait(update_ev1.triggered);
B = 1'b1;
->>update_ev2;
end : threadB
join_none
#100;
end
endprogram