Systemverilog - 触发相同事件的多个进程

时间:2015-07-21 13:08:33

标签: system-verilog

为什么我得到如下所示的结果?我希望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
// -------------------------------------------------------

3 个答案:

答案 0 :(得分:0)

实际和预期的结果都是可能的,因为它是一种竞争条件。在@(update_ev)响应之前,没有什么可以阻止第二个> update_ev触发。

我通常建议人们避免使用SV事件,在这种情况下,函数调用可以达到预期目的。

答案 1 :(得分:0)

事件触发器->可能看起来是堆栈,具体取决于调度顺序。 LRM声明调度程序可以以不确定的顺序评估同一区域中的事件。我使用的每个模拟器似乎都没有随机化事件顺序;他们倾向于优先编制订单。

让我们给fork中的线程赋予标签名称。然后介绍一个可能的场景。

  • disp_thread 将成为forever
  • 的流程
  • thread_A 将成为A = 1'b0;
  • 的流程
  • thread_B 将成为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