1、線程的使用
1.1程序和模塊
? module (模塊)作為SV從Verilog繼承過來的概念,自然地保持了它的特點除了作為RTL模型的外殼包裝和實現硬件行為, 在更高層的集成層面,模塊之間也需要通信和同步。
? 對于硬件的過程塊,它們之間的通信可理解為不同邏輯/時序塊之間的通信或者同步,是通過信號的變化來完成的。
? 從硬件實現的角度來看,Verilog通過always,initial過程語句塊和信號數據連接實現進程間通信。
? 我們可以將不同的module作為獨立的程序塊,他們之間的同步通過信號的變化(event觸發)、等待特定事件(時鐘周期)或者時間(固定延時)來完成。
如果按照軟件的思維理解硬件仿真,仿真中的各個模塊首先是獨立運行的線程(thread)。
模塊(線程)在仿真一開始便并行執行, 除了每個線程會依照自身內部產生的事件來觸發過程語句塊之外, 也同時依靠相鄰模塊間的信號變化來完成模塊之間的線程同步。
? 線程即獨立運行的程序。
? 線程需要被觸發, 可以結束或者不結束。
? 在module中的initial和always,都可以看做獨立的線程,它們會在仿真0時刻開始,而選擇結束或者不結束。
? 硬件模型中由于都是always語句塊 , 所以可以看成是多個獨立運行的線程, 而這些線程會一直占用仿真資源, 因為它們并不會結束。
? 軟件測試平臺中的驗證環境都需要由initial語句塊去創建, 而在仿真過程中, 驗證環境中的對象可以創建和銷毀, 因此軟件測試端的資源占用是動態的。
軟件環境中的initial塊對語句有兩種分組方式 , 使用begin ... end或fork... join。
begin ... end的語句以順序的方式執行,而fork...join中的語句則以并發方式執行。
與fork... join類似的并行方式語句還包括fork...join_any,fork...join_none.
? 線程的執行軌跡是呈樹狀結構的, 即任何的線程都應該有父線程。
? 父線程可以開辟若干個子線程, 父線程可以暫停或者終止子線程。
? 當子線程終止時, 父線程可以繼續執行。
? 當父線程終止時, 其所開辟的所有子線程都應當會終止。
2、線程的控制
2.1 fork.... join
//fork...join initial begin $display("@%0t:start fork... join example", $time); #10 $display("@%0t:sequential after #10", $time); fork $display("@%Ot: parallel start", $time); #50 $display("@%0t:parallel after #50", $time); #10 $display("@%0t: parallel after #lO", $time); begin #30 $display("@%0t:sequential after #30", $time); #10 $display("@%0t:sequential after #10", $time); end join $display ("@%0t:after join", $time); #80 $display("@%0t: finish after #80", $time); end?
打印代碼:
@0: start fork... join example
@10: sequential after #10
@10: parallel start
@20: parallel after #10
@40: sequential after #30
@50: sequential after #10
@60: parallel after #50
@60: after join
@140: finish after #80
2.2 fork...join_any
//fork...join_any initial begin $display("@%0t:start fork... join_any example", $time); #10 $display("@%0t:sequential after #10", $time); fork $display("@%Ot: parallel start", $time); #50 $display("@%0t:parallel after #50", $time); #10 $display("@%0t: parallel after #lO", $time); begin #30 $display("@%0t:sequential after #30", $time); #10 $display("@%0t:sequential after #10", $time); end join_any $display ("@%0t:after join", $time); #80 $display("@%0t: finish after #80", $time); end
打印代碼:
@0: start fork... join_any example
@10: sequential after #10
@10: parallel start
@10: after join_any
@20: parallel after #10
@40: sequential after #30
@50: sequential after #10
@60: parallel after #50
@90: finish after #80
2.3 fork...join_none
//fork...join_none initial begin $display("@%0t:start fork... none example", $time); #10 $display("@%0t:sequential after #10", $time); fork $display("@%Ot: parallel start", $time); #50 $display("@%0t:parallel after #50", $time); #10 $display("@%0t: parallel after #lO", $time); begin #30 $display("@%0t:sequential after #30", $time); #10 $display("@%0t:sequential after #10", $time); end join_none $display ("@%0t:after join_none", $time); #80 $display("@%0t: finish after #80", $time); end
打印代碼:
@0: start fork... join_none example
@10: sequential after #10
@10: after join_none
@10: parallel start
@20: parallel after #10
@40: sequential after #30
@50: sequential after #10
@60: parallel after #50
@90: finish after #80
2.4 等待所有衍生線程
? 在SV中, 當程序中的initial塊全部執行完畢, 仿真器就退出了。
? 如果我們希望等待fork塊中的所有線程執行完畢再退出結束initial塊, 我們可以使用wait fork語句來等待所有子線程結束。
task run_threads; ... fork check_trans(trl); //線程1 check_trans(tr2); //線程2 check_trans(tr3); //線程3 join_none ... //等待所有fork中的線程結束再退出task wait fork; endtask
2.5 停止單個線程
在使用了fork.. join_any或者fork... join_none以后,我們可以使用disable來指定需要停止的線程。
parameter TIME_OUT = 1000; task check_trans{Transaction tr); fork begin //等待回應,或者達到某個最大時延 fork: timeout_block begin wait(bus.cb.addr == tr.addr); $display("@%0t: Addr match %d", $time, tr.addr); end #TIME_ OUT $display ("@%0t:Error: timeout, $time); join_any disable timeout_block; end join_none endtask
2.6 停止多個線程
disable fork可以停止從當前線程中衍生出來的所有子線程。
initial begin check_trans(trO); //線程0 //創建一個線程來限制disable fork的作用范圍 fork//線程1 begin check_trans(trl); //線程2 fork//線程3 check_trans(tr2); / /線程4 join //停止線程1-4, 單獨保留線程0 #(TIME_OUT/2) disable fork end join end
2.7 停止被多次調用的任務
如果你給某—個任務或者線程指明標號, 那么當這個線程被調用多次以后 , 如果通過disable去禁止這個線程標號, 所有衍生的同名線程都將被禁止。
task wait_for_time_out(int id); if (id == 0) fork begin #2; $display("@%0t:disable wait_for_time_out"' $time); disable wait_for_time_out; end join_none fork : just_a_little begin $display ("@%0t:%m: %0d entering thread", $time, id); #TIME_OUT; $display("@%0t:%m: %0d done", $0ime, id); end join_none endtask initial begin wait_for_time_out(0); // Spawn thread 0 wait_for_time_out(1); // Spawn thread 1 wait_for_time_out(2); // Spawn thread 2 #(TIME_OUT*2) $display("@%0t:All done", $time); end
? 任務wait_for_time_out被調用了三次, 從而衍生了三個線程。
? 線程0在#2延時之后禁止了該任務,而由于三個線程均是“ 同名”線程, 因此這些線程都被禁止了, 最終也都沒有完成。
內容來源:IC修真院優秀學員
審核編輯:湯梓紅
-
Verilog
+關注
關注
28文章
1343瀏覽量
109983 -
時鐘
+關注
關注
10文章
1720瀏覽量
131362 -
RTL
+關注
關注
1文章
385瀏覽量
59699 -
程序
+關注
關注
116文章
3776瀏覽量
80848 -
線程
+關注
關注
0文章
504瀏覽量
19651
原文標題:IC學霸筆記 | SV線程
文章出處:【微信號:IC修真院,微信公眾號:IC修真院】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論