System verilog Systemverilog-触发同一事件的多个进程
为什么我会得到下面显示的结果?我希望update_ev事件的多个触发器会导致显示“Main Loop,…”执行两次。但它只执行一次System verilog Systemverilog-触发同一事件的多个进程,system-verilog,System Verilog,为什么我会得到下面显示的结果?我希望update_ev事件的多个触发器会导致显示“Main Loop,…”执行两次。但它只执行一次 program multiple_trigger(); initial begin event update_ev; bit orResult, prev_orResult, A, B; // Multiple trigg logic fork begin : threadDisplay
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
// -------------------------------------------------------
实际结果和预期结果都是可能的,因为这是一种竞赛条件。在@(更新电动汽车)响应之前,没有任何东西可以阻止第二个->更新电动汽车触发
我通常建议人们避免SV事件,在这种情况下,函数调用可以达到预期的目的。实际结果和预期结果都是可能的,因为这是一种竞赛条件。在@(更新电动汽车)响应之前,没有任何东西可以阻止第二个->更新电动汽车触发
我通常建议人们避免SV事件,在这种情况下,函数调用可以达到预期的目的。事件触发器
->
可以根据调度顺序显示为堆栈。LRM声明调度器可以不确定的顺序评估同一区域中的事件。我使用过的每一个模拟器似乎都没有随机化事件顺序;他们倾向于优先考虑编译顺序
让我们为fork中的线程指定标签名称。然后浏览一个可能的场景
- disp_thread将是具有
永久性的进程
- 线程A将是
A=1'b0的进程代码>
- 线程B将是
B=1'b1的进程代码>
@(更新ev)时停止代码>。然后线程A将启动并立即睡眠(将在#10
中行走)线程B统计并睡眠#10
。没有计划任何操作时,模拟将不正确的时间步线程A和线程B唤醒,然后线程A首先执行并触发->更新ev
并完成disp\U thread现在已解锁,模拟器有一个选择:继续disp\U thread或thread\U B。通常,它会选择继续执行disp_线程,并启动第二个循环(因为forever在同一个进程线程中),然后在@处再次暂停(update_ev)代码>线程B现在运行一个更改,它触发->update\u ev
并完成。再次取消阻止disp_线程,以便它可以再次运行
如果将事件触发器移到事件等待语句上方,则可能会看到仅发出一次的$display
消息。然而,这是一个脆弱的解决方案,不会重新开始
而是使用非阻塞事件触发器(->
)。这将移动调度程序的事件触发器区域。在解除阻止disp_线程之前,允许两个触发线程运行并计划更新。变更后的时间表如下:
模拟器首先执行disp_thread,到达@(更新ev)时停止代码>。然后线程A将启动并立即睡眠(将在#10
中行走)线程B统计并睡眠#10
。没有计划任何操作时,模拟将不正确的时间步线程A和线程B唤醒,并且线程A首先执行,并为NBA计划->>更新线程ev
,然后完成。此时,调度程序仍在活动区域中显示线程仍被阻止线程B(当前唯一可以运行的东西)将运行并计划NBA区域的->update\u ev
,并完成。注意到可以在当前区域中运行,调度程序继续到下一个非空区域;美国篮球职业联盟update_ev
的事件触发器同时发生。调度程序重新进入活动区域,此时disp_thread已解锁
注意:如果您计划使用此功能,请保持简单,并使自己非常熟悉调度语义(§4)。可预测性将随着复杂性的增加而变得脆弱(如未阻塞的阻塞事件触发新事件)。当可预测性中断时,它可能从不同的模拟器、版本、机器、操作系统、系统时间、系统负载等随机发生。事件触发器->
可能会根据调度顺序显示为堆栈。LRM声明调度器可以不确定的顺序评估同一区域中的事件。我使用过的每一个模拟器似乎都没有随机化事件顺序;他们倾向于优先考虑编译顺序
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
让我们为fork中的线程指定标签名称。然后浏览一个可能的场景
- disp_thread将是具有
永久性的进程
- 线程A将是
A=1'b0的进程代码>
- 线程B将是
B=1'b1的进程代码>
模拟器首先执行disp_thread,到达@(更新ev)时停止代码>。然后线程A将启动并立即睡眠(将在#10
中行走)线程B统计并睡眠#10
。没有计划任何操作时,模拟将不正确的时间步线程A和线程B唤醒,然后线程A首先执行并触发->更新ev
并完成disp\U thread现在已解锁,模拟器有一个选择:继续disp\U thread或thread\U B。通常,它会选择继续执行disp_线程,并启动第二个循环(因为forever在同一个进程线程中),然后在@处再次暂停(更新)