SV中,fork-join,fork-join_any、fork-join_none的理解_fork join_any-程序员宅基地

技术标签: SV知识点  

fork join的用法

我们早在学习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一个健忘症一个暴脾气,他们是猴子请来搞笑的吗?除了增加我们的概念记忆还有什么作用?

   考虑遇到这样一种情况:当某些线程小朋友是无限循环的,永远吃不完,而下面我们就想结束所有的程序,那一直等下去有时就会出问题。 这时对于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控制的完全等价了。
在这里插入图片描述

fork join none的坑

1. 回忆下fork-join_none

fork-join_none相信大家应该熟悉了,新来的朋友可以回顾下jerry之前的文章,就是之前jerry提到的那个“暴脾气”的哥们,他不会去等别人,直接会着急做自己的事情。
前文回顾(点击查看):fork-join挺好用的了,fork-join_any、fork-join_none有什么用?
回顾下那篇文章中我们举的一个例子,这个暴脾气可以这么用:

fork
aa( );
aa( );
aa( );
……
aa( );
aa( );
join

如上代码,我们想要并行的执行100个 aa( )这个函数进程。通过fork-join要写到手软,用这个暴脾气,几句话就搞定:

for(int i=0; i<100; i++)
fork
aa( );
join_none

但是,今天jerry告诉各位初学者,这个暴脾气有不好驾驭的那一面的哦,弄不好就很容易翻车!!

2. fork-join_none翻车现场

什么情况下容易翻车呢?

大家仔细看看上面的例子,并行运行的aa( ),都是一样的内容,放在for循环中,却并没有使用for循环的循环因子 i 啊~

有人说,这有什么关系吗?
好的,来,看看jerry今天准备的代码,逼出它的邪恶面!

我们还是通过暴脾气fork-join_none,外加for循环,这次我们用上for的循环因子i,
怎么用i呢?我们直接通过$display来打印,打出10个选美者的编号和颜值等级:

for (int i=0 ; i<10; i++)
fork
$display(“No%0d,My face_grade is %0d”, i ,i );
join_none

大家先不看答案,先猜猜,这个会怎么打?

算了,jerry先猜猜你们是怎么想的?是不是打印出下面这样?

No0,My face_grade is 0
No1,My face_grade is 1
No2,My face_grade is 2
No3,My face_grade is 3
……
No9,My face_grade is 9

大错特错!!太天真了!这个时候这个暴脾气只会在电脑的某个角落里看着你笑着说“愚蠢的人类”!!
jerry告诉你打印出来的是什么:

No10,My face_grade is 10
No10,My face_grade is 10
No10,My face_grade is 10
……
No10,My face_grade is 10

太阴险了!怎么会是这样呢?我0-9怎么还出来10了?

3. 再认识下for循环

先解释下这个for循环范围0-9,怎么打出来10了?
看下这段代码:

int apple_num;
for (apple_num=0 ; apple_num<10; apple_num++);
$display(“i have %0d apples”, apple_num );

直接告诉大家,这个代码打印出来的是:

i have 10 apples

这个代码,for循环是执行的一个空语句,for结束后才进行打印循环因子。让不注重细节的伙伴们再认识下for,for在最后执行完成他的值是还要再走apple_num++的,正是因为加到了10,才不满足apple_num<10的条件不再进行循环下去了。
我们再回头看看这个代码:

for (int i=0 ; i<10; i++)
fork
$display(“No%0d,My face_grade is %0d”, i ,i );
join_none

现在知道这个打印出来10是怎么来的了,是for循环执行完了以后循环因子i的值啊!!

好像差不多理解了:for循环的时候依次创建了10个进程,然后等for循环结束后,才并行执行10个fork进程。

因为fork-join_none,for全部循环完了以后, 10个 d i s p l a y ( “ N o display(“No%0d,My face_grade is %0d”, i ,i ); 才并行的执行完!!在打印的时候得到的i值就是最后的10了。换句话理解:这10个并行的 display(Nodisplay里面的i其实是同一个int i,i++是会改变它的。

4. 怎么防止它的翻车

来,jerry先直接告诉各位怎么解这个问题:

for (int i=0 ; i<10; i++)
fork
automatic int j=i;
$display(“No%0d,My face_grade is %0d”, j ,j );
join_none

这段代码打印的正是我们期望的:
No0,My face_grade is 0
No1,My face_grade is 1
No2,My face_grade is 2
No3,My face_grade is 3
……
No9,My face_grade is 9

为什么呢?我们来分析一下:

如上代码,我们加了一个automatic int j=i 转了一下,把i给j,我们打印j。

此处automatic类型,意味着进入fork进程被创建,结束被撤销。保证了10个并行的display语句,每个进程中的j是它自己的,是独一无二的(不清楚automatic和static的区别的可以自己查或者关注jerry后面的文章哈)。

先不要恍然大悟,仔细想想,仅仅保证了独一无二,就行了?automatic int j=i;

这句话还没执行,for不就应该执行完了?那这里的i岂不是还是应该是10??

是啊!除非……?

没错!
automatic int j=i;在i++之前就执行了!!!

我们来验证下,假如这么写:

for (int i=0 ; i<10; i++)
fork
#0;
begin
automatic int j=i;
$display(“No%0d,My face_grade is %0d”, j ,j );
end
join_none

果然就又出错打印成下面这样了!!!

No10,My face_grade is 10
No10,My face_grade is 10
No10,My face_grade is 10
……
No10,My face_grade is 10

其实不要说那样,就即便如下这样都是不对的!!

for (int i=0 ; i<10; i++)
fork
automatic int j;
j=i;
$display(“No%0d,My face_grade is %0d”, j ,j );
join_none

看来除了保证独一无二,更关键的原因是执行顺序!!

什么执行顺序呢?

简单的说,如果把我们这段代码理解为两个过程:“创建进程”、“执行进程”。

创建进程的时候,创建10个并行的进程,然后统一执行。

这句神奇的automatic int j=i;偏偏就是在创建进程的过程中就执行了!

大家可以看一下下面的视频,DVE上的断点单步调试,上面提到的两种代码执行顺序对比:

先执行的94行for进入第一段代码“创建线程”阶段,然后马上95行神奇的automatic int j=i;可见它也是第一段代码“创建线程”阶段执行的!然后并没有接着执行96行的display,而是101行的for!进入了第二段代码的“创建线程”阶段!线程都创建完成之后才再回去96行进入执行进程阶段,执行了display,最后执行了102行的display。

各位初学者可以这样简单的理解这段代码,但是其实呢要更进一步探究就涉及到了
sv的仿真调度机制!!!

先简单看一眼,就是这些个东西啦:

我擦,短短几句代码需要想到这么多知识吗?这里这个调度机制我们就先不深究了,大家先擦擦汗,jerry后面的文章会娓娓道来的~

我们回到今天要讲的重点:“for循环+fork-join_none结构”的坑,怎么处理呢?最简单的一种方式就是用一个automatic int j=i 转一下,一定要在fork的一开始定义,并且赋值。

disable fork用法

SystemVerilog允许大家在使用fork + join/join_any/join_none创建进程之后,通过disable fork来提前结束这些进程。

例如下面的代码片段1,fork + join_any产生了两个并行的子进程:

第一个子进程等待valid信号,然后打印第12行的信息;

第二个子进程等待max_delay个ns,然后打印第16行的信息。

不论是哪一种结果,都会导致join_any跳出fork,接着执行disable fork来结束这个fork进程及其子进程。

在这里插入图片描述

代码片段1

这个task在等待valid的同时,为了避免长时间等待,加了一个超时机制。不论是等到valid,还是超时了,都不必再等待另一个子进程继续执行下去。这段代码乍一看好像没什么问题啊?

别急,继续往下看。

假如还有另一个task B,需要在启动task A之前启动,常见的做法就是先fork + join_none的方式启动B,再启动A。

如下面代码片段2所示。
在这里插入图片描述
代码片段2

执行task C,会惊奇的发现:不论task A里面是否wait valid成功,当执行后面的disable fork之后,task B始终都没有打印第27行的信息?

为什么会这样?是不是开始怀疑人生了?

别急,这是因为当disable fork的时候,不仅杀掉了task A里面的fork进程,连task C里面的fork/join_none进程也杀死了。disable的杀伤力,远远超出了想象,有没有?

不是我不小心,只是……

要避免这样的误杀,办法其实很多。最常见的做法是添加所谓的guard fork,来限制disable fork的作用范围。

如下面的代码片段3所示:
在这里插入图片描述

代码片段3

还有一种不太好做法是给fork的进程添加别名,然后disable这个指定的进程,如下面的代码片段4所示:

在这里插入图片描述

代码片段4

这种做法看似也OK,但是会引入另外一种风险。思考一下,不知道你是否猜到了?

Q哥带你揭晓答案。

如下面所示的代码片段5,task D里面通过fork join同时启动了两个调用task A的子进程并行执行。当调用A(1000)执行到disable p1的时候,会惊奇地发现,A(2000)也被意外地终结了。

在这里插入图片描述

代码片段5

给fork进程命名,弄巧成拙了。推荐大家还是使用guard fork,这是一种良好的coding style。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44969124/article/details/108175684

智能推荐

docker上启动部署好的ES方法_如何确认docker启动的es是否成功-程序员宅基地

文章浏览阅读2k次。1.查看镜像文件有哪些docker images2.启动3.查看是否成功在浏览器输入:http://192.168.150.101:9200 可以判断es是否部署成功http://192.168.150.101:5601 可以判断kibana是否部署成功**注意:**需要修改成自己虚拟机的ip地址查看ip地址的方法:ip addr或者ifconfig..._如何确认docker启动的es是否成功

ADVANCE.AI寿栋:面对高度竞争的国际市场 中国出海企业的机会在哪?_advance.ai出海遇到的问题-程序员宅基地

文章浏览阅读190次。据悉,『细分·增长2022新兴市场品牌出海线上峰会』是一次专为企业出海新兴市场打造的行业级跨境盛会,峰会集结了包括猫王音响、森马、Y.O.U、重力星球、挪客、Yeelight等在内的多家各领域全球化品牌,旨在为跨境行业揭示不确定性下的新兴市场机遇,探寻基于海外用户需求洞察形成的细分品类增长策略。..._advance.ai出海遇到的问题

【恒源智享云】在云服务器上安装tensorflow_gpu教程_服务器 tensorflow-gpu-程序员宅基地

文章浏览阅读884次,点赞30次,收藏9次。如果显示版本号了,那就是已经安装了。一般,创建好实例后,恒源智享云已经配置好cuda、anaconda、python了。whl文件的选择需要对应tensorflow版本、python版本,且因为是在服务器上运行,所以选择linux版本的。_服务器 tensorflow-gpu

使用QEMU模拟搭建ARM开发平台_qemu arm led-程序员宅基地

文章浏览阅读2.7k次。使用QEMU模拟搭建ARM开发平台(一):交叉编译Linux内核tags: qemu安装需要的包,我的主机环境是debian squeeze/x86_64,首先需要安装交叉编译工具链,安装qemu模拟器,下载linux内核。安装交叉编译工具链将下列源添加到/etc/apt/sources.list或在/etc/apt/sources.list.d/目录下新建一个_qemu arm led

jQuery_jquery-3.4.1.min.js-程序员宅基地

文章浏览阅读311次。一、概述jQuery是一个简单的,快速的 js 库。倡导:写更少的代码,做更多的事特点具有独特的链式语法。支持高效灵活的CSS选择器。拥有丰富的插件。兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等。二、安装一种方式:引入式安装本地引入cdn引入jquery-3.4.1.min.js 压缩版,不利于查看源码jquery-3.4.1.js 正常版 可以产看源码本地引入:cdn引入:什么cdn?cdn 内容分发_jquery-3.4.1.min.js

数据安全流通的解决方案(一)_数据作为产生价值的一个源头,面临着数据要素流动与价值释放,但这与数据治理与隐私-程序员宅基地

文章浏览阅读1.9k次。作者介绍@汪溯阿里云DataTrust主架构师/高级安全专家“数据人创作者联盟”成员。01前言我今天在这个分享首先会以介绍这个云上的安全,威胁和挑战以及阿里云的一个应对措施为切入点,来介绍阿里云的产品的整个一个蓝图,然后后面我会根据这个阿里云的安全的基础能力,来介绍我们这个data trust的一些整体的架构。02安全挑战首先一个就是说我们先来谈一下这个当前云的安全挑战和趋势今年应该是这个云运算在整个全社会的关注度是非常高的,因为我们国家现在是大力提倡数字.._数据作为产生价值的一个源头,面临着数据要素流动与价值释放,但这与数据治理与隐私

随便推点

python常用转义字符串总结:各种字符转义的不同、如何取消转义字符效果?-程序员宅基地

文章浏览阅读573次,点赞13次,收藏20次。我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。print(‘v 的转义效果是:\v Python 集中营’)

Linux下手动安装screen_screen预编译二进制文件下载-程序员宅基地

文章浏览阅读1w次,点赞2次,收藏9次。简单说来就是自己用screen源码编译安装第一步:下载screen源码并解压下载地址:http://ftp.gnu.org/gnu/screen/第二步:运行配置程序,生成Makefile文件运行源码目录下的./configure,运行成功会得到如下提示:Now please check the pathnames in the Makefile and in the ..._screen预编译二进制文件下载

OpenCV图像处理知识_opencv中为了更好的对图像进行区分、识别,通常使用()来衡量图像之间的差异。-程序员宅基地

文章浏览阅读863次。OpenCV图像处理知识OpenCV图像处理参考博客_opencv中为了更好的对图像进行区分、识别,通常使用()来衡量图像之间的差异。

微信公众平台测试账号:redirect_uri参数错误_redirect_uri 参数错误-程序员宅基地

文章浏览阅读2.6k次,点赞21次,收藏10次。本文记录了微信公众平台测试账号:redirect_uri参数错误的解决办法。_redirect_uri 参数错误

安装应用需要打开未知来源权限_OPPO手机未知来源权限在哪 OPPO手机未知来源权限设置方法...-程序员宅基地

文章浏览阅读4.9k次。OPPO手机未知来源权限在哪里设置?当我们在安装第三方程序下载的软件时,想要开启第三方安装权限时,却找不到未知来源权限在哪里设置,下面就和小编一起来看看吧!1、ColorOS 5.0及以上版本,进入设置--其他设置--设备与隐私--未知来源安装应用,打开对应应用开关,即可安装从该应用内下载的软件;2、ColorOS 3.1版本,进入设置--其他设置--安全与隐私--允许安装未知来源应用,打开即可安..._opppo手机第三方未知应用安装在哪里

【JAVA】学习java 运行环境配置_java运行环境配置-程序员宅基地

文章浏览阅读7.7k次,点赞8次,收藏17次。JAVA 学习 第一章_java运行环境配置

推荐文章

热门文章

相关标签