Systemverilog中fork...join等线程的使用 Systemverilog中fork...join等线程的使用前言fork...join 三个变体的区别fork...join 三个变体的典型应用场景fork...join示例fork...join_any示例fork...join_none示例线程控制方法wait forkdisable fork注意事项循环中的自动变量join_none与#0延迟前言在 SystemVerilog​ 中代码总是以initial块启动从0时刻开始执行并且顺序执行代码块。但在某些场景下必须需要某些代码块并行执行这时候就需要用到fork...join了。fork...join及其变体是创建并发线程并发进程的核心机制是测试平台testbench中实现多线程行为的核心结构之一其是构建测试平台Testbench、实现并行激励和检查的关键。它允许在仿真中同时运行多个语句或任务而不是像传统软件那样顺序执行。fork…join 三个变体的区别如上图所示fork...join还存在fork...join_any、fork...join_none两个变体这三者的区别在于何时解除对父线程的阻塞。具体区别如下进程类型行为描述类比fork…join等待所有的子线程完成之后才可以继续执行后续代码整个部队全队集合完毕才可以出发fork…join_any任意一个子线程完成之后就可以继续执行后续代码其余未完成的线程会继续执行只要有一个人都到达终点教练就继续下一个项目fork…join_none立刻执行后续的代码不等待任何子线程所有子线程在后台并发执行发令枪响运动员开跑裁判立刻去忙别的事fork…join 三个变体的典型应用场景fork…join的典型应用场景并行测试同时驱动多个接口且等待全部发送完成后检查结果例如在验证环境中同时发送多笔事务所有事务处理完成后再统一进行对比多通道数据采集从多个接口信号采集数据要确保所有接口均采集完成后再进行下一步处理fork…join_any的典型应用场景:超时保护当前执行的任务和延迟计数器并行运行当前任务和延迟计数器哪个先完成就退出等待竞争仲裁若多个模块同时请求资源哪个模块先完成就先获取到资源fork…join_none的典型应用场景看门狗启动一个信号状态监视的进程该进程用于看门狗不影响主进程的运行动态线程池可在循环中不断启动子线程而主线程可以立刻执行其他操作无需等待每个子线程结束fork…join示例initial begin $display(%0t UVM_Frequency: fork...join Start,$time);fork #10$display(%0t UVM_Frequency: process A,$time);#5$display(%0t UVM_Frequency: process B,$time);#15$display(%0t UVM_Frequency: process C,$time);join $display(%0t UVM_Frequency: fork...join end,$time);end在上述示例中最终的打印信息为0UVM_Frequency:fork...join Start 5ns UVM_Frequency:process B 10ns UVM_Frequency:process A 15ns UVM_Frequency:process C 15ns UVM_Frequency:fork...join end在UVM环境中的运行结果如下fork…join_any示例initial begin $display(%0t UVM_Frequency: fork...join_any Start,$time);fork #10$display(%0t UVM_Frequency: process A,$time);#5$display(%0t UVM_Frequency: process B,$time);#15$display(%0t UVM_Frequency: process C,$time);join_any $display(%0t UVM_Frequency: fork...join_any end,$time);end在上述示例中最终的打印信息为0ns UVM_Frequencyfork...join_any Start 5ns UVM_Frequencyprocess B 5ns UVM_Frequencyfork...join_any end 10ns UVM_Frequencyprocess A 15ns UVM_Frequencyprocess C在UVM环境中的运行结果如下fork…join_none示例initial begin $display(%0t UVM_Frequency: fork...join_none Start,$time);fork #10$display(%0t UVM_Frequency: process A,$time);#5$display(%0t UVM_Frequency: process B,$time);#15$display(%0t UVM_Frequency: process C,$time);join_none $display(%0t UVM_Frequency: fork...join_none end,$time);end在上述示例中最终的打印信息为0ns UVM_Frequencyfork...join_none Start 0ns UVM_Frequencyfork...join_none end 5ns UVM_Frequencyprocess B 10ns UVM_Frequencyprocess A 15ns UVM_Frequencyprocess C在UVM环境中的运行结果如下线程控制方法为了管理好fork...join的并发线程systemverilog提供了两个重要的控制语句wait fork和disable fork。wait fork该语句用于等待当前进程中的所有子进程全部结束后才会结束仿真包括fork...join_none产生的进程具体如下等待所有子进程结束wait fork会等待当前进程直接或间接创建的所有子进程都终止包括执行完成或者被disable fork强行终止灵活的同步点与fork...join必须在同一个fork中等待所有子进程结束不一样wait fork可以在后续任意时刻执行其允许先启动一部分进程执行一些其他操作最后统一等待他们完成即wait fork可跨多个fork块等待调用者所有活跃的子进程结束避免进程泄漏若不使用wait fork则有可能在父进程执行结束之后尚未完成的子进程仍在后台运行有可能会导致信号意外被驱动或者资源占用注意事项只能作用于initial、always、task、function等过程块不关心其他进程只关心当前进程的后代若当前进程没有子进程则wait fork会立即返回不阻塞执行其他进程wait fork代码示例initial begin $display([%0t]UVM_Frequency: paraent process start,$time);fork begin #10;$display([%0t]UVM_Frequency: son process A end,$time);end begin #20;$display([%0t]UVM_Frequency: son process B end,$time);end join_none// 父进程不阻塞$display([%0t]UVM_Frequency: paraent process continue,$time);#5;$display([%0t]UVM_Frequency: paraent process wait son process,$time);wait fork;// 等待所有子进程结束$display([%0t]UVM_Frequency: all son process end,paraent process continue,$time);#10$finish;end上述代码的运行结果为[0]UVM_Frequency:paraent process start[0]UVM_Frequency:paraent processcontinue[5]UVM_Frequency:paraent process wait son process[10]UVM_Frequency:son process A end[20]UVM_Frequency:son process B end[20]UVM_Frequency:all son process end,paraent processcontinue在UVM环境中的运行结果如下disable fork该语句用于终止当前进程中的所有子进程强行停止这些进程。常用于超时控制、异常退出等具体如下终止所有由当前进程派生的子进程disable fork会终止包含fork直接创建的进程及子进程内部再次fork产生的后代进程并将其递归终止释放资源disable fork可以停止一些不需要的后台监控、超时计数器等避免其产生意外的副作用与wait…fork一起配合完成精准控制可以先用disable fork终止一些不需要的进程再用wait fork等待剩余进程自然结束确保完成同步注意事项只能作用于initial、always、task、function等过程块其作用于调用它的那个进程所拥有的子进程而不会影响兄弟进程或父进程的其他分支谨慎使用disable fork若滥用disable fork则会有概率导致难以调试的竞态条件其会突然终止正在运行的进程从而导致某些信号处于不确定状态disable fork代码示例initial begin fork begin:outer $display([%0t]UVM_Frequency: outer start,$time);fork begin:innerA #20;$display([%0t]UVM_Frequency: inner A end,$time);end begin:innerB #40;$display([%0t]UVM_Frequency: inner B end,$time);end join_none #10;$display([%0t]UVM_Frequency: outer end,$time);end join_none #5;$display([%0t]UVM_Frequency: paraent process continue executing disable_fork,$time);disable fork;// 终止外层及其所有内层子进程#10$finish;end上述代码的运行结果为[0]UVM_Frequency:outer start[5]UVM_Frequency:paraent processcontinueexecuting disable_fork在UVM环境中的运行结果如下即在5ns的时候disable fork终止了外层进程outer进而递归终止了其内部的内层进程innerA和innerB因此innerA、innerB、outer的结束打印信息均不会出现。注意事项循环中的自动变量在fork循环中如果使用静态变量如下面示例中的i那么产生的所有线程将会共享该变量从而会导致结果有误错误示例如下for(inti0;i10;i)begin fork $display(UVM_Frequency: i %0d,i);// 危险所有线程看到的i都是最终值10join_none end上述代码在UVM环境中的运行结果如下所有线程看到的i都是最终值10。正确做法是使用automatic具体如下for(inti0;i10;i)begin automaticintji;fork $display(UVM_Frequency: j %0d,j);join_none end上述代码在UVM环境中的运行结果如下join_none与#0延迟当采用fork...join_none时其启动的线程虽然立刻开始调度但是在#0内即零延时父线程的后续代码会优先执行。若需要子线程先执行的话可以采用mailbox等同步机制。 ~ 本节完结 ~