systemverilog中for/foreach并行执行
for-join_none并行
for循环和fork-join_none语句可以组合使用来并行执行多个块,这里必须使用非阻塞的fork-join_none来启动多线程,因为使用fork-join_none时每一次循环都会创建新的fork块,并且不影响之后创建fork块,而fork-join则会阻塞后面的fork块的执行,并且保证fork块内的线程并行执行。但其实在使用时,很容易出错,下面先描述一种常见错误情况。
program no_auto;
initial begin
for(int j=0;j<3;j++)
fork
$write(j);
join_none
#0 $display("\n");
end
endprogram
上面代码本意时每个线程依次打印0,1,2,但当运行消耗时间之前需要在不消耗时间的函数在#0时延之前计算完成。因此依次执行下面代码
j=0创建write(j)——线程0,创建不执行j=1创建write(j)——线程1,-创建不执行j=2创建write(j)——线程2,-创建不执行- 在
#0执行消耗时间前,多线程同时执行 j=2执行write(j)——线程0j=2执行write(j)——线程1j=2执行write(j)——线程2$display("\n")——主线程执行
而由于多线程之间使用的时同一个变量,则导致线程0、1、2打印的都为j=2.
因此为了避免这种错误,可以使用自动变量来保存变量的拷贝,之后每一个线程都会创建自动变量k并保存一次j的值,#0之后三个线程将打印出其拷贝值k,而不再是共同且唯一的变量j。
program no_auto;
initial begin
for(int j=0;j<3;j++)
//当然也可以放在这里
//automatic int k=j;
fork
automatic int k=j;
$write(k);
join_none
#0 $display("\n");
end
endprogram
当然如果代码是在automatic类型的代码或者模块里面,那么声明时可以不适用关键词automatic声明变量,而是可以直接在循环中使用变量,即可自动在每个线程中创建变量。
program automatic auto;//通过指定代码块为自动变量存储,此时在for循环中的k
initial begin //在每一次循环中都会给定不同的存储空,此时多次调用
for(int j=0;j<3;j++)//将不会存在问题
int k=j;
fork
$write(k);
join_none
#0 $display("\n");
end
endprogram
但在UVM中是不是需要必须指明
automatic代码块呢?
答案是不一定的,因此SV中的class的方法就默认是automatic模式,因此不需要特别的在fork前面的外部自动变量做automatic int k=j的声明,直接按照变量声明即可int k=j,但为保证含义清晰,最好添加上automatic。
foreach并行
foreach相比于for循环,其需要输入数组变量,foreach(变量[迭代器]]),输入变量少,而且可以更方便的迭代。常见用法包括可以在UVM环境中将并行启动多个seqence,将seqence同时发送到多个agent上。
begin : foreach_fork
seq_class seq [`CONST];
foreach(env.agt[i])
begin
automatic int j = i;
seq[j] = seq_class::type_id::create
(.name($sformatf("seq_%0d", j)), .contxt(get_full_name()));
fork
begin
seq[j].start(env.agt[j].sqr);
end
join_none // non-blocking thread
end
wait fork; //等待
end : foreach_fork
具体执行过程如下:
foreach循环遍历每一个env.agt集合的每个代理。automatic int j = i;创建为唯一的索引,对于避免并发问题,这里相当重要,由于这里是类的方法,所以可以不加automatic而单纯是int j=1,并创建对应的sequence.- 使用
fork-join_none进行并行化启动,此在fork-join_none中新线程来并执行其中的代码块,此关键字指示仿真器继续执行,而无需等待已经启动的线程完成,这允许循环继续迭代并为其他代理启动新线程。 seq.start(env.agt[j].sqr):此行使用sequence在env.agt[j].sqr上启动。wait fork:所有线程启动完成之后,并在此行之前同时执行。此语句使主线程等待,直到所有先前fork的线程(来自foreach)完成执行,这确保所有线程的sequence发送完成。
参考文献
[1]How can I use foreach and fork together to do something in parallel?
[2] 【system verilog】fork-join_none与循环语句共同使用的行为探究_fork join none-CSDN博客
[3] 绿皮书
浙公网安备 33010602011771号