Verilog设计中如何匹配变量的位宽

1、位宽太小
在fpga设计中,我们经常需要用寄存器来寄存某些“数量类”的变量,比如fifo的深度啦、或者计数器的最大值啦;又或者输入输出信号也需要将位宽用parameter参数化以便更好的调用等。
举个简单的小例子:系统频率100m(周期10ns),假设需要要求设计一个计时器计时100ns,那么需要计数次数为:100ns/10ns - 1 = 9,9这个数需要用多大位宽的寄存器表示呢?很简单,以2为底取对数就行,答案是最少4位宽。为了方便地复用这个模块,我们把计时时间参数化并放到模块外,如下:
module counter #(
parameter time = 'd10 //计时时间,单位10ns
)(
input clk_100m ,
input rst
);
reg [3:0] cnt; //计数器
//计时器
always@(posedge clk_100m)begin
if(rst)
cnt else if(cnt == time - 1)
cnt else
cnt end
endmodule
假设我们下次设计需要一个计时器的话,直接调用上面的counter模块并把time这个参数改成自己需要的参数就可以,这样做理论上是可以的,只是会有一个致命的隐患。不妨再假设:我现在调用了counter模块,并将time设置为20,以实现计时200ns的功能。当time = 20这个参数传递到被例化模块后,可以发现由于cnt寄存器的位宽仅为4位,其能表示的最大值为4'b1111(即十进制下的数字15),每次其到达15后就溢出为0重新开始了,也就是说这个200ns的计时器实际上根本就计数不到200ns。
这个隐患发生的原因就是在设计寄存器cnt时的位宽只有4位,无法满足“大量时间的计时任务”。
2、自己写一个function
现在来想一下如何解决上述的位宽不匹配的问题。将寄存器的位宽设计为一个较大的数值(如固定为32bit)不失为一个不错的方法,但是如果将这条规则适用到每一个寄存器,则势必造成大量的资源浪费(你资源多你随便玩)。而且该方法指标不治本,我们需要做的是,这个寄存器应该有多大就设计多大的位宽(有多大的脚就穿多大的鞋,鞋子太大一定能穿,但你脚不一定舒服)。
前面说过寄存器的位宽的计算方法:以2为底取对数。所以我们只需要设计一个function(可综合),来实现此项功能即可。刚好在xilinx的许多源码都出现了这个简单的function,我们直接拿过来用就是的:
// function 实现
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
// 使用案例
localparam integer c_transactions_num = clogb2(c_m_axi_burst_len-1);
reg [c_transactions_num : 0] write_index;
reg [c_transactions_num : 0] read_index;
上面的代码就是定义了一个求位宽的function,用其求得某类寄存器的位宽,然后再对寄存器赋值时就直接使用求得的位宽来赋值,这样复用起来就比较方便了。
我们将这个代码放到上面的计数器模块中后,不管需要计数多大时间,都能计算出相匹配的寄存器位宽了。
3、无法在输入输出端口使用
自己写function实现对2取对数的功能也有一定的局限性:无法对输入输出端口信号使用该function。function是定义在模块内部,所以若输入输出端口也需要根据输入的parameter参数来以2为底取对数的话此种方法就无能为力了。比如:设计一个同步fifo,输出信号fifo_cnt(计数器)是对写入fifo的数据进行计数的寄存器,其最大值即为fifo的深度data_depth ,所以fifo_cnt的位宽就需要在定义模块输入输出端口时确定,显然这无法使用自己构造的 cblogb2 function。那该当如何?
//计数器法实现同步fifo
module sync_fifo_cnt
#(
parameter data_width = 'd8 , //fifo位宽
parameter data_depth = 'd16 //fifo深度
)
(
input clk , //系统时钟
input rst_n , //低电平有效的复位信号
input [data_width-1:0] data_in , //写入的数据
input rd_en , //读使能信号,高电平有效
input wr_en , //写使能信号,高电平有效
output reg [data_width-1:0] data_out, //输出的数据
output empty , //空标志,高电平表示当前fifo已被写满
output full , //满标志,高电平表示当前fifo已被读空
output reg [$clog2(data_depth) : 0] fifo_cnt //$clog2是以2为底取对数
);
//省略部分代码
endmodule
4、$clog2系统函数
其实办法也有,在上面的代码中也展示出来了,就是使用 $clog2 这个verilog的系统函数。$clog2是verilog--2005标准新增的一个系统函数,功能就是对输入整数实现以2为底取对数,其结果向上取整(如5.5取6)。有一点需要说明的是,目前vivado2017以上的版本都是支持这个系统函数的(quartus ii不清楚 )。但是百度搜索这条结果的时候有两条结论是错误的:
1、vivado不支持$clog2系统函数
2、$clog2系统函数在vivado实现的是以e为底取对数,而不是2
接下来写个简单的模块验证下vivado对$clog2系统函数的支持如何
`timescale 1ns / 1ps
module clog2_test#(
parameter integer num = 325
)
(
input clk,
input rst,
output reg [$clog2(num) - 1:0] result
);
always @(posedge clk)begin
if(rst)
result else
result end
endmodule
我们直接看reg result的位宽综合出来到底是多少。如果以e为底向上取整,则位宽应是6;如果以2为底向上取整,则位宽应是9。vivado综合的原理图局部如下:
可以看到最后编译出的结果是9位的,也就说明vivado是支持这个系统函数的(版本:2019.2)。
其他变量的位宽设计同理。


串口屏解决方案:大彩串口屏在超声智能除垢行业的应用
三诺数码集团市外点亮绿色照明
先进制程布局各有打算,GF/联电争抢晶圆榜眼
佳能的CMOS传感器具有独特的设计,可以满足苛刻的视觉应用需求
从《哪吒》说起 回顾人工智能产业的那些年
Verilog设计中如何匹配变量的位宽
浅析导热硅脂对电脑的重要性
UDC签约华星光电,预测2019年OLED材料市场迎来“重要性增长”
C语言中不建议使用的关键词
非接触式预付费智能表方案
斯诺登再爆猛料 美国更大规模监控计划曝光
华尔街日报:欧洲运营商客户抱怨华为对手交货速度慢 拖延5G部署进程
VR未来最有潜力的市场原来是它
深开鸿与华为政务一网通军团签约同舟共济合作伙伴,携手深耕城市数字化
传上汽奥迪再清库存,最大直降20万元
智能家居市场又推出了新家电,智能魔镜亮相
区块链和GDPR可以学到哪些
全面碾压高通5G 联发科天玑1000简直开挂
200ppi转以太网通过4Gwifi在医药设备移动平台(平板电脑、手机)
研究人员对恐龙新物种可能的样子进行计算机模拟