Verilog仿真文件中的阻塞和非阻塞赋值问题探讨


在 RTL 代码中我们知道如果表达组合逻辑时使用“=”赋值,表达时序逻辑时使用“<=”赋值,如果我们不按照这种规则来设计往往会得到意想不到的答案。虽然说在 Testbench 中我们对赋值号的要求并不是很在意,使用“=”和“<=”赋值均可,都能够仿真出来结果,且最后不会被综合成实际的电路,不会影响功能。网络上的各种资料教程也各有不同的写法,难道在 Testbench 中随便使用“=”和“<=”赋值真的对测试没有任何影响吗?经过下面的测试验证我们得到了出乎意料的答案。

测试验证

RTL代码

首先编写被测试测RTL代码:一个简单的两输入 1bit 数据相与后通过寄存器输出。

//========================================================================
// 	module_name.v	:rtl_template.v
// 	Created on		:2023-9-27
// 	Author			:YprgDay
// 	Description		:用于Verilog仿真文件中的阻塞和非阻塞问题探讨
//========================================================================
module test
(
	//=========================< Port Name >==============================
	//input
	input 		wire 					sys_clk	  	 		,
	input 		wire					sys_rst_n			,
	input 	  	wire    				in1					,
	input 	  	wire 					in2					,
		                                                    
	//output	                                            
	output		reg						out					
);

	//=========================< Always block >===========================
	//sequential logic
	//block description:用于两输入的与
	always @(posedge sys_clk or negedge sys_rst_n)	begin
		if(sys_rst_n == 1'b0)begin
			out <=	1'b0		;
		end
		else	begin
			out <=	in1 & in2	;
		end
	end	

endmodule

一、时钟初始值为1’b1

1.1、时钟用“=”赋值,输入信号用“<=”赋值(correct)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	 = 1'b1;
sys_rst_n 	<= 1'b0;
in1 		<= 1'b0;
in2 		<= 1'b0;
#200
sys_rst_n 	<= 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;

always #10 in1 <= {$random};
always #10 in2 <= {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

仿真结果同 RTL 逻辑代码实现的功能一致。

此处是220ns后两个时钟周期高,340ns后一个时钟周期高。同时还测试了该情况下的“|”、“+”、“^”运算均正确。

在这里插入图片描述

1.2、时钟和输入信号都用“<=”赋值(error)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	<= 1'b1;
sys_rst_n 	<= 1'b0;
in1 		<= 1'b0;
in2 		<= 1'b0;
#200
sys_rst_n 	<= 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk <= ~sys_clk;

always #10 in1 <= {$random};
always #10 in2 <= {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

与1.1中相比。此处是200ns后一个时钟周期高,320ns后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

在这里插入图片描述

1.3、时钟和输入信号都用“=”赋值(error)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	= 1'b1;
sys_rst_n 	= 1'b0;
in1 		= 1'b0;
in2 		= 1'b0;
#200
sys_rst_n 	= 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;

always #10 in1 = {$random};
always #10 in2 = {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

与1.1中相比。此处是200ns后一个时钟周期高,320ns后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

在这里插入图片描述

1.4、时钟用“<=”赋值,输入信号用“=”赋值(error)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	<= 1'b1;
sys_rst_n 	 = 1'b0;
in1 		 = 1'b0;
in2 		 = 1'b0;
#200
sys_rst_n 	 = 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;

always #10 in1 <= {$random};
always #10 in2 <= {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

与1.1中相比。此处是220ns后两个时钟周期高,340ns后一个时钟周期高,和1.1同,但测试了该情况下的“|”、“+”、“^”运算均有错误。

在这里插入图片描述

二、时钟初始值为1’b0

2.1、时钟用“=”赋值,输入信号用“<=”赋值(correct)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	 = 1'b0;
sys_rst_n 	<= 1'b0;
in1 		<= 1'b0;
in2 		<= 1'b0;
#200
sys_rst_n 	<= 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;

always #10 in1 <= {$random};
always #10 in2 <= {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

仿真结果同 RTL 逻辑代码实现的功能一致。

此处是210ns后1个时钟周期高,330ns后一个时钟周期高。同时还测试了该情况下的“|”、“+”、“^”运算均正确。

在这里插入图片描述

2.2、时钟和输入信号都用“<=”赋值(error)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	<= 1'b0;
sys_rst_n 	<= 1'b0;
in1 		<= 1'b0;
in2 		<= 1'b0;
#200
sys_rst_n 	<= 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk <= ~sys_clk;

always #10 in1 <= {$random};
always #10 in2 <= {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

与2.1中相比。此处是210ns后两个时钟周期高,330ns后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

在这里插入图片描述

2.3、时钟和输入信号都用“=”赋值(error)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	= 1'b0;
sys_rst_n 	= 1'b0;
in1 		= 1'b0;
in2 		= 1'b0;
#200
sys_rst_n 	= 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;

always #10 in1 = {$random};
always #10 in2 = {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

与2.1中相比。此处是210ns后两个时钟周期高,330ns后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

在这里插入图片描述

2.4、时钟用“<=”赋值,输入信号用“=”赋值(error)

仿真代码:

`timescale 1ns/1ns

module tb_test();

reg sys_clk;
reg sys_rst_n;
reg in1;
reg in2;

wire out;

//初始化
initial begin
sys_clk 	<= 1'b0;
sys_rst_n 	 = 1'b0;
in1 		 = 1'b0;
in2 		 = 1'b0;
#200
sys_rst_n 	 = 1'b1;
end

//产生 50Mhz 的时钟
always #10 sys_clk = ~sys_clk;

always #10 in1 = {$random};
always #10 in2 = {$random};

//------------------------test_isnt------------------------
test test_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.in1 (in1 ), //input in1
.in2 (in2 ), //input in2

.out (out ) //output out
);

endmodule

仿真波形效果:

与2.1中相比。此处是210ns后两个时钟周期高,330ns后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

在这里插入图片描述

结论

时钟初始值为1’b1或时钟初始值为1’b0的情况下,时钟用“=”赋值,输入信号用“<=”赋值不发生错误,其他情况均有错误出现。

所以推荐在写 Testbench 的时候时钟用“=”赋值,输入信号用“<=”赋值才能够避免这种情况,这种问题的根源其实是产生的数据没有同步时钟的原因,导致产生一些错乱,这种情况在 System Verilog 中就不会差生,这也是 System Verilog 更适合作为仿真验证语言的原因之一。至于时钟的初始值是 0 还是 1 对仿真的正确性影响不大,但是推荐大家把时钟的初始值幅值为 1,方便数据的变化都是在时钟的上升沿进行,和我们的 RTL 代码更接近。

推荐书写方式:

//==========================< Reset block >============================
initial begin
	sys_clk 	 = 1'b1;
	sys_rst_n 	<= 1'b0;
	in1 		<= 1'b0;
	in2 		<= 1'b0;
	#200
	sys_rst_n 	<= 1'b1;
end

参考资料

仿真文件中的阻塞和非阻塞

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值