Systemverilog(綠皮書)第七章——線程及其通信(二)線程控制

initial begin
    $display("@%0t: start fork ... join example", $time);
    #10 $display("@$0t: sequential after #10", $time);
    fork
        $display("@%0t: parallel start", $time);
        #50 $display("@%0t: parallel after #50", $time);
        #10 $display("@%0t: parallel after #50", $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: after join", $time);
end

本例中fork...join開了4個線程(即並行運行程序,其中begin...end是順序執行,算作一個線程)。 initial 塊執行結束一共需要10+50+80=140ns。其中打印的順序爲下,在140ns 中全部執行結束。 

@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

如果替換爲fork...join_any會怎麼樣呢?

initial begin
    $display("@%0t: start fork ... join example", $time);
    #10 $display("@$0t: sequential after #10", $time);
    fork
        $display("@%0t: parallel start", $time);
        #50 $display("@%0t: parallel after #50", $time);
        #10 $display("@%0t: parallel after #50", $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: after join", $time);
end

本例中fork...join開了4個線程(即並行運行程序,其中begin...end是順序執行,算作一個線程)。 initial 塊執行結束一共需要10+80=90ns。其中,fork...join_any語句執行完$display("@%0t: parallel start", $time);語句耗時爲0,之後就跳出join_any語句。其中打印的順序爲下,在90ns 中全部執行結束。 

@0: start fork...join 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

如果替換爲fork...join_none會怎麼樣呢?

initial begin
    $display("@%0t: start fork ... join example", $time);
    #10 $display("@$0t: sequential after #10", $time);
    fork
        $display("@%0t: parallel start", $time);
        #50 $display("@%0t: parallel after #50", $time);
        #10 $display("@%0t: parallel after #50", $time);
        begin
            #30 $display("@%0t: sequential after #30", $time);
            #10 $display("@%0t: sequential after #10", $time);
        end
    join_none
    $display("@%0t: after join", $time);
    #80 $display("@%0t: after join", $time);
end

本例中fork...join開了4個線程(即並行運行程序,其中begin...end是順序執行,算作一個線程)。 initial 塊執行結束一共需要10+80=90ns。其中,fork...join_none語句不執行完$display("@%0t: parallel start", $time)直接跳出join_any語句執行$display("@%0t: after join", $time);其中打印的順序爲下,在90ns 中全部執行結束。 

@0: start fork...join example
@10: sequential after #10
@10: after join_any
@10: parallel start
@20: parallel after #10
@40:  sequential after #30
@50:  sequential  after #10
@60:  parallel after #50
@90: finish after #80

問:如果上述三段代碼都沒有最後一段打印語句:#80 $display("@%0t: after join", $time)。誰可以把之前的句子全部打印完。

本題主要考察initial最後語句執行結束後,程序就刻結束執行。因此,fork...join_any和fork...join_none都沒有機會把全部程序執行完。 

在SV 中,當程序中inital塊全部執行完畢,仿真其就推出了

如果我們希望等待fork塊中所有的線程執行完畢再退出initial塊,此時使用wait fork語句等待所有子線程結束。

task run_threads;
    ...
    fork
        check_trans(tr1);    //線程1
        check_trans(tr2);    //線程2
        check_trans(tr3);    //線程3
    join_none
    ...
    //等待所有的fork中線程結束後在退出task
    wait fork;
endtask

停止單個線程

在使用fork...jion_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

其中,fork...join_none線程中間還有一個fork...join_any線程作爲並行的塊。fork...join_any塊中只要 滿足begin...end或者#TIME_OUT中的一個,那麼就會退出(誰先執行完成,就是哪個)。這裏只要其中一個線程執行完畢,自動關閉另一個線程,節約資源。 

停止多個線程

disable fork可以停止當前線程中衍生出的所有子線程。

initial begin
    check_trans(tr0);    //線程0
    //創建一個線程來限制disable fork的作用範圍
    fork    //線程1
        begin
            check_trans(tr1);    //線程2
            fork                 //線程3
                check_tans(tr2);    //線程4
            join
            //停止線程1-4,單獨保留線程0
            #(TIME_OUT/2) disable fork
        end
    join
end

disable線程1,以及其衍生出來的線程。

停止多次 調用的任務

如果你給某一個任務或者線程指明標號,那麼當這個線程被調用多次後,如果通過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 enteriing thread", $time,id);
                    #TIME_OUT;
                    $display("@%0t; %m: %0d done", $time,id);
                end
            join_none
endtask

該任務只有id=0的時候,等待2ns之後禁止任務線程。

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延時之後禁止了該任務,由於三個線程均是同名線程,因此這些線程都被禁止了,最終也沒有完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章