System verilog Systemverilog-触发同一事件的多个进程

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

为什么我会得到下面显示的结果?我希望update_ev事件的多个触发器会导致显示“Main Loop,…”执行两次。但它只执行一次

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的进程
模拟器首先执行disp_thread,到达
@(更新ev)时停止。然后线程A将启动并立即睡眠(将在
#10
中行走)线程B统计并睡眠
#10
。没有计划任何操作时,模拟将不正确的时间步线程A线程B唤醒,然后线程A首先执行并触发
->更新ev
并完成disp\U thread现在已解锁,模拟器有一个选择:继续disp\U threadthread\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 threadthread\U B。通常,它会选择继续执行disp_线程,并启动第二个循环(因为forever在同一个进程线程中),然后在
@处再次暂停(更新)