2015-07-21 91 views
1

为什么我会得到如下所示的结果?我期望多次触发update_ev事件应该会导致显示“Main Loop,..”被执行两次。但它只执行一次。Systemverilog - 多个进程触发相同的事件

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

根据调度顺序,事件触发器->可能显示为堆栈。 LRM指出调度程序可能以不确定的顺序评估同一区域中的事件。我用过的每个模拟器似乎都没有将事件顺序随机化;他们倾向于优先编制订单。

允许给fork中的线程赋予标签名称。然后通过一个可能的场景。

  • disp_thread将与forever
  • 过程thread_A将与A = 1'b0;
  • 过程thread_B将与B = 1'b1;
过程

模拟器首先执行disp_thread并在达到@(update_ev);时停止。然后thread_A将开始并立即睡觉(将在#10步行)。 thread_B stats up,也睡#10。如果没有预定的操作,模拟将会错误地显示时间步。 thread_Athread_B醒来,并thread_A先执行,并触发->update_ev和完成。 disp_thread现在未封闭的,仿真器具有一个选择:继续disp_threadthread_B。通常情况下,它会选择继续disp_thread并开始其第二个循环(因为永远在同一个进程线程中)并在@(update_ev);处再次暂停。 thread_B现在得到一个更改运行,并触发->update_ev并结束。再次disp_thread被解除封锁,因此它可以再次运行。

如果您将事件触发器移至事件等待语句上方,您可能会可能看到$display消息只发出一次。然而,这是一个脆弱的解决方案,不会重新开始。

改为使用非阻塞事件触发器(->>)。这将移动调度器的事件触发器NBA区域。在解锁disp_thread之前允许触发线程运行并安排更新。随着这一变化的时间表将是如下:

模拟器执行disp_thread第一,当它到达@(update_ev);停止。然后thread_A将开始并立即睡觉(将在#10步行)。 thread_B stats up,也睡#10。如果没有预定的操作,模拟将会错误地显示时间步。 thread_Athread_B醒来,并thread_A执行第一和时间表->>update_evNBA和完成。此时调度器仍处于活动区域; disp_thread仍然被阻止。 thread_B(唯一可以运行的当前运行)将运行并安排->>update_evNBA区域和完成。注意到可以在当前区域运行,调度器继续到下一个非空区域; NBA。事件触发到update_ev都发生。调度程序重新进入活动区域disp_thread现在未被阻止。

注意:如果您计划使用此功能,请保持简单并使您对调度语义非常了解(IEEE std 1800-2012 § 4)。可预测性会变得更加脆弱并增加复杂性(例如未阻塞的阻塞事件触发新事件)。当预测破裂,可能会发生随机形成不同的仿真器,版本,机器,操作系统,系统时间,系统负载等

+0

HI Greg,谢谢你的回复。我打算在我的验证环境中使用它。假设解决多个进程(thread_A,thread_B ....)的目的,试图通知一个听力进程(disp_thread)关于共享变量的一些变化。听证过程应该决定,与线程执行的顺序无关,最终结果与前一个结果相同或不同。你认为 - >>应该解决目的吗? –

+0

' - >>'应该在这种情况下工作。另一个选择是在@(update_ev)之后放一点点延迟;假设是否有一点延迟是可以接受的。 – Greg

+0

我永远不会建议在测试台中放一点点延迟。一旦你开始在整个地方推迟延迟来定义排序,你可以结束事情变得难以管理。 –

0

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

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

+0

喜戴夫,如果我想要的结果总是表现为实际的,我可以使用 - >>运算符? –

+0

是的。但考虑其他机制,如信号量或邮箱在线程之间进行通信。列表线程使用分支join_any来等待其他进程。 –

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