FPGA笔记(十)-正确初始化的重要性

时间:2022-09-09 14:42:32

FPGA笔记(十)-正确初始化的重要性

在做上一个Rom的读取仿真过程中遇到一个问题,困扰了我俩三天,百思不得其解!很简单的一个例程,仿真了很久才找出问题,一看就是经验不足导致,毕竟积累得少。废话不多说,看我遇到这个奇葩的问题吧。我一开始写的代码如下:

module testrom(
input CLK,
input Rst,
output [7:0] data,
output reg [4:0] addr
);

Rom Rom_0(
.address(addr),
.clock(CLK),
.q(data));
//初始化
initial
begin
addr <= 5'd0;
end

always@(posedge CLK or negedge Rst)
begin
if(!Rst)
addr <= 5'd0;
else
if( addr == 5'd31 )
addr <= 5'd0;
else
addr <= addr + 5'd1;
end

endmodule

在我看来这里逻辑上没有任何问题啊,只不过是个计数器,从addr=0计数到31就从新开始计数,正好用来提供一个地址用来读取Rom

结果仿真出来的图是这样的:

FPGA笔记(十)-正确初始化的重要性

data、addr全是红线,后来一步一步地排查才找到原来从地址这块就有问题。

FPGA笔记(十)-正确初始化的重要性

简化程序找错误啊,找啊,已经简称这样了,还看不出来

module testrom(
input CLK,
input Rst,
//output [7:0] data,
output reg [4:0] addr
);



initial
begin
addr <= 5'd1;
end


always@(posedge CLK or negedge Rst)
begin
if(!Rst)
addr <= 5'd0;
else
if( addr == 5'd31 )
addr <= 5'd0;
else
addr <= addr + 5'd1;
end

endmodule
简化成这样都不好使,我就纳闷了,计数器这么简单的几句不可能写错啊

但是换一种形式来表达的话是好使的,always进程块中,if(addr<=5'd31) addr<=addr+5'd1;  else   addr<=5'd1;这让我百思不得其解呀。俩种表达形式,换成数电的角度来看,只是选择器的使能端不一样而已,看RTL级视图

FPGA笔记(十)-正确初始化的重要性

FPGA笔记(十)-正确初始化的重要性

看一看也没有毛病!

查了很久才知道毛病是出在哪块地方了,问题出在初始化!只要初始化对了,这俩种表达形式都可以,但是没有初始化的话(初始化的形式错了),网上很多争论初始化的地方,我都一一试过了,就只有lessthan比较器(或者less)(注:这里出错了,应该用less,用if(addre<5'd31)才符合从0-31,懒得去换图~将就着看吧)这样的表达形式好使。看我后来改的代码。

module testrom(
input CLK,
input Rst,
//output [7:0] data,
output [4:0] addr
);

reg [4:0] addre=5'd0;//定义寄存器时直接初始化时可以赋初值的

assign addr=addre;
//Rom Rom_0(
//.address(addr),
//.clock(CLK),
//.q(data));

  //不在在这里使用,一般用于testbench内,是属于不可综合的
//initial
//begin
//addr <= 5'd1;
//end
//已经测试完毕,addr并不是从1开始而是0

always@(posedge CLK or negedge Rst)
begin
if(!Rst)
addre <= 5'd0;
//这里也不是初始化的地方,测试完毕
else
if( addre == 5'd31 )
addre <= 5'd0;
else
addre <= addre + 5'd1;
end

endmodule
FPGA笔记(十)-正确初始化的重要性
所以下次赋初值的时候不要在initial块内赋值了,没有用,直接在定义寄存器时赋初值。但是看到没,这个addr是从1开始的。所以我第一次访问的是1-31的地址所存的数。第二次循环才能从零开始。如果在上面这个建模内将if(addre==5'd31)  addre<=5'd30;else  addre<=addre+5'd1;改成 if(addr<5'd31) addr<=addr+5'd1;  else   addr<=5'd1; 还是一样从1开始,但是再将定义寄存器时不初始化。

仿真结果又不一样了。

FPGA笔记(十)-正确初始化的重要性

这是从address是从0开始的,这又让我费解,第一个上升沿到底在哪?请看testbench

FPGA笔记(十)-正确初始化的重要性

CLK初始化为1,如果将第一个上升沿理解为一初始化为1的那个上升沿,那么不初始化时候的现象又作何解释。

我又陷入了一个找第一个上升沿的怪圈,既然这样我把testbench内CLK的初始值改为CLK=1'b0;以正常初始化寄存器来仿真

FPGA笔记(十)-正确初始化的重要性

这样的话,就没有问题了,初始化为0,第一个上升沿过来是为1,那么之前初始化CLK=1'b1时的仿真结果和不初始化的仿真结果又作何解释呢。

 以我的理解就是:不初始化,modelsim会将第一个脉冲触发沿用来初始为0的,从第二个脉冲触发沿对always块才生效。

已初始化,第一个脉冲触发沿对always块生效。(属于个人理解,如有不一样的看法可以一起探讨探讨)

从以下不初始化的寄存器可以看到结果 。

FPGA笔记(十)-正确初始化的重要性

所以上述CLK=1'b1时的现象就说的通了,不初始化之所以会从0开始是因为它要初始化,而初始化过的从1开始是因为它已经初始化过了 。

针对遇到的这么多问题,总结一下:

寄存器正确初始化——>reg [5:0] addresss=5'd1;//定义寄存器时就赋初值,initial块(用于Testbench)在.v文件是不可综合的

一个仿真的经验——>如果是posedge CLK触发的话,testbench里初始化CLK=1'b0;(这样看得更清楚)

不初始化的危害——>仿真时modelsim会先赋初值0,然后再响应敏感事件(烧写到具体电路时,情况不明),且上述俩种逻辑上一样的if-else却有一种情况是不好使的。

建议——>对于要自加的数(如:addr<=addr+1'b1),只能出现在时序逻辑里,而且一定要初始化!



笔记做得有些混乱,请莫见怪!如有不一样的理解,可以一起讨论讨论!