在Verilog/SystemVerilog中使用fork/join的注意事项

    fork/join是Verilog中常用的语句。该语法在SystemVerilog中添加了join_any和join_none两个关键字,使fork的行为发生了变化。本文将比较全面的介绍fork的用法,其中不使用join_any和join_none关键字的时候,其用法和Verilog中一致。

1. fork块中的begin/end块

    在fork块中,begin和end之间的语句会顺序执行,如果没有begin和end,则各条语句会并发执行。看下面的例子。在fork块中有A、B两个任务,由于fork中么有begin/end块,这两个任务是并发执行的,我们看看打印结果。

module tb(

    );

reg 			sigout=0;

initial begin
	fork 
		A();
		B();
	join

	// $display("Started!!");
	// $display("Finished!!");
end 

task A;
	integer i=0;
	repeat (2) begin
		#10;
		$display("Number %d.", i);
		i+=2;
	end 
endtask : A

task B;
	integer j=1;
	#5;
	repeat (4) begin
		#10;
		$display("Number %d.", j);
		j+=2;
	end 
endtask : B

endmodule

    打印结果如下,输出是从0  1  2  3  5  7。A任务和B任务的前两次repeat输出了0  1  2  3,此时A任务结束,B任务继续输出最后两次repeat的值,即5  7。

在Verilog/SystemVerilog中使用fork/join的注意事项_第1张图片

    如果将A和B任务用begin/end块包含起来,这两个任务将会顺序执行,输出就会变成0  2  1  3  5  7,即A任务执行并输出完毕后,B任务才开始执行。如下图。

在Verilog/SystemVerilog中使用fork/join的注意事项_第2张图片

2. join/join_any/join_none

    先看看SystemVerilog 3.1a版对于上述关键字的描述。

在Verilog/SystemVerilog中使用fork/join的注意事项_第3张图片

    使用join时,该fork块将阻塞进程,直到所有在fork中所有的语句都执行完毕;使用join_any时,该fork块将阻塞进程,直到fork块中任意一个进程结束;使用join_none时,该fork块不会阻塞,并会和其他进程一起并发执行。但是这些并发的进程将在遇到第一个阻塞语句时才开始执行。

    使用join时的用法和Verilog是一致的,不再赘述。

2.1 join_any

    为了测试join_any,把上文的代码修改一下。在fork外加了两个打印语句。

module tb(

    );

reg 			sigout=0;

initial begin
	fork 
		A();
		B();
	join_any

	$display("Started!!");
	$display("Finished!!");
end 

task A;
	integer i=0;
	repeat (2) begin
		#10;
		$display("Number %d.", i);
		i+=2;
	end 
endtask : A

task B;
	integer j=1;
	#5;
	repeat (4) begin
		#10;
		$display("Number %d.", j);
		j+=2;
	end 
endtask : B

endmodule

    以下是打印输出。可以看到,fork阻塞了进程,任务A首先完成,输出了0和2。此时由于A任务结束,fork不再阻塞进程,所以可以看到2输出后,紧接着输出了fork外面的两天打印语句。然后任务B接着输出3 5 7。

在Verilog/SystemVerilog中使用fork/join的注意事项_第4张图片

2.2 join_none

    首先把用于测试join_any的代码中的join_any修改为join_none,其输出如下。

在Verilog/SystemVerilog中使用fork/join的注意事项_第5张图片

    两条打印语句Started和Finished首先输出,有点出乎意料。这是由于fork块后面没有任何阻塞语句,而join_none不会阻塞下一条阻塞语句之前的所有进程。由于任务A和B中都添加了延时,所以Started和Finished被首先打印,然后才轮到任务A和B输出。为了更清晰的理解join_none,将测试程序进行修改,添加一个阻塞赋值语句,同时增加输出信号。

module tb(

    );

reg 			sigout=0;
reg 			a_end, b_end;

initial begin
	a_end = 0;
	b_end = 0;
	fork 
		A();
		B();
	join_none
	
	$display("Started!!");
	#20 sigout = 1;
	$display("Finished!!");
end 

task A;
	integer i=0;
	repeat (2) begin
		#10;
		$display("Number %d.", i);
		i+=2;
	end 
	a_end = 1;
endtask : A

task B;
	integer j=1;
	#5;
	repeat (4) begin
		#10;
		$display("Number %d.", j);
		j+=2;
	end 
	b_end = 1;
endtask : B

endmodule

    首先在Started和Finished两条打印语句之间插入了一个赋值语句,同时定义了三个新信号:a_end、b_end、sigout。该程序打印输入如下。fork内部的语句将和#20 sigout = 1之前的语句同时开始并发执行,但是该执行被挂起,直到遇到了#20 sigout = 1之后,才会执行。所以我们看到,Started的打印是0延时的,所以首先输出。接着是输出0和1,然后是20ns之后的sigout有效和Finished打印语句。

在Verilog/SystemVerilog中使用fork/join的注意事项_第6张图片

    在看输出的波形,sigout和a_end都在20ns时输出,b_end在45ns时输出,符合预期。

在Verilog/SystemVerilog中使用fork/join的注意事项_第7张图片

 

你可能感兴趣的:(#,systemverilog,verilog,fpga/cpld)