- 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時延之前計算完成。因此依次執行下面程式碼
j=0
建立write(j)——執行緒0,建立不執行j=1
建立write(j)——執行緒1,-建立不執行j=2
建立write(j)——執行緒2,-建立不執行- 在
#0
執行消耗時間前,多執行緒同時執行 j=2
執行write(j)——執行緒0j=2
執行write(j)——執行緒1j=2
執行write(j)——執行緒2$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
具體執行過程如下:
foreach
迴圈遍歷每一個env.agt集合的每個代理。automatic int j = i;
建立為唯一的索引,對於避免併發問題,這裡相當重要,由於這裡是類的方法,所以可以不加automatic
而單純是int j=1
,並建立對應的sequence.- 使用
fork-join_none
進行並行化啟動,此在fork-join_none中新執行緒來並執行其中的程式碼塊,此關鍵字指示模擬器繼續執行,而無需等待已經啟動的執行緒完成,這允許迴圈繼續迭代併為其他代理啟動新執行緒。 seq.start(env.agt[j].sqr)
:此行使用sequence在env.agt[j].sqr上啟動。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] 綠皮書