• 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    一、时钟初始值为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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    仿真结果同 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    与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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    与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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    与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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    仿真结果同 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    与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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    与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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仿真波形效果:

    与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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    参考资料

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

  • 相关阅读:
    【PyQt】调整子控件的层级以调整绘制的先后顺序
    前端解决跨域 3 种方法 -对接读卡器时出现跨域问题
    Scrapy使用GitHub上的ProxyPool代理池
    使用VBA快速完成不规则数据整理
    公司新来了个拿25K的测试,一介绍,原来是测试天花板级别的···
    LeetCode每日一题(833. Find And Replace in String)
    GB/T 7134-2008 浇筑型工业有机玻璃板材检测
    PHP 程序员是学 Swoole ?还是学 Go ?
    Listener(监听器)-ServletContextListener
    Vue3之uni-app中注册全局属性案例
  • 原文地址:https://blog.csdn.net/weixin_48412658/article/details/133362051