本文参考绿皮书第七章,线程及其线程间的通信,Verilog HDL A guide to digital design and synthesis 2nd第七章。主要对于begin…end,fork…join等进行简单总结。然后给出相对应的实例进行理解。有问题欢迎同学指出。


System Verilog线程——fork join的理解使用

  • 概述
  • 顺序块与并发块(Sequential and Parallel Blocks)
  • begin...end块
  • fork...join块
  • fork...join_any与fork...join_none
  • 停止进程disable
  • 附录
  • 线程与进程的关系


概述

在Verilog中,存在组合逻辑与时序逻辑,需要仿真器对于两者进行模拟。因此测试平台需要并发执行某些操作,称之为并发的线程。在verilog中存在begin…end,以及fork…join两中分组方式,用于顺序或者是并发执行。在system vrilog中,引入了fork…join_any以及fork…join_none新的方法用于创建线程。最后解释停止进程。

顺序块与并发块(Sequential and Parallel Blocks)

begin…end块

关键字begin和end用于将语句分组为顺序块。
顺序块具有以下特征:

  • 顺序块中的语句按指定顺序处理。只有在前一条语句完成执行后,才会执行该语句。
  • 如果指定了延迟或事件控制,则该延时或事件与块中的前一条语句完成执行时的模拟时间有关。
    分别举例如下
//案例 1: Sequential block without delay
module
reg x, y;
reg [1:0] z, w;
initial begin
	x = 1'b0;
	y = 1'b1;
	z = {x, y};
	w = {y, x};
end
endmodule

对应案例 1: Sequential block without delay

systemverilog中队列操作 system verilog fork_父进程

//案例2: Sequential blocks with delay.
reg x, y;
reg [1:0] z, w;
initial begin
	x = 1'b0; //completes at simulation time 0
	#5 y = 1'b1; //completes at simulation time 5
	#10 z = {x, y}; //completes at simulation time 15
	#20 w = {y, x}; //completes at simulation time 35
end

对应案例2: Sequential blocks with delay.

systemverilog中队列操作 system verilog fork_事件控制_02


注意这里的仿真时间,对w的赋值时间再35ns处

fork…join块

由关键字fork和join指定的并行块提供了有趣的模拟特征。平行块具有以下特征:
•并行块中的语句并行执行。
•语句的顺序由分配给每个语句的延迟或事件控制来控制陈述
•如果指定了延迟或事件控制,则它与执行块的时间有关进入。
案例1:

module tb();
reg x, y;
reg [1:0] z, w;
initial fork
	x = 1'b0; //completes at simulation time 0
	#5 y = 1'b1; //completes at simulation time 5
	#10 z = {x, y}; //completes at simulation time 10
	#20 w = {y, x}; //completes at simulation time 20
join
endmodule

systemverilog中队列操作 system verilog fork_父进程_03


注意这里的仿真时间,在20ns已经结束了对w的赋值

案例2:

module tb();
reg x, y;
reg [1:0] z, w;
initial fork
	x = 1'b0;
	y = 1'b1;
	z = {x, y};
	w = {y, x};
join
endmodule

systemverilog中队列操作 system verilog fork_fpga开发_04


产生同样波形的原因是存在竞争问题。本例中特意引入了竞争条件。所有语句都在模拟时间0开始。语句的执行顺序未知。如果先执行x=1’b0和y=1’b1,变量z和w将得到值1和2。如果x=1’b0和y=1’b1最后执行,变量z和w将得到值2’bxx和2’bxx。因此,z和w的结果是不确定的,并且取决于模拟器的实现。在模拟时,fork-join块中的所有语句都会立即执行。然而,在现实中,运行模拟的CPU一次只能执行一条语句。不同的模拟器以不同的顺序执行语句。因此,竞争条件是模拟器的限制,而不是fork-join块的限制。

fork…join_any与fork…join_none

前面对于fork的引入是为了更好的理解如下点。

join 父进程会阻塞直到这个分支产生的所有进程结束。
join_any 父进程会阻塞直到这个分支产生的任意一个进程结束。 然后继续执行
join_none 父进程会继续与这个分支产生的所有进程并发执行。在父线程执行一条阻塞语句之前,产生的进程不会启动执行。

简单的测试如下

systemverilog中队列操作 system verilog fork_Verilog_05


systemverilog中队列操作 system verilog fork_systemverilog中队列操作_06


systemverilog中队列操作 system verilog fork_systemverilog中队列操作_07

停止进程disable

关键字disable提供了一种终止命名块执行的方法。disable可用于根据控制信号跳出循环、处理错误条件或控制代码段的执行。禁用块会将执行控制传递给紧随该块之后的语句。对于C程序员来说,这与用于退出循环的break语句非常相似。区别在于break语句只能中断当前循环,而关键字disable允许禁用设计中的任何命名块。
在下面的这个例子中,等待时钟或者是等待到特定信号的下降沿。就会推出这个fork线程。两种写法都是可以的。

task get_payload();
    pkt2cmp_payload.delete();//清除之前缓存的数据
    fork begin : wd_timer_fork
            fork : frameo_wd_timer  
                @(negedge rtr_io.cb.frameo_n[da]);  //这里是第一个线程(thread)
				begin  					//begin...end这里是第二个线程start
                    repeat(100) @(rtr_io.cb);
      	            $display("\n%m\n[ERROR]%t Frame signal timed out!\n", $realtime);
                    $finish;
                end 							//begin...end这里是第二个线程end
            join_any : frameo_wd_timer
            //disable fork;					//等待失败,或者是等到下降沿,推出fork join。执行下面的内容
            disable frameo_wd_timer;		//同disable fork;
         end : wd_timer_fork
    join

后需要在进行补充吧~

附录

线程与进程的关系

摘自绿皮书第七章绪论注释

systemverilog中队列操作 system verilog fork_fpga开发_08