- ?前言
- ?实践分析
- ?推荐的仿真设计
- ?总结
前言
提前给出一些观点:
- 仿真是为了仿真,所以不要设置极限情况,例如在时钟上升沿通过阻塞赋值给数据,应该避免这种情况;
- 各种不同的仿真软件对时钟上升沿通过阻塞赋值给数据的理解不一致,例如modelsim和isim;
- 可以使用非阻塞赋值设置数据值,避免在时钟上升沿时刻使用阻塞赋值给数据。
本文最后会给出推荐的仿真观点。
实践分析
事实上,上面三点说的是针对一种情况,我们举一个简单的例子说明。我们的设计文件,很简单,就是一个检测上升沿的程序:
代码语言:javascript复制`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// website : https://blog.csdn.net/Reborn_Lee
//////////////////////////////////////////////////////////////////////////////////
module delay(
input wire in,
input wire clk,
input wire rst,
output reg out_r3
);
reg in_reg;
always@(posedge clk) begin
if(rst) begin
in_reg <= 0;
end
else begin
in_reg <= in;
end
end
wire mid_pos;
assign mid_pos = ~in_reg & in;
reg reg_pos;
reg reg1_pos;
reg reg2_pos;
always@(posedge clk) begin
if(rst) begin
reg_pos <= 0;
reg1_pos <= 0;
reg2_pos <= 0;
out_r3 <= 0;
end
else begin
reg_pos <= mid_pos;
reg1_pos <= reg_pos;
reg2_pos <= reg1_pos;
out_r3 <= reg2_pos;
end
end
endmodule
如下,我们的testbench为:
代码语言:javascript复制`timescale 1ns / 1ps
// Engineer: Reborn Lee
// website : https://blog.csdn.net/Reborn_Lee
module sim_delay;
reg clk;
reg rst;
reg in;
wire out_r3;
initial begin
clk = 0;
forever begin
#5 clk = ~clk;
end
end
initial begin
rst = 1;
in = 0;
#20
rst = 0;
#100
@(posedge clk) begin
in = 1;
end
#300
in = 0;
end
delay delay_inst(
.in(in),
.clk(clk),
.rst(rst),
.out_r3(out_r3)
);
endmodule
我们对输入进行赋值:
代码语言:javascript复制 @(posedge clk) begin
in = 1;
end
可见,使用的是阻塞赋值,且在时钟上升沿赋值;
在这种情况下,我们使用vivado自带的仿真工具仿真,得到结果如下:
isim仿真工具
可见,得不到上升沿,这我们其实也能理解;由于是行为仿真,所以,一切都是理想的,不考虑延迟;我们使用阻塞赋值在时钟上升沿时刻给输入赋值,立即生效;固然,我们的时钟在上升沿采样的时候,得到in_reg和in是同边沿的,这样自然就得不到边沿了,后面延迟多拍也自然无用。
这是vivado的仿真工具isim对这种情况的理解。
现在问题来了,当我们使用modelsim进行仿真的时候,情况是这样的:
modelsim仿真工具
它会对in延迟一整拍,也就是要给时钟,最终也就能得到上升沿了。同样的设计,同样的仿真文件,为何会出现这样的差异呢?这里给出的解释是仿真工具对这种情况的理解问题:在实际情况中(考虑真实环境,存在延迟),这种输入的边沿出现在时钟的有效沿,本身就是不合法的,因为这会导致时序通过不了,例如建立时间。对于这种情况,modelsim或者questasim的处理就比较直接,我不准出现这种情况,如果你出现了,我们认为此刻无效。其效果类似于非阻塞赋值:
代码语言:javascript复制 @(posedge clk) begin
in <= 1;
end
这里使用了非阻塞赋值,那么在时钟上升沿时刻,in的值就没那么快生效,如此,无论在那个平台仿真,仿真情况都一致了。
下面是这种情况下在任意平台的仿真图:
任意平台
推荐的仿真设计
开门见山,在时钟有效沿时刻给数据是不符合实际的,是极端的做法,这在实际情况中不会出现,即使出现,综合布线工具也会重新布线避免这种情况,否则就是时许违规。因此为了有意义的仿真且统一仿真平台,我们应该在距离有效沿一定延迟给数据,例如:
代码语言:javascript复制 #100
@(posedge clk) begin
#1 in = 1;
end
或者:
代码语言:javascript复制 #100
@(negedge clk) begin
in = 1;
end
总之,别在有效沿给数据,以这种情况为例,给出仿真图:
代码语言:javascript复制 #100
@(posedge clk) begin
#1 in = 1;
end
推荐的仿真方式
总结
从上面的分析可以看出,为了适应不同的仿真平台(并不是说哪个仿真平台错了) ,且本着仿真意义的实际情况,我们不应该在极端的情况下进行仿真,不仅没有意义,而且让人疑惑。推荐的做法是在下降沿或者距离时钟的上升沿有一定的延迟给数据,这才能避开不同平台的差异且有实际意义。