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

文章目录

  • 测试验证
    • RTL代码
    • 一、时钟初始值为1’b1
      • 1.1、时钟用“=”赋值,输入信号用“<=”赋值(correct)
      • 1.2、时钟和输入信号都用“<=”赋值(error)
      • 1.3、时钟和输入信号都用“=”赋值(error)
      • 1.4、时钟用“<=”赋值,输入信号用“=”赋值(error)
    • 二、时钟初始值为1’b0
      • 2.1、时钟用“=”赋值,输入信号用“<=”赋值(correct)
      • 2.2、时钟和输入信号都用“<=”赋值(error)
      • 2.3、时钟和输入信号都用“=”赋值(error)
      • 2.4、时钟用“<=”赋值,输入信号用“=”赋值(error)
  • 结论
  • 参考资料

在 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后一个时钟周期高。同时还测试了该情况下的“|”、“+”、“^”运算均正确。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第1张图片

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后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第2张图片

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后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第3张图片

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同,但测试了该情况下的“|”、“+”、“^”运算均有错误。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第4张图片

二、时钟初始值为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后一个时钟周期高。同时还测试了该情况下的“|”、“+”、“^”运算均正确。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第5张图片

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后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第6张图片

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后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第7张图片

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后一个时钟周期高。有错误,同时还测试了该情况下的“|”、“+”、“^”运算均错误。

Verilog仿真文件中的阻塞和非阻塞赋值问题探讨_第8张图片

结论

时钟初始值为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

参考资料

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

你可能感兴趣的:(Verilog语法,fpga开发,Verilog,阻塞与非阻塞,仿真注意事项)