systemverilog中关于多线程的若干思考 最近在做自研总线相关的验证遇到的情景是2048个master和2048个slave同时并行发送和接收数据这部分必然会用到systemverilog中fork相关的语法而在这个过程中遇到了从未见过的复杂组合包括wait fork及disable fork作用域的问题。很多的理解需要做简单的仿真来进行实验进而总结结论加深印象现总结如下。例子1多个initial块之间是并行执行的在initial块内部可以写begin……end的顺序结构也可以写fork的并行结构。(包括fork……joinfork……join_nonefork……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块四条语句同时开始执行第10ns01 print02 print第20nsfork join块结束第30ns03 print第40ns04 printfork……join_any仿真开始运行10ns进入fork块四条语句同时开始执行第15ns02 print语句执行完成走出fork块其他线程继续执行 后续线程开始执行第20ns01 printfork块内所有线程执行完成第25ns03 print第35ns04 printfork……join_none仿真开始运行10ns进入fork块四条语句同时开始执行同时fork块之后的语句同时开始执行第15ns02 print第20ns01 print 03 printfork块内所有线程执行完成第30ns04 print当fork块和begin end块连用和嵌套时分析的基本原则是从外到内层层分析。遇到嵌套时先把过程块当成一个整体。fork……join中嵌套begin……end仿真开始运行10ns进入fork块在fork块内部有四条语句还有一个begin……end块先把begin……end块当成一个整体所以begin end块和四条语句是并行执行的而在begin end块内部有三条语句这三条语句是顺序执行的第10ns进入fork块四条语句和begin……end块同时开始执行第15ns02 print第20ns01 print03 print第30ns在begin end块中74行的延时执行完成第35ns04 printbegin……end块执行完毕走出fork join块第45ns05 print第55ns06 printfork……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块同时并行执行第15ns02 print此时fork……join_any块中最早的线程完成走出fork块第20ns01 print03 print05 print第25ns07 print第30ns无打印第35ns04 print06 print 08 printfork……join_none中嵌套begin……end仿真开始运行10ns进入fork块在fork块内部有四条语句还有两个begin……end块先把begin……end块当成一个整体所以两个begin end块和四条语句是并行执行的而在begin end块内部有三条语句这三条语句是顺序执行的第10ns进入fork块四条语句和两个begin end块同时并行执行同时开始执行fork块之后的语句第15ns02 print第20ns07 print01 print03 print 05 print第30ns08 print第35ns04 print06 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块之后的语句也开始执行第15ns02 print第20ns09 print01 print 03 print05 print07 print此时第132~137行fork join块结束第25ns04 print06 print第30ns10 print第35ns08 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不会阻塞之后的线程第5ns05 print第7ns06 print第10ns01 print02 print03 print04 printbegin end块中有一个fork join有一个语句这两个是顺序执行的需要先执行完fork join在fork join块中有四个fork join_none还有一个打印语句这五个线程在fork join中是并行的fork join_none可以认为立即执行完毕并不是fork join_none中的语句执行完毕才会退出fork join第5ns05 print走出fork join第7ns06 print第10ns01 print02 print03 print04 printwait fork的用法作用域是什么到底等的是哪些线程结束disable fork的用法