SV - fork join_none 套用循环时需要注意的点
在验证过程中,我们不得不接触fork join_none配合循环的例子。例如,我们在Monitor需要检测多个通道的输出,可能就需要用到fork join_none。让程序后台生成多个线程,同时Monitor监测的多路结果输出到对应通道的scoreboard中。
又比如,假设DUT有4个数据口。我们需要在UVM的body()函数里把4个sequence同时挂载到对应的psequencer上,可能也会用到这种语法(详见下图)。或许有看客想到,这种我不是可以用fork join实现吗?诚然,但是如果你有300个sequence呢?难道光是一个挂载sequence的代码就300行吗?
注:此处的wait fork不可以省略。因为系统在挂载成功后,会直接进入endtask。此后,系统认为sequence已执行完毕,从而清理sequence之前占据的空间。事实上,这几个sequence根本没有执行。
目录
- Fork join_none的automaic问题
动态变量的创建时间
什么是procedual_statement
静态程序
改善第1步
改善第2步
引生的问题 - Fork join_none的位置问题
- Fork join_none中使用begin end
- references
Automaic问题
动态变量的创建时间
我们首先需要明确一个概念:Automatic variables get created upon entry and initialized before executing any procedural statement within the block they are located in.
什么是procedual_statement
以下的语句都是procedural statement:
1 | initial // enable this statement at the beginning of simulation and execute it only once |
静态程序
首先来看看,下面这个例子。program是静态的,所以 i 是一个静态变量,这个临时变量 i 会用一个存储空间。所以最终输出的值应该是 i 的最终值5。
1 | program fork_join; |
1 | driving port 5 |
改善第1步
为改善之前的状态,我们需要将program声明为动态的。但是我们会发现发现子线程拿到的 i 依旧最后保存的5。这又是为什么呢?
这是因为send(i)是一个独立的statement位于fork join_none中,所以fork join_none会先生成5个子线程,再去创建和初始化变量i。所以最终5个子线程拿到的是 i=5。
1 | program automatic fork_join; |
1 | driving port 5 |
改善第2步
开篇提到,动态变量的创建和初始化是优先于task这个procedural statement的。我们不如多申请一个临时的动态变量 j。并且每次循环时,并行的把当前的 i 赋值给 j(注意此时不能时候用begin end,否则将会把两个statement包裹成为一个statement,出现类似于上个程序的情况)。这样让5个在线程可以拿到分配了5个空间的变量temp。这样最终可以使得子线程拿到 0 - 4的变化值。
1 | program automatic fork_join; |
1 | driving port 0 |
引生的问题:
有同学可能会有疑惑,例2中的task send(int j)不是有一个变量吗?难道send(i)的时候不会做形式参数的拷贝吗?
确实,也会做拷贝。但是send里的形式参数j是栈里的局部变量,这个变量不是动态的。让我做一个测试,将这个形式参数声明为automatic:
1 | program automatic fork_join; |
1 | Error-[IDM] Invalid declaration modifier |
从报错信息看,这个automatic并不能作为task的参数声明。
Join_none的位置问题
目前,我们的fork join_none都是在for循环下做声明。假设我们声明在task内部有会怎么样呢?
1 | program automatic fork_join; |
1 | driving port 0 |
从结果上看出,因为每次给task的时候,i 的值会更新,所以子线程拿到的就是5个不同的变量i。
Join_none中使用begin end
有同学可能有疑惑,假如我们把例3的程序中的并行线程改成串行,会有什么结果呢?让我回顾一下之前讲的内容:这是因为begin end将两个statement包裹成了是一个独立的statement,所以fork join_none会先生成5个子线程,再去创建和初始化变量i。
让我们看看仿真结果:
1 | program automatic fork_join; |
1 | driving port 5 |
references
- https://verificationacademy.com/forums/systemverilog/fork-joinnone-inside-loop
- http://www.asic-world.com/systemverilog/procedure_ctrl1.html
- 张强. UVM 实战[M]. Ji xie gong ye chu ban she, 2014.