从第一个问题的考虑来看,在预先生成测试向量的情况下,如果无法对单一的stimulator做出很好的调度,那么自然无法对于后期MCDF集成环境中的多个stimulator做出灵活的调度了。
在之前《SV组件实现篇》中提到了组件之间(线程之间)的通信手段,而这里我们再需要考虑如何进行进程之间的并行运行。上面的vector类通过并行执行的方式,可以实现真实的硬件场景。我们在这里需要思考一下,为什么并行的线程才是真实的硬件场景呢? 因为硬件的行为和处理方式都是“并行”的,所以软件的激励为了模拟硬件的行为也应该是并发的。下面再来梳理梳理fork-join常见的几种使用方式吧。
面的图中,T1、T2和T3是耗时不一的三个线程,耗时按照从长到短的顺序排列依次为T2、T1、T3。在这三种不同的模式下,执行的细节应该是:
- fork-join:fork线程块(包含三个子线程T1、T2、T3)需要等待三个子线程都执行完毕之后才会结束,即需要等待最长的T2执行完毕,进而执行接下来的部分。
- fork-join_any:fork线程块只需要等待T1、T2、T3中的任何一个子线程执行完毕,就可以继续执行后面的任务。这里,fork块只需要等待T3执行完毕,就可以完成等待动作,执行后面的任务了。
- fork-join_none:fork线程块无需等待任何的子程序完成,没有任何延迟,即可以执行后面的任务。在这里fork-join_none的线程块起到了触发子线程运行的“点火”作用,而不会等待这些线程“烟花”结束。
从上面回顾的三种fork线程块使用方式来看,读者需要注意的是:
- 三种方式都可以用来对线程进行“点火”。
- 三种方式的不同在于结束fork块等待的时序不同。最慢的数fork-join,fork-join_any次之,fork-join_none则是最快的。
- 对于fork-join_any和fork-join_none而言,尽管fork块结束等待后继续进行后面的任务,但是已经被“点火”的线程还会持续“升空”直到绽放出烟花,运行完毕各个子线程。这一点读者要务必注意。
fork-join的特点在于fork线程块结束等待时,也就是各个子线程全部执行完毕的时候。那么对于fork-join_any和fork-join_none,我们如何得知各个子线程的执行状况呢?毕竟如果不做特殊的处理,子线程的结束不会再额外“敲门”,告诉verifier,嗨,快来瞧瞧我,我得走(执行完毕)了!
我们可以通过设置一些在子线程和线程外部的共享变量来满足上面的需求。譬如下面这个例子:
module fork_case1;
event e1, e2, e3;
task t1;
#15ns;
$display("t1 is leaving");
-> e1;
endtask
task t2;
#20ns;
$display("t2 is leaving");
-> e2;
endtask
task t3;
#10ns;
$display("t3 is leaving");
-> e3;
endtask
initial begin
$display("fork:thread_trigger start");
fork: thread_trigger
t1();
t2();
t3();
join_none
$display("fork:thread_trigger finish");
$display("fork:thread_monitor start");
fork: thread_monitor
@e1 $display("bye to t1");
@e2 $display("bye to t2");
@e3 $display("bye to t3");
join
$display("fork:thread_monitor finish");
end
endmodule
输出结果:
# fork:thread_trigger start
# fork:thread_trigger finish
# fork:thread_monitor start
# t3 is leaving
# bye to t3
# t1 is leaving
# bye to t1
# t2 is leaving
# bye to t2
# fork:thread_monitor finish
首先来看看fork块thread_trigger,由于是join_none的时序要求,所以只管“点火”,点完就跑到下面的fork块thread_monitor,该块的作用是监测thread_trigger中的三个子程序t1、t2、t3,等待它们各自执行完毕时触发的event e1、e2和e3。由于在进程thread_trigger和thread_monitor之间,e1、e2、e3是共享的,因此thread_monitor可以通过这三个event来监测各个子进程是何时结束的。待所有子进程都执行完毕之后,thread_monitor的三个子线程的监测任务才会结束。
谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。