在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。

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

2. join/join_any/join_none

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

    使用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。

2.2 join_none

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

    两条打印语句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打印语句。

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

 

Verilog/SystemVerilog IEEE规范是由IEEE(美国电气与电子工程师协会)制定的一套关于硬件描述语言Verilog和SystemVerilog的规范和标准。这本规范详细定义了这两种语言的语法、语义、约束和使用方法。 Verilog是一种硬件描述语言,用于描述数字电路的行为和结构。Verilog使用了一种类似于C语言的语法,可以描述硬件的组成、功能和时序等信息。Verilog语言广泛应用于集成电路设计,可以用于逻辑仿真、综合、布局和时序验证等环节。 SystemVerilogVerilog的扩展版本,增加了一些特性,如类、接口、包、属性、事务级建模等。这使得SystemVerilog更适合用于复杂系统的描述和验证。SystemVerilog是一种更高级的硬件描述语言,提供了更多的工程特性和设计方法。 Verilog/SystemVerilog IEEE规范提供了一个统一的标准,确保了不同的厂商和工具之间的互操作性。遵守规范可以保证设计的正确性和可移植性。此外,规范还定义了一些验证方法和技术,帮助设计人员进行功能验证和系统级仿真,从而提高设计的可靠性和效率。 对于硬件工程师和设计人员来说,熟悉Verilog/SystemVerilog IEEE规范非常重要。规范提供了详细的语法和语义定义,使得开发人员能够编写正确且高效的硬件描述代码。同时,规范还包含了一些重要的建议和最佳实践,可以帮助设计人员避免一些常见的设计错误和陷阱。因此,掌握Verilog/SystemVerilog IEEE规范对于硬件设计的成功至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值