我们早在学习Verilog语言时就学过:相对于begin-end顺序执行的语句块,还存在fork-join并行执行的语句块。这些知识用起来已经很顺手了,但是当学习到SystemVerilog语言的时候,突然告诉你:其实fork-join还有两个失散多年的亲兄弟活着!那就是fork-join_any和fork-join_none!!!
这三个兄弟虽然长的比较像,但是其实性格是不一样的!他们的主要性格区别是他们对待称为“线程”的小朋友的态度上。“线程”小朋友是轻量级的“进程”,是程序调度的基本单位。假如某一时段同时来了好几个线程小朋友去他们家吃饭,三兄弟性格可以表现的非常明显。其中fork-join的性格是最温和耐心的,他会静静等待所有线程小朋友全部吃完饭才去做别的事情。而fork-join_any性格是最健忘和丢三落四的,当他看到其中某个线程小朋友吃完后会直接忘了别的小朋友还在吃,以为都完成了,直接去做自己的事情。至于fork-join_none则是脾气最暴躁的,他不会等待任何一个线程小朋友吃饭,会直接去搞自己的事情!
那fork-join_any、fork-join_none一个健忘症一个暴脾气,他们是猴子请来搞笑的吗?除了增加我们的概念记忆还有什么作用?
李白说“天生我材必有用”,Jerry今天就简单的说说这两个兄弟的作用吧~
考虑遇到这样一种情况:当某些线程小朋友是无限循环的,永远吃不完,而下面我们就想结束所有的程序,那一直等下去有时就会出问题。 这时对于fork-join_any这个健忘症,就可以有用武之地了。我们常常设立一个吃的最快的线程小朋友作为“组长”,而fork-join_any通过等待“组长”来控制这段程序运行结束。
实际中的一个常见经典用法如下,(初学者不需要了解代码中每行的含义,只需要对fork-join_any的应用有一个直观认识即可)想在记分板中控制验证平台的结束时,通过配置num参数来控制想收到的实际数据包数量,运行完for循环就会执行drop_objection,结束平台运行。这里面的for循环就是我们前面提到的“组长”,如果没有fork-join_any,单纯的使用fork-join便会一直停不下来,是不能实现这个功能的了。
对于fork-join_none这个暴脾气,其实也是很有用处的。比如有这样一个需求:把某个相同的线程并行启动运行100个。这个需求用fork-join可以实现,但是你要在其中罗列100次这个线程:
这样写显然不合理,太麻烦了,如果更大的数那就是不可能完成的任务了。这时候fork-join_none就显示出了很好的作用,如下,配合for循环几行就可以达到启动的100个的目的。
值得一提的是,这两段代码作用其实是不等价的,通过fork-join_none运行的100个线程,是并行启动了,但是不等他们全部结束程序就会进行到后面的程序中去,如果想要等价可以在后面使用wait fork语句来等待所有线程结束,如下代码就与fork-join控制的完全等价了。
通过Jerry今天的白话乱侃,相信天才的你对fork三兄弟会有更清楚的一个认识了,初学者们至少把这个易混淆的概念轻松记住了吧。当然,其实在SystemVerilog中还提供比如disable关键词、suspend()方法、resume()方法等手段结合fork三兄弟对线程进行更加丰富的控制,各位可以日后慢慢探索哈~
今天就侃到这里,大家有什么建议或问题都可以直接反馈公众号哈~Jerry和大家一起成长~