verilog中parameter的计算
在verilog中计算参数可以避免重复计算。
一个很常用的例子是用DDS(Direct Digital Synthesizer)来产生一个定时器。
通常DDS的公式为:
f = Fcw*fclk/2^B_DDS
通常将Fcw称为DDS的频率控制字,
2^B_DDS为DDS的相位间隔数,
fclk为时钟频率。
用t表示f对应的周期,则:
t = 1/f
我们需要由t,fclk,B_DDS计算出Fcw。
Fcw = 2^B_DDS/(t*fclk)
在verilog中,可以采用参数来计算Fcw。
以后就不用每次手工计算Fcw了。
没想到在写代码的过程中,出现了三次错误。
第一次:
localparam fclk = 10000000;
//单位ms
localparam t = 80;
localparam B_DDS = 32;
localparam [32:0]Fcw = (2**B_DDS)/fclk/t*1000;
reg over = 0;
reg [31:0] cnt = 0;
always@(posedge CLK)begin
{over,cnt} <= cnt+Fcw[31:0];
end
这次的问题是:Fcw的位宽设置成33位,算出来的值不对。
为啥不对?
把位宽设置大了就行了。
为啥设置大了就行?
我也没想通。
第二次:
localparam fclk = 10000000;
//单位ms
localparam t = 80;
localparam B_DDS = 32;
localparam [63:0]Fcw = (2**B_DDS)/fclk/t*1000;
reg over = 0;
reg [31:0] cnt = 0;
always@(posedge CLK)begin
{over,cnt} <= cnt+Fcw[31:0];
end
这次发现参数里的‘/'的结果是截断小数位的整数。如果连着做两次除法,计算出来的Fcw值误差比较大。
第三次:
localparam fclk = 10000000;
//单位ms
localparam t = 80;
localparam B_DDS = 32;
localparam [63:0]Fcw = (2**B_DDS)*1000/(fclk*t);
reg over = 0;
reg [31:0] cnt = 0;
always@(posedge CLK)begin
{over,cnt} <= cnt+Fcw[31:0];
end
这次先计算乘法,只做一次除法。
这次的程序用modelsim仿真的结果是正确的,但是用ise综合的结果是over始终拉高。
最后,把程序改成了:
localparam fclk = 10000000;
//单位ms
localparam t = 80;
localparam B_DDS = 32;
localparam [63:0]Fcw = (2**B_DDS)*1000/(fclk*t);
reg over = 0;
reg [31:0] cnt = 0;
always@(posedge CLK)begin
{over,cnt} <= cnt+Fcw;
end
这次去掉了Fcw的位宽限制。ise综合之后,下载的bit文件功能正常了。
为啥正常了,什么道理。
根据夏宇闻,甘伟翻译的《Verilog Hdl入门》第三版P52,表达式中的所有中间结果应取最大操作数的位宽(在赋值时,此规则也包括赋值等号左端的目标变量)。
为啥仿真结果和ise综合结果不同,我真的疑惑。