SystemVerilog中fork join 和 for循环嵌套问题探讨
一、for循环和fork ...join的循环嵌套
在我们coding时,经常会遇到一种情况,需要并行运行n个线程,这里n是可变的,如果只是简单的列出所有情况比如:
1 fork 2 wait_clk(0); 3 wait_clk(1); 4 wait_clk(2); 5 wait_clk(3); 6 join
很显然这种代码很不利于维护,如果n改变了,那么我们的代码就需要改变。
一种比较好的方式是使用for循环,那么这里就涉及到了fork join 和for循环的嵌套问题
[for循环嵌套fork ... join]
1 task print_num(int dw_num); 2 #1; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 for(int i=0;i<4;i++) begin 8 fork 9 print_num(i); 10 join 11 end 12 endtask
打印出来的消息是:
我们预期的是4个线程并行,但是实验结果4个线程并未如我们所预期的那样并行。
原因:fork join 会阻塞父进程 for循环,那么我们把fork join 换成 fork join_none试试:
[for 循环嵌套fork ... join_none]
1 task print_num(int dw_num); 2 #1; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 for(int i=0;i<4;i++) begin 8 fork 9 print_num(i); 10 join_none 11 end 12 #10ns; 13 endtask
打印结果:
可以看到虽然打印了四次,但是打印的值dw_num=4;
这段代码执行的过程:先for循环产生4个print_num()进程,然后i=4, 执行print_num(i);
(具体可看绿皮书185页)
这里需要使用automatic 型变量,automatic 型变量的声明会和for循环产生线程同时进行,这样四个独立的线程就可以并行执行
1 task print_num(int dw_num); 2 #1; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 for(int i=0;i<4;i++) begin 8 fork 9 automatic int j = i; 10 print_num(j); 11 join_none 12 end 13 #10ns; 14 endtask
打印的结果:
二、for循环和fork ...join_none循环嵌套后顺序执行其他code
如果在上面循环嵌套后仍然想顺序执行其他代码该怎么设置呢?这里需要用到wait fork,等待所有子线程执行完毕。
1 task print_num(int dw_num); 2 #1; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 for(int i=0;i<4;i++) begin 8 fork 9 automatic int j = i; 10 print_num(j); 11 join_none 12 end 13 wait fork; 14 #2 $display("#%0p,wait fork finished.",$time); 15 #10ns; 16 endtask
打印结果:
三、延伸:如何实现n个线程并行,有一个线程完成则退出循环?
能否直接用fork ...join_any 替换fork ...joni_none呢?答案是不可以
直接看打印结果:
这里考虑到fork ...join_any 和fork ...join_none的嵌套
1 task print_num(int dw_num,int time_wait); 2 #time_wait; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 fork 8 begin 9 for(int i=0;i<4;i++) begin 10 automatic int j = i; 11 automatic int time_wait = $urandom_range(1,5); 12 fork 13 begin 14 fork:dw_fork 15 print_num(j,time_wait); 16 join_any 17 disable dw_fork; 18 end 19 join_none 20 end 21 wait fork; 22 end 23 join 24 disable fork; 25 #2 $display("#%0p,wait fork finished.",$time); 26 #10ns; 27 endtask
需要注意的点有:
1. fork ...join_none中的begin ...end不可或缺。
2.测试了一下最外层的fork...join disable fork;可以comment掉。
四、拓展:多个for循环嵌套fork ... join_none
场景一: wait_fork在for循环外
1 task print_num(int dw_num,int time_wait); 2 #time_wait; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 begin 8 for(int m=0;m<2;m++) begin 9 automatic int n=m; 10 for(int i=0;i<4;i++) begin 11 automatic int j = i; 12 automatic int time_wait = $urandom_range(1,5); 13 fork 14 begin 15 //fork:dw_fork 16 //print_num(j+n*4,time_wait); 17 print_num(j+n*4,1); 18 //join_any 19 //disable dw_fork; 20 end 21 join_none 22 end 23 //wait fork; 24 end 25 wait fork; 26 end 27 #2 $display("#%0p,wait fork finished.",$time); 28 #10ns; 29 endtask
这种场景下打印的结果是怎样的呢?
场景二、wait fork 在第一层循环里面,这种场景下打印的结果是怎样的呢?
1 task print_num(int dw_num,int time_wait); 2 #time_wait; 3 $display("#%0p dw_num=%0d",$time,dw_num); 4 endtask 5 6 task for_loop(); 7 begin 8 for(int m=0;m<2;m++) begin 9 automatic int n=m; 10 for(int i=0;i<4;i++) begin 11 automatic int j = i; 12 automatic int time_wait = $urandom_range(1,5); 13 fork 14 begin 15 //fork:dw_fork 16 //print_num(j+n*4,time_wait); 17 print_num(j+n*4,1); 18 //join_any 19 //disable dw_fork; 20 end 21 join_none 22 end 23 wait fork; 24 end 25 //wait fork; 26 end 27 #2 $display("#%0p,wait fork finished.",$time); 28 #10ns; 29 endtask
很好理解,当wait fork在第一层for循环里面的时候,当m=0时内层的fork ...join_none会阻塞外层的for循环,所以#1的时候打印的dw_num0-3,#2时打印的是4-7.