之前通过《面试系列:RTL基础知识【2】关于disable fork的误解》说明了disable fork的具体作用范围,但是在实际的使用过程中,如果使用不当还是会遇到一些问题,本文将示例遇到的一些问题,并且给出解决方法。
【示例】期望taska和taskb各执行各的,taskb先于taska执行,并且执行过程相互不影响。Taska实现等待valid信号在“20*delay”之内有效,否则超时退出,并通过“disable fork”干掉taska中的thread_a的fork结构。初步完成的代码如下。

【仿真结果】
从仿真结果可以观测到valid信号有效后,执行了“disable fork”,确实干掉了thread_a,但是居然把taskb中的thread_b页同时干掉了,这是我们不期望出现的情况,其执行过程如下图所示:
在START_PROCESS中,任务taskb先于任务taska启动后,同时开启执行任务taska中的thread_a,thread_a的子进程sub_thread_a和sub_thread_b在同时启动,当sub_thread_a和sub_thread_b任何一个先执行完就执行thread_a之后的“disable fork”,而另一个没有执行完子进程将继续保持,在“disable fork”完成其对应的功能后执行其后的“#10 $display”。这里需要注意的是,任务taskb中的thread_b和任务taska中的thread_a、“disable fork”、“#10 $display”都属于initial begin-end开启的子进程。所以thread_b、thread_a、“disable fork”、“#10 $display”与initial begin-end之间的关系是父子进程关系。当执行“disable fork”时,将会终止调用其的父进程所派生的所有子进程中处于激活状态的进程,所以示例中thread_a(及其子进程)和thread_b被终止。但是示例中的“#10 $display”并没有被终止,这是为什么呢?在IEEE1800中规定,当执行“disable fork”时,将会终止调用其的父进程所派生的所有处于激活状态的子进程,而示例中执行“disable fork”时,“#10 $display”还没有执行被激活,处于未激活状态(inactive),所以幸免于难。那么为了实现taskb和taska各执行各的,或者说实现多个同时执行的fork互相不影响应该如何处理呢?有人可能认为使用“disable LABEL”的方式,那么这样做可行吗?请看下例。
【示例】使用“disable LABEL”方式
【仿真结果】
注:示例中之所以在task声明时使用automatic,可以参考《Verilog系列:【13】task和function》,此处不再赘述。
在示例中,当使用“disable thread_a”时,我们发现任务taska(`DELAY)还没有等到valid有效就被taska(‘h05)中调用的“disable thread_a”终止掉了。因为一般情况下函数或者任务都是为了代码的复用而设置的,即函数或者任务会被多次调用,如果像示例中使用“disable LABEL”的方式,将会终止掉同时执行的多个任务,那么这势必影响了其他调用该任务的代码的执行,所以,使用“disable LABEL”的方式对于某个仅调用一次的任务有效(这样专门定义个任务也没有什么意义了),对于调用多次的任务将会产生不期望的连锁终止反应。既然通过“disable LABEL”的方式不能很好的实现期望的行为,那么还可以通过什么方式解决“disable fork”仅终止特定的进程呢?请看下例。
【示例】使用“mask fork”
【仿真结果】
示例中代码执行过程如下图所示。
示例中通过fork转移了进程之间的父子关系,实现了对于“disable fork”作用范围的有效控制。在任务taska中使用“mask fork”,将“disable fork”的父进程从原来的initial转移到了“fork begin-end join”中,这样按照IEEE1800要求,此时“disable fork”将会终止示例中mask_fork开启的所有激活的子进程及子进程的子子孙孙,而mask_fork中处于未激活状态的子进程并没有被“disable fork”干掉。另外示例中使用的是“fork begin-end join”实现“mask fork”,也可以使用其他的fork结构与begin组合使用,但是此时“disable fork”之后的进程的执行需要具体分析,此处不再赘述。
通过上述示例,可以总结得到以下几点:
Ø “disable fork”将会终止其父进程开启的所有处于激活状态的子进程(及子进程的子进程);
Ø “disable fork”不会终止其父进程开启的处于未激活状态的子进程(及子进程的子进程),即不会影响其后未激活状态的子进程在“disable fork”之后的执行;
Ø “disable fork”不会终止其父进程下的所有进程;
Ø 有效控制“disable fork”作用范围时尽量使用“mask fork”;
Ø 将begin...end嵌套于fork...join中可以实现对于其中“disable fork”的“mask fork”,其作用是在原有父子进程关系之间增加了一层父子关系,从而形成了一种三代同堂的局面;
Ø 不推荐使用“disable LABEL”方式来终止并发进程;
Ø 虽说fork-join中的子进程是并发执行的,但是在同一个时间槽中还是按照一定的顺序执行的,此时就会有被先执行的语句(active),未被执行到的则处于未激活状态(inactive);