system verilog中wait fork和disable fork

disable_fork 解读

 1 timescale 1ns/1ps;
 2 
 3 task jobs();
 4     fork
 5         begin
 6             #10;
 7             $display("delay 10ns")
 8         end
 9         begin
10             #20
11             $display("delay 20ns")
12         end
13     join_any
14     disable_fork
15 
16 endtask

若只执行jobs,结果是fork... join_any会在打印delay 10ns之后结束整个fork逻辑

若:

 1 timescale 1ns/1ps;
 2 
 3 task job_delay_200ns();
 4     #200ns;
 5     $display("job_delay_200ns end")
 6 endtask
 7 
 8 task jobs_test();
 9     fork 
10          job_delay_200ns();
11     join_none
12 
13     jobs();
14 endtask

看起来在打印delay 10ns之后会执行200ns的等待,但是实际上并未执行。

disable_fork 作用范围是当前线程的子线程,以及子线程的子线程全部被kill

整个task jobs_test中,jobs和fork_join_none在一个线程内,展开描述如下:

 1 timescale 1ns/1ps;
 2  
 3 task job_delay_200ns();
 4     #200ns;
 5     $display("job_delay_200ns end")
 6 endtask
 7  
 8 task jobs_test();
 9     fork 
10          job_delay_200ns();
11     join_none
12  
13     fork
14         begin
15             #10;
16             $display("delay 10ns")
17         end
18         begin
19             #20;
20             $display("delay 20ns")
21         end
22     join_any
23     disable_fork
24 endtask

这里task job_delay_200ns和fork join_any中begin .. end线程是同级的,都可以看作是fork join_any的子线程,所以会被kill

解决方法,添加一层guard:

 1 timescale 1ns/1ps;
 2  
 3 task jobs();
 4     fork:guard_fork
 5         begin  
 6             fork
 7                 begin
 8                     #10;
 9                     $display("delay 10ns")
10                 end
11                 begin
12                     #20
13                     $display("delay 20ns")
14                 end
15             join_any
16             disable_fork
17         end
18     join : guard_fork
19 endtask

相当于disable_fork降级,task job_delay_200ns和线程fork join_any是平级的

 

wait fork 解读

wait fork作用的父进程下的子进程,而不包括子进程下的子进程;而disable fork则是作用于父进程下的所有进程,包括子进程的子进程

ps:调用进程和其子进程,调用进程即调用wait fork或者disable fork的进程,或者叫做父进程,而在父进程中创建的其它进程即子进程。

 1 module tb_top;
 2  
 3   initial begin
 4     // Fork off 3 sub-threads in parallel and the currently executing main thread
 5     // will finish when any of the 3 sub-threads have finished.
 6     fork
 7       // Thread1 : Will finish first at time 40ns
 8       #40 $display ("[%0t ns] Show #40 $display statement", $time);    
 9       // Thread2 : Will finish at time 70ns
10       begin
11         #20 $display ("[%0t ns] Show #20 $display statement", $time);
12         #50 $display ("[%0t ns] Show #50 $display statement", $time);
13       end
14       // Thread3 : Will finish at time 60ns
15       #60 $display ("[%0t ns] TIMEOUT", $time);
16     join_any
17         // Display as soon as the fork is done
18         $display ("[%0t ns] Fork join is done, wait fork to end", $time);
19  
20         // Fork two more processes
21         fork
22           #10 $display ("[%0t ns] Wait for 10", $time);
23           #20 $display ("[%0t ns] Wait for 20", $time);
24         join_any
25         // Wait until ALL forked processes are over
26         wait fork;
27         $display ("[%0t ns] Fork join is over", $time);
28    end
29 endmodule
1 ####仿真结果
2 [20 ns] Show #20 $display statement
3 [40 ns] Show #40 $display statement
4 [40 ns] Fork join is done, wait fork to end
5 [50 ns] Wait for 10
6 [60 ns] TIMEOUT
7 [60 ns] Wait for 20
8 [70 ns] Show #50 $display statement
9 [70 ns] Fork join is over

能够建立进程的只有initial块,always块,以及fork语句(也许还有其它的)。 inital、always块、fork join创建静态进程, fork join_any 和 fork join_none创建动态进程。

第一个fork join_any创建了三个进程(三条并行语句),第二个fork join_any创建了两个进程,一共五个。父进程调用的wait fork于是需要等待这五个进程全部结束之后才会打印后面的display,这五个进程结束得最晚的是Thread2, 花费70个仿真时间所以最后一个display打印时间是70 ns。
如果在wait fork之前打印一次时间,应该是50 ns,这个第一个和第二个fork join_any种的最短进程的花费时间之和。

问题升阶:

 1 class A;
 2     string name;
 3     function new (string name ="");
 4         this.name = name;
 5     endfunction
 6     task run (int delay,  string mark);
 7         fork
 8             # delay;
 9         join_none
10         $display ("@%0t-----------%s_run_1", $time,  mark);
11         wait fork;
12         $display ("@%0t-----------%s_run_2", $time,  mark);
13     endtask
14 enclass
15 module Top;
16     A a = new ("a");
17     initial begin
18         fork 
19             # 60;
20         join_none;
21 
22        fork 
23            a.run (5, "thread1");
24            a.run (6, "thread2");
25        join_none
26        fork 
27            a.run (2, "thread3");
28        join_none
29        begin 
30            a.run (1, "thread4");
31        end
32        
33        wait fork;
34        
35         $display ("@%0t-----------%s", $time, "wait 1");
36         fork 
37            a.run (10, "thread5");
38            a.run (20, "thread6");
39        join_none
40        a.run (10, "thread7");
41        
42        wait fork;
43        
44        $display ("@%0t-----------%s", $time, "wait 2");
45     end
46 endmodule
 1 #######仿真结果
 2 @0-----------thread1_run_1
 3 @0-----------thread2_run_1
 4 @0-----------thread3_run_1
 5 @2-----------thread3_run_2
 6 @2-----------thread4_run_1
 7 @5-----------thread1_run_2
 8 @6-----------thread2_run_2
 9 @60-----------thread4_run_2
10 @60-----------wait 1
11 @60-----------thread7_run_1
12 @60-----------thread5_run_1
13 @60-----------thread6_run_1
14 @70-----------thread5_run_2
15 @80-----------thread6_run_2
16 @80-----------thread7_run_2
17 @80-----------wait 2

首先有多个fork join_none创建了多个子进程,以第二个fork join_none为例,里面调用了两个子线程,每个子线程对应一个任务a.run,任务体都可以视为一个begin块, 所以就产生了两个子进程(父进程是initial块),而在这个子进程即任务体中,又使用fork join_none 创建了一个子进程,其父进程则是这个任务体,它是一个子进程的子进程(run任务中的fork join_none)。

wait fork只会阻塞调用它的父进程,所以看打印时间,thread1, thread2, thread3的run_1都是在0时刻打印,而run_2则分别在5, 6, 2时刻打印,而这正是三次调用任务的传递的delay参数。这说明了wait fork不会等待属于父进程的父进程的子进程,只会等待自己的调用进程的子进程结束

run(1, “thread4”)是在一个begin块中被调用了,而wait fork又是在这个任务体中被调用了,但即使有一个begin end,还有一个task endtask封装,这个wait fork的调用进程还是intial块。因为能够创建进程的只有initial块,always块和fork语句。所以thread4的wait fork语句并不处于一个iniital块的子进程中,其父进程还是initial块,自然地,它阻塞了initial继续运行,等待前面挂起的子进程结束,前面挂起的最长的子进程时间是60,所以thread4_run_2的打印时间就是60。
后面的thread7的run_2打印时间是80的原因也是如此,它需要等待新创建的thread5和thread6进程的结束

类似地,如下:

 1 initial begin
 2     fork 
 3         # 30;
 4     join_none
 5     fork
 6         begin 
 7             a.run (10, "thread1");
 8             begin
 9                 a.run (20, "thread2");
10             end
11             wait fork;
12         end
13     join
14     $display ("@%0t-----------%s", $time, "test");
15 end
1 #####仿真结果
2 @0-----------thread1_run_1
3 @0-----------thread2_run_1
4 @10-----------thread1_run_2
5 @20-----------thread2_run_2
6 @20-----------test

这里wait fork不会等待fork join_none中线程结束,是因为fork join_none开辟的子线程和fork join开辟的子线程是同级的,而wait fork所在的线程是begin end所开辟的这个子线程,低于#30一级,所以不会阻塞#30的这个线程

 

posted on 2022-05-20 09:13  薛定谔's猫  阅读(3385)  评论(0)    收藏  举报

导航