文章目录
- 测试验证
- 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后一个时钟周期高。同时还测试了该情况下的“|”、“+”、“^”运算均正确。
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
参考资料
仿真文件中的阻塞和非阻塞