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的这个线程
浙公网安备 33010602011771号