news 2026/5/27 18:14:28

systemverilog中关于多线程的若干思考

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
systemverilog中关于多线程的若干思考

最近在做自研总线相关的验证,遇到的情景是2048个master和2048个slave同时并行发送和接收数据,这部分必然会用到systemverilog中fork相关的语法,而在这个过程中,遇到了从未见过的复杂组合,包括wait fork及disable fork作用域的问题。很多的理解需要做简单的仿真来进行实验,进而总结结论,加深印象,现总结如下。

例子1

多个initial块之间是并行执行的

在initial块内部,可以写begin……end的顺序结构,也可以写fork的并行结构。(包括fork……join,fork……join_none,fork……join_any)

如果是多个begin……end,是从上到下顺序执行的。

不太常见的写法,initial fork …… join结构


因此,了解这些语句执行顺序的关键首先要明白四种过程块结构的规则

  • begin……end 内部的语句依次按顺序执行
  • fork……join 内部语句同时开始执行,最晚的线程结束后,才会退出fork……join过程块,继续执行之后的语句
  • fork……join_any 内部语句同时开始执行,最早的线程结束后,即可走出fork……join_any过程块,继续执行之后的语句,与此同时,fork……join_any内未执行完的线程仍在继续执行,多个线程在并行执行。
  • fork……join_none 内部语句同时开始执行,但是并不会阻塞后面的线程,fork……join_none之后的线程会同时执行,多个线程同时并行执行。

sv绿皮书上很经典的一张图。对于fork……join_any和fork_join_none而言,跳出fork块,并不意味着fork块内还没有执行完的线程就会结束终止,而是仍在并行执行。

fork……join

  • 仿真开始,运行10ns,然后进入fork块,四条语句同时开始执行
  • 第10ns,01 print,02 print
  • 第20ns,fork join块结束
  • 第30ns,03 print
  • 第40ns,04 print

fork……join_any

  • 仿真开始,运行10ns,进入fork块,四条语句同时开始执行
  • 第15ns,02 print,语句执行完成,走出fork块,其他线程继续执行 ,后续线程开始执行
  • 第20ns,01 print,fork块内所有线程执行完成
  • 第25ns,03 print
  • 第35ns,04 print

fork……join_none

  • 仿真开始,运行10ns,进入fork块,四条语句同时开始执行,同时,fork块之后的语句同时开始执行
  • 第15ns,02 print
  • 第20ns,01 print, 03 print,fork块内所有线程执行完成
  • 第30ns,04 print

当fork块和begin end块连用和嵌套时,分析的基本原则是,从外到内,层层分析。遇到嵌套时,先把过程块当成一个整体。

fork……join中嵌套begin……end

  • 仿真开始,运行10ns,进入fork块,在fork块内部,有四条语句,还有一个begin……end块,先把begin……end块当成一个整体,所以,begin end块和四条语句是并行执行的,而在begin end块内部,有三条语句,这三条语句是顺序执行的
  • 第10ns,进入fork块,四条语句和begin……end块同时开始执行
  • 第15ns,02 print
  • 第20ns,01 print,03 print
  • 第30ns,在begin end块中,74行的延时执行完成
  • 第35ns,04 print,begin……end块执行完毕,走出fork join块
  • 第45ns,05 print
  • 第55ns,06 print
fork……join_none中很容易犯的书写错误

忘记在fork……join_none中添加 begin end,导致fork……join_none块里的语句都是并行的,这会导致很奇怪的现象,不容易debug出来。


fork……join_any中嵌套begin……end

  • 仿真开始,运行10ns,进入fork块,在fork块内部,有四条语句,还有两个begin……end块,先把begin……end块当成一个整体,所以,两个begin end块和四条语句是并行执行的,而在begin end块内部,有三条语句,这三条语句是顺序执行的
  • 第10ns,进入fork块,四条语句和两个begin end块同时并行执行
  • 第15ns,02 print,此时fork……join_any块中最早的线程完成,走出fork块
  • 第20ns,01 print,03 print,05 print
  • 第25ns,07 print
  • 第30ns,无打印
  • 第35ns,04 print,06 print, 08 print

fork……join_none中嵌套begin……end

  • 仿真开始,运行10ns,进入fork块,在fork块内部,有四条语句,还有两个begin……end块,先把begin……end块当成一个整体,所以,两个begin end块和四条语句是并行执行的,而在begin end块内部,有三条语句,这三条语句是顺序执行的
  • 第10ns,进入fork块,四条语句和两个begin end块同时并行执行,同时开始执行fork块之后的语句
  • 第15ns,02 print
  • 第20ns,07 print,01 print,03 print, 05 print
  • 第30ns,08 print
  • 第35ns,04 print,06 print

不管有多少层级嵌套,都要遵守分析规则,由外到内,整体法层层分析

对自己狠一点,整一个复杂的嵌套,

从外到内,层层分析

在initial块的最上层,是begin end,这一层begin end中,可以看做四部分,第130行,第131~157行fork块,第158行,第159行。这四部分是顺序执行的。

进入fork块内部 ,fork……join_none内部,可以看做四部分,第132~137行fork join块,第138~144行fork join_any块,第145~151行begin end块,第152~156行begin end块,这四部分在fork……join_none块中是并行执行的。

第10ns,进入fork块,与此同时,fork块之后的语句也开始执行

第15ns,02 print

第20ns,09 print,01 print, 03 print,05 print,07 print,此时第132~137行fork join块结束

第25ns,04 print,06 print

第30ns,10 print

第35ns,08 print


利用循环产生多个并发线程

一种错误的写法

详细原因可以参考绿皮书,4次for循环,产生4个fork join_none,这4个同时开始,并发执行,但是产生这四个的时候,循环变量i已经到4了,因此最终打印的都是4

正确写法应该是写一个自动变量,将每次循环的值copy下来

把自动化变量的赋值,写在fork……join_none内部也是可以的

将这种写法等价于再begin……end块中,有多个fork……join_none块


此外,还有另一种写法,把for循环写到fork……join_none内部,注意这种写法和上面的写法结果是有本质差别的。

把foreach块当作一个整体,fork join_none只有一个线程,且会立即执行fork join_none之后的代码,但是之后for循环的执行,会按照begin end来顺序执行

按照deepseek的说法:

  • 执行逻辑fork join_none会将foreach循环当作一个整体来并发执行。也就是说,foreach循环的所有迭代会被一次性地提交到事件队列里,接着fork join_none块会立刻退出,后续的代码会继续执行。
  • 时间顺序:由于foreach循环的所有迭代是一次性提交的,各个迭代里的延迟时间会决定它们执行的先后顺序。在上述示例中,array[0]会在0时刻显示,array[1]会在10时刻显示,依此类推。
  • 并发情况foreach循环的所有迭代是并发执行的,不过它们的显示顺序会由各自的延迟时间来决定。

再看下面一个例子

在begin end中有四个fork join_none,还有两个语句,这些都是顺序执行的,而fork join_none不会阻塞之后的线程

第5ns,05 print

第7ns,06 print

第10ns,01 print,02 print,03 print,04 print


begin end块中,有一个fork join,有一个语句,这两个是顺序执行的,需要先执行完fork join

在fork join块中,有四个fork join_none,还有一个打印语句,这五个线程在fork join中是并行的,fork join_none可以认为立即执行完毕,并不是fork join_none中的语句执行完毕,才会退出fork join

第5ns,05 print,走出fork join

第7ns,06 print

第10ns,01 print,02 print,03 print,04 print


wait fork的用法

作用域是什么?到底等的是哪些线程结束

disable fork的用法

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/27 18:14:23

电子发票PDF识别——效率提升与成本节约

文章目录 背景目标实现应用下载 背景 海量且持续增长: 企业(尤其是中大型企业)每天需要处理成百上千张发票(进项、销项、报销等),数量巨大且不断增长。人工录入为主: 传统方式是财务人员手动查…

作者头像 李华
网站建设 2026/5/27 18:14:20

如何快速上手T5 Large模型?3分钟完成你的第一个文本转换任务

如何快速上手T5 Large模型?3分钟完成你的第一个文本转换任务 【免费下载链接】t5_large 项目地址: https://ai.gitcode.com/hf_mirrors/wuhaicc/t5_large 想要快速掌握强大的T5 Large文本转换模型吗?这篇终极指南将带你3分钟内完成第一个文本转换…

作者头像 李华
网站建设 2026/5/27 18:11:15

硬件安全必修课:扫描攻击与JTAG滥用的原理、威胁与防护方案

1. 项目概述:当测试成为后门在芯片设计领域,可测试性设计(Design-for-Testability, DfT)就像给一个复杂的黑盒子装上了一系列的“观察窗”和“控制杆”。它的初衷无比美好:在生产线上快速、高效地检测出制造缺陷&#…

作者头像 李华
网站建设 2026/5/27 18:09:24

5个场景告诉你,为什么你需要这个跨平台资源下载神器

5个场景告诉你,为什么你需要这个跨平台资源下载神器 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader 你是否曾经遇…

作者头像 李华
网站建设 2026/5/27 18:09:14

Pomolectron:如何在现代桌面应用中优雅实现番茄工作法

Pomolectron:如何在现代桌面应用中优雅实现番茄工作法 【免费下载链接】pomolectron :tomato: A pomodoro app for your menubar/tray. 项目地址: https://gitcode.com/gh_mirrors/po/pomolectron 你是否曾为专注力分散而困扰?当传统番茄钟应用要…

作者头像 李华
网站建设 2026/5/27 18:09:05

DDrawCompat 终极指南:如何在Windows 10/11上完美运行经典老游戏

DDrawCompat 终极指南:如何在Windows 10/11上完美运行经典老游戏 【免费下载链接】DDrawCompat DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11 项目地址: https://gitcode.com/gh_mirrors…

作者头像 李华