Modelsim前仿真波形与NC-verilog/Active-HDL不一致?
本帖最后由 xiaocat85 于 2012-3-26 22:40 编辑本贴我已经在eetop上发过,得到了高手解答,在此贴出来给大家参考。
非常简单的加法运算,code和testbench如下:
module adder(a,b,en,clk,m,m_1,m_2);
inputa;
inputb;
input en;
input clk;
output m;
output m_1;
output m_2;
wire p;
assign p=a + b;
reg m;
reg m_1;
reg m_2;
always@(posedge clk)
if(en==1'b1)
begin
m <= p;
m_2 <= a + b;
end
else
begin
m<=4'd0;
m_2<=4'd0;
end
always@(posedge clk)
m_1<=m;
endmodule
module adder_tb;
rega;
regb;
reg en;
reg clk;
wire m;
wire m_1;
wire m_2;
adder u_adder_0(
.a(a),
.b(b),
.en(en),
.clk(clk),
.m(m),
.m_1(m_1),
.m_2(m_2));
always #1 clk=~clk;
initial begin
#0;
clk=1;
a=4'd3;
b=4'd2;
en=1'b0;
#10;
@(posedge clk)
en=1'b1;
#10;
@(posedge clk)
a=4'd5;
#10;
@(posedge clk)
b=4'd7;
#10;
@(posedge clk)
begin
a=4'd6;
b=4'd1;
end
#10;
@(posedge clk)
en=1'b0;
#20$stop;
end
endmodule
在testbench中已经写明是“边沿对齐”。Modeslim中仿真波形如下:
m与m_2 波形一样,m_1被打一拍。
但是,在NC-VERILOG和ACTIVE-HDL这两个软件中,仿真结果却不一样:
NC与active-hdl的仿真波形一致,当a或者b在时钟边沿变化的时候,m_2比m要早一拍,m_2是a与b直接输入的,m只是用了一个wire变量p,综合后的电路应该是一样的,但为什么出现这样的结果呢?
不知道modelsim与NC/active-hdl的结果,谁更合理?对于直接输入的input跟经过wire后的input的默认延迟不一样吗,尝试修改NC中的延时模型,结果还是一样。另外在开源仿真器Icarus仿真的结果更诡异,
输入量都默认没有延迟?
修改testbench,去掉“@(posedge clk)”:
module adder_tb;
rega;
regb;
reg en;
reg clk;
wire m;
wire m_1;
wire m_2;
adder u_adder_0(
.a(a),
.b(b),
.en(en),
.clk(clk),
.m(m),
.m_1(m_1),
.m_2(m_2));
always #1 clk=~clk;
initial begin
#0;
clk=1;
a=4'd3;
b=4'd2;
en=1'b0;
#10;
en=1'b1;
#10;
a=4'd5;
#10;
b=4'd7;
#10;
a=4'd6;
b=4'd1;
#10;
en=1'b0;
#20$stop;
end
endmodule
直接用时间对齐后,NC和ACTIVE-HDL的波形没有变,但modelsim的结果又发生变化了:
波形与上面的Icarus结果的一样了。
是软件中的设置有什么问题吗?到底怎么理解这些差异呢,虽然在实际中肯定会有延时。
软件版本(modelsim:10.0d/6.5gNC:5.4-S020ACTIVE-HDL:8.2-SP3 Icarus:0.9.5)
eetop3个高手回复:
it's a easy question. the reason is different simulators has different algorithm to process concurrent statement. you know the simulator must tackle many concurrent statements, but the computer we use is usually one or dual core, so the concurrent statements are processed in procedural one by one. The Verilog language reference manual doesn't give a clear rule, so it depends on simulator supplier to decide the order. Do you understand?
写tb时注意信号的生成不要在pos clk的同时用阻塞赋值(当你的模块是pos clk时)。这就是多事件同时发生:如果都是非阻塞赋值就不存在问题。但阻塞赋值时,不同仿真器的处理顺序不一样,会导致结果差异。
可用3种方式:
1:neg clk 和阻塞赋值;
2:pos clk和非阻塞赋值;
3:pos clk +单位延时 + 阻塞赋值。
将testbench中的赋值,改为:
@(posedge clk);
a <= 。。。。。
b<=。。。。。。
en<=。。。。。。
不要用阻塞赋值,会和DUT中的赋值产生竞争。
不管是什么仿真器,只要结果不一致的地方一般来说都是出现了竞争现象,在没有出现竞争的时候,结果肯定一样,和用什么算法没有关系。
主要是因为:在调度过程中,非阻塞赋值右边表达式的值的计算是和阻塞赋值的处理是同时处理的,但是它们这两件事情谁先谁后是不定的,不同的仿真器可以有不同的处理方法,这是verilog包括systemverilog标准里规定的。
什么意思呢,举个例子来说:在时刻10ns有如下操作:
事件1,阻塞赋值a = 1 ; b = 2 ;(假设之前a,b的值都是0)
事件2,非阻塞赋值 c <= a + b ;
那么我们把这三个语句写在一起会出现什么样的结果呢?
c的值在此时刻有两种可能:
c可能等于3也可能等于0还可能是其它值,为什么?
因为非阻塞赋值右边表达式a+b的计算和a=1,b=2这三件事情是同时进行的,但是呢这三个事情先后顺序IEEE是没有规定的,仿真器可能如下操作:
(1)a=1;b=2;a+b此时c的值会是3
(2)a+b;a=1;b=2此时c的值会是0
(3)a=1;a+b;b=2此时c的值会是1
(4)b=2;a+b;a=1此时c的值会是2
(5)a+b;b=2;a=1此时c的值会是0
.......一共6种可能4种结果
在调度的下一步才是执行用a+b的值来更新c的值,所以非阻塞赋值主要分为两步:第一步是先计算右边表达式的值,第二步才是用这个值更新左边的变量的值,而且这两步是离散的。
而阻塞赋值是一步就完成了计算右边表达式的值并且更新,比如c = a+b ,那么a+b和赋值是一气呵成的,是有个原子步骤,而不是可分割的两个离散的步骤。
多读verilog的standard,体会其中含义,这样才能精通verilog,而不是光会几个if else 等等,表面上懂了,实际上碰到有问题的代码就分析不出来其中问题的源头了,祝进步!
PS:晚上回来后修改了tb的代码,全部改成非阻塞的赋值,4个仿真器的结果一致了,此问题也就得到了完美解答。在编写RTL代码时需要注意 阻塞与非阻塞,在编写tb的时候也需要跟DUT联合起来考虑这个问题,另外如果加入延时参数,阻塞与非阻塞的差异就容易看出来了
用modelsim,没有这样比较过,顶楼主的比较和探索。。。 加入延时参数,阻塞与非阻塞的差异就容易看出来了,谢谢提醒 顶楼主的分享精神,赞! 不错,不错,赞一个
页:
[1]