systemverilog中for/foreach並行執行

NullBeer發表於2024-08-04

目錄
  • for-join_none並行
  • foreach並行

for-join_none並行

for迴圈fork-join_none語句可以組合使用來並行執行多個塊,這裡必須使用非阻塞的fork-join_none來啟動多執行緒,因為使用fork-join_none時每一次迴圈都會建立新的fork塊,並且不影響之後建立fork塊,而fork-join則會阻塞後面的fork塊的執行,主要是保證fork塊內的執行緒並行執行。但其實在使用for-join_none並行執行時,很容易出現錯誤情況,下面先描述一種常見錯誤情況。

program no_auto;
  initial begin
  for(int j=0;j<3;j++)
    fork
      $write(j);
    join_none
  #0 $display("\n");
  end
endprogram

上面程式碼本意時每個執行緒依次列印0,1,2,但在執行執行緒前需要在#0時延之前計算完成。因此依次執行下面程式碼

  1. j=0 建立write(j)——執行緒0,建立不執行
  2. j=1 建立write(j)——執行緒1,-建立不執行
  3. j=2 建立write(j)——執行緒2,-建立不執行
  4. #0 執行消耗時間前,多執行緒同時執行
  5. j=2 執行write(j)——執行緒0
  6. j=2 執行write(j)——執行緒1
  7. j=2 執行write(j)——執行緒2
  8. $display("\n")——主執行緒執行

而由於多執行緒之間使用的時同一個變數,則導致執行緒0、1、2列印的都為j=2.
因此為了避免這種錯誤,可以使用自動變數來儲存變數的複製,之後每一個執行緒都會建立自動變數k並儲存一次j的值,#0之後三個執行緒將列印出其複製值k,而不再是共同且唯一的變數j。

program no_auto;
  initial begin
  for(int j=0;j<3;j++)
    //當然也可以放在這裡
    //automatic int k=j;
    fork
      automatic int k=j;
      $write(k);
    join_none
  #0 $display("\n");
  end
endprogram

當然如果程式碼是在automatic型別的程式碼或者模組裡面,那麼宣告時可以不適用關鍵詞automatic宣告變數,而是可以直接在迴圈中使用變數,即可自動在每個執行緒中建立變數。

program automatic auto;//透過指定程式碼塊為自動變數儲存,此時在for迴圈中的k
  initial begin					//在每一次迴圈中都會給定不同的儲存空,此時多次呼叫
    for(int j=0;j<3;j++)//將不會存在問題
    int k=j;
    fork
      $write(k);
    join_none
  #0 $display("\n");
  end
endprogram

但在UVM中是不是需要必須指明automatic程式碼塊呢?
答案是不一定的,因為SV中的class的方法就預設是automatic模式,因此不需要特別的在fork前面的外部自動變數做automatic int k=j的宣告,直接按照變數宣告即可 int k=j,但為保證含義清晰,最好新增上automatic

foreach並行

foreach相比於for迴圈,其需要輸入陣列變數,foreach(變數[迭代器]]),輸入變數少,而且可以更方便的迭代。常見用法包括可以在UVM環境中將並行啟動多個seqence,將seqence同時傳送到多個agent上。


  begin : foreach_fork
    seq_class seq [`CONST];
    foreach(env.agt[i])
      begin
        automatic int j = i;
          seq[j] = seq_class::type_id::create
                  (.name($sformatf("seq_%0d", j)), .contxt(get_full_name()));
        fork
          begin
            seq[j].start(env.agt[j].sqr);
          end
        join_none // non-blocking thread
      end
    wait fork; //等待
  end : foreach_fork  

具體執行過程如下:

  1. foreach迴圈遍歷每一個env.agt集合的每個代理。
  2. automatic int j = i;建立為唯一的索引,對於避免併發問題,這裡相當重要,由於這裡是類的方法,所以可以不加automatic而單純是int j=1,並建立對應的sequence.
  3. 使用 fork-join_none 進行並行化啟動,此在fork-join_none中新執行緒來並執行其中的程式碼塊,此關鍵字指示模擬器繼續執行,而無需等待已經啟動的執行緒完成,這允許迴圈繼續迭代併為其他代理啟動新執行緒。
  4. seq.start(env.agt[j].sqr):此行使用sequence在env.agt[j].sqr上啟動。
  5. wait fork:所有執行緒啟動完成之後,並在此行之前同時執行。此語句使主執行緒等待,直到所有先前fork的執行緒(來自foreach)完成執行,這確保所有執行緒的sequence傳送完成。

參考文獻
[1]How can I use foreach and fork together to do something in parallel?
[2] 【system verilog】fork-join_none與迴圈語句共同使用的行為探究_fork join none-CSDN部落格
[3] 綠皮書

相關文章