fork join和for循环结合的实际应用

fork join 结构中,父进程会被阻塞直到所有的子进程结束;
fork...join_any 结构中,父进程会被阻塞直到其中任意一个子进程结束;
fork.... join_none 父进程会立即与产生的所有进程并发执行。
fork中每条语句并行执行 ,如果将多个语句封装在一个 begin end 块中会将整个块 作为单个进程执行,其中每条语句顺序地执行。
实际应用:
实际应用中,我们通常会使用了for循环和fork join_none来并行启动多个进程(这样不用写哪些重复的代码)。
for+fork...join
首先说明使用for+fork...join是 不能实现并行启动多个进程,因为fork...join是会阻塞父进程的,外部的for循环是主程序一部分,所以fork...join也会阻塞for循环的进行。如下代码的结果·中,从打印的时间,看三次for循环并没有并行(即使使用automatic也一样的,这是fork...join阻塞父进程决定的):
`timescale 1ns/1ns

module top();
	int a;
	int i;

	task lab(int i);
		a<=i;
		#1;
		$display("a=%0d,time=%0t",i,$time);
	endtask
	initial begin

		for(int j=0;j<3;j++) begin
			fork
				$display("j=%0d,time=%0t",j,$time);
				lab(j);
			join
		end
		$display("main,time=%0t",$time);
		#10;
		$finish;
	end
endmodule

结果:

*Verdi* : Begin traversing the scopes, layer (0).
*Verdi* : Enable +all dumping.
*Verdi* : End of traversing.
ucli% run 1000ns
j=0,time=0
a=0,time=1
j=1,time=1
a=1,time=2
j=2,time=2
a=2,time=3
main,time=3
$finish called from file "lab13_fork.sv", line 22.
$finish at simulation time                   13
           V C S   S i m u l a t i o n   R e p o r t 

for+fork...join_none

fork...join_none则不会阻塞父进程,因此使用for+fork...join_none才能实现并行启动多个进程。当我们将上面代码改为fork...join_none:

代码:

`timescale 1ns/1ns

module top();
	int a;
	int i;

	task lab(int i);
		a<=i;
		#1;
		$display("a=%0d,time=%0t",i,$time);
	endtask
	initial begin

		for(int j=0;j<3;j++) begin
			fork
				$display("j=%0d,time=%0t",j,$time);
				lab(j);
			join_none
		end
		$display("main,time=%0t",$time);
		#10;
		$finish;
	end
endmodule

结果:

*Verdi* : Begin traversing the scopes, layer (0).
*Verdi* : Enable +all dumping.
*Verdi* : End of traversing.
ucli% run 1000ns
main,time=0
j=3,time=0
j=3,time=0
j=3,time=0
a=3,time=1
a=3,time=1
a=3,time=1
$finish called from file "lab13_fork.sv", line 22.
$finish at simulation time                   10
           V C S   S i m u l a t i o n   R e p o r t 

可以看到打印的三个时间是一样的了说明是并行,但是a的值是一样的,这是因为SV中function和task默认都是静态的(注意:在module或initial中是默认静态的,所以这里表现出来是静态的。来自路桑的提醒),这些静态的function和task里面的局部变量会使用共享的静态存储区,所以不同的线程之间会窜用这些变量,可以在unction和task加入automatic改做动态的,此外还需要一个动态变量来接收for循环的j,不然结果依然,这是因为并行启动时j已经等于3了,此时各个进程会共用一个j。改变后代码如下:

`timescale 1ns/1ns

module top();
	int a;
	int i;

	task automatic lab(int i);
		a<=i;
		#1;
		$display("a=%0d,time=%0t",i,$time);
	endtask
	initial begin

		for(int j=0;j<3;j++) begin
			fork
				automatic int k=j;
				$display("k=%0d,time=%0t",k,$time);
				lab(k);
			join_none
		end
		$display("main,time=%0t",$time);
		#10;
		$finish;
	end
endmodule

结果:

*Verdi* : Begin traversing the scopes, layer (0).
*Verdi* : Enable +all dumping.
*Verdi* : End of traversing.
ucli% run 1000ns
main,time=0
k=0,time=0
k=1,time=0
k=2,time=0
a=0,time=1
a=1,time=1
a=2,time=1
$finish called from file "lab13_fork.sv", line 23.
$finish at simulation time                   10
           V C S   S i m u l a t i o n   R e p o r t 

但是上面结果有另外一个问题,如果for循环后面display这个语句执行是和整个循环并行的(打印的time是0),因为fork...join_none并没有阻塞父进程。如果for后面代码依赖for循环内的并行进程时,我们就希望在for内所有进程都执行完再执行for后面的父进程。此时,我们就需要加上一个隔离进程。

for+fork...join_none+fork..join

因为fork...join可以阻塞父进程,所以我们可以在for外面在套一个fork...join作为隔离,此外我们需要使用wait fork来等待子进程执行完成。如下结果中我们可以看到display的打印已经在time=1。说明for后面主进程是在并行进程执行完之后执行的。

代码:

`timescale 1ns/1ns

module top();
	int a;
	int i;

	task automatic lab(int i);
		a<=i;
		#1;
		$display("a=%0d,time=%0t",i,$time);
	endtask
	initial begin
		fork 
			begin
				for(int j=0;j<3;j++) begin
					fork
						automatic int k=j;
						$display("k=%0d,time=%0t",k,$time);
						lab(k);
					join_none
				end
				wait fork;
			end
		join
		$display("main,time=%0t",$time);
		#10;
		$finish;
	end
endmodule

结果:

*Verdi* : Enable +all dumping.
*Verdi* : End of traversing.
ucli% run 1000ns
k=0,time=0
k=1,time=0
k=2,time=0
a=0,time=1
a=1,time=1
a=2,time=1
main,time=1
$finish called from file "lab13_fork.sv", line 27.
$finish at simulation time                   11
           V C S   S i m u l a t i o n   R e p o r t 

automatic int k=j;这句和其余两句如果放在begin ... end中,比如:

结果是:

 结果会是k都一样都为最后一个(这个的原因:我自己的理解是automatic也在begin end中那么对于父进程(fork join_none)就是不可见的,因为是局部的,这样相当于又隔了一层,而automatic如果是在begin end外面那么对于父进程(fork join_none)就是可见的,对于父进程来说他就是父进程的automatic 变量,并且对父进程的子进程可见)。如果automatic的这句单独用begin end包起来,这样k属于局部变量在第二个begin begin中会不能使用。

在实际代码中,我们可能需要同时送不同的item给不同的sequencer,而这不同的item之间又需要同步发送,那么就需要使用这样的组合。比如下面代码:

		fork
			begin
			for(int j=0;j<seq_id.size();j++) begin
				fork 
					automatic int k=j;
					begin
						string tr_name=$sformatf("px_tr_seq%0d",k);
						px_tr_img[k]=pixel_transaction::type_id::create(tr_name, ,get_full_name());
						`uvm_info("image_gen_sequence",$sformatf("Send sequence%0d's px_tr_img ...",seq_id[k]),UVM_LOW);
						start_item(px_tr_img[k],-1,p_sequencer.px_sqr[k]);
						px_tr_img[k].copy(px_tr);
						for(int i=0;i<(2+1);i++) begin//parameter from ghl
							px_tr_img[k].data_in0[i]=block_img[k][0][i];
							px_tr_img[k].data_in1[i]=block_img[k][1][i];
						end
						finish_item(px_tr_img[k]);
						`uvm_info("image_gen_sequence",$sformatf("Finish send sequence%0d's px_tr_img ...",seq_id[k]),UVM_LOW);
					end
				join_none
			end

			wait fork;
			end
		join

注意px_tr_img一定不能申明为队列,因为是并行,而队列中如果前一个元素没有创建,后一个元素是不能创建的,所以px_tr_img只能是定数组或动态数组。(提醒自己)

  • 26
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值