2015-07-21 43 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