Systemverilog中fork...join等线程的使用

📅 2026/7/14 6:24:42 👁️ 阅读次数 📝 编程学习
Systemverilog中fork...join等线程的使用

Systemverilog中fork...join等线程的使用

  • 前言
  • fork...join 三个变体的区别
  • fork...join 三个变体的典型应用场景
  • fork...join示例
  • fork...join_any示例
  • fork...join_none示例
  • 线程控制方法
    • wait fork
    • disable 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_Frequency:fork...join_any Start @5ns UVM_Frequency:process B @5ns UVM_Frequency:fork...join_any end @10ns UVM_Frequency:process A @15ns UVM_Frequency:process 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_Frequency:fork...join_none Start @0ns UVM_Frequency:fork...join_none end @5ns UVM_Frequency:process B @10ns UVM_Frequency:process A @15ns UVM_Frequency:process C

在UVM环境中的运行结果如下:


线程控制方法

为了管理好fork...join的并发线程,systemverilog提供了两个重要的控制语句:wait forkdisable fork


wait fork

该语句用于等待当前进程中的所有子进程全部结束后才会结束仿真,包括fork...join_none产生的进程,具体如下:

  • 等待所有子进程结束wait fork会等待当前进程直接或间接创建的所有子进程都终止(包括执行完成或者被disable fork强行终止);
  • 灵活的同步点:与fork...join必须在同一个fork中等待所有子进程结束不一样,wait fork可以在后续任意时刻执行,其允许先启动一部分进程,执行一些其他操作,最后统一等待他们完成;即wait fork可跨多个fork块,等待调用者所有活跃的子进程结束;
  • 避免进程泄漏:若不使用wait fork,则有可能在父进程执行结束之后,尚未完成的子进程仍在后台运行,有可能会导致信号意外被驱动或者资源占用;
  • 注意事项
    • 只能作用于initialalwaystaskfunction等过程块;
    • 不关心其他进程,只关心当前进程的后代;
    • 若当前进程没有子进程,则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等待剩余进程自然结束,确保完成同步;
  • 注意事项
    • 只能作用于initialalwaystaskfunction等过程块;
    • 其作用于调用它的那个进程所拥有的子进程,而不会影响兄弟进程或父进程的其他分支;
    • 谨慎使用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,进而递归终止了其内部的内层进程innerAinnerB,因此innerAinnerBouter的结束打印信息均不会出现。


注意事项

循环中的自动变量

fork循环中,如果使用静态变量(如下面示例中的i),那么产生的所有线程将会共享该变量,从而会导致结果有误,错误示例如下:

for(inti=0;i<10;i++)begin fork $display("UVM_Frequency: i = %0d",i);// 危险!所有线程看到的i都是最终值10join_none end

上述代码在UVM环境中的运行结果如下,所有线程看到的i都是最终值10。

正确做法是使用automatic,具体如下:

for(inti=0;i<10;i++)begin automaticintj=i;fork $display("UVM_Frequency: j = %0d",j);join_none end

上述代码在UVM环境中的运行结果如下:


join_none与#0延迟

当采用fork...join_none时,其启动的线程虽然立刻开始调度,但是在#0内(即零延时),父线程的后续代码会优先执行。
若需要子线程先执行的话,可以采用mailbox等同步机制。


💓 💖 💓 ~ 本节完结 ~ 💓 💖 💓