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.

 

posted @ 2023-12-01 14:26  IVY_Liu  阅读(3092)  评论(0编辑  收藏  举报