`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只能是定数组或动态数组。(提醒自己)