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)beginif(rst)cnt <= 0;else if(cnt == TIME - 1)cnt <= 0;elsecnt <= cnt + 1;
endendmodule

假设我们下次设计需要一个计时器的话,直接调用上面的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 / 1psmodule clog2_test#(parameter integer    num = 325
)
(input                          clk,input                           rst,output  reg [$clog2(num) - 1:0] result
);always @(posedge clk)beginif(rst)result <= 0;elseresult <= result + 1;
endendmodule

我们直接看reg result的位宽综合出来到底是多少。如果以e为底向上取整,则位宽应是6;如果以2为底向上取整,则位宽应是9。Vivado综合的原理图局部如下:

可以看到最后编译出的结果是9位的,也就说明Vivado是支持这个系统函数的(版本:2019.2)。

其他变量的位宽设计同理。

Verilog设计中如何匹配变量的位宽?($clog2系统函数)相关推荐

  1. 高速PCB 设计中终端匹配电阻的放置

    摘要:本文简要的总结了在高速数字设计中串联终端匹配和并联终端匹配的优缺点,并对这两种匹配方式的终端匹配电阻处于不同位置时的匹配效果做了相应的仿真和深入的分析,得出了串联终端匹配电阻对位置的要求没有终端 ...

  2. Verilog学习之数据常量表达与位宽

    本文原创内容,转载请标明出处. 原文链接:https://blog.csdn.net/Cixil/article/details/89422200 Verilog的表示形式 Verilog中整数,整型 ...

  3. FPGA基础设计(11)Verilog任务、函数、系统任务、系统函数

    目录 1.概述 2.函数(function) 3.任务(task) 4.系统任务 4.1 显示任务 4.1.1 display和write任务 4.1.2 strobe监控 4.1.3 连续监控 4. ...

  4. 【Linux学习笔记】27:环境变量中的语系变量

    语系就是用的是什么人类语言,本节专门学习环境变量中的语系变量. [1]locale确定系统当前语系 [root@bogon ~]# locale LANG=zh_CN.UTF-8 LC_CTYPE=& ...

  5. c语言volatile 结构体,volatile一般用来修饰结构体中的成员变量吗?

    保留字肯定就是关键字撒, (1)auto 这个这个关键字用于声明变量的生存期为自动,即将不在任何类.结构.枚举.联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量.这个关键字不怎么多 ...

  6. python中字符串怎么引用_Python:字符串中引用外部变量的3种方法

    方法一: username=input('username:') age=input('age:') job=input('job:') salary=input('salary') info1='' ...

  7. python定义私有变量的方法_Python类中的 私有变量和私有方法

    默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类似public,private等关键词来修饰成员函数和成员变量. 在python中定义私有变量只需要在变 ...

  8. 【Verilog 语法】~ if-else、case、for、generate、函数 function、任务 task、过程块、位宽计算、阻塞/非阻塞、时间尺度、存储器设计、

    文章目录 1. if-else 1.1 设计要点 2. case 2.1 概述 2.2 语法 2.3 注意事项 3. for 3.1 区别与其它语言的for循环 3.2 注意事项 4. generat ...

  9. (42)System Verilog接口变量数据位宽扩展

    (42)System Verilog接口变量数据位宽扩展 1.1 目录 1)目录 2)FPGA简介 3)System Verilog简介 4)System Verilog接口变量数据位宽扩展 5)结语 ...

最新文章

  1. linux下使用sort命令升序、降序、随机及组合方式排序方法
  2. Java各版本的重大改变
  3. 计算机及Linux基础简介
  4. 你所不知道的SQL Server数据库启动过程,以及启动不起来的各种问题的分析及解决技巧(转)...
  5. 【jQuery】使用jquery.form.js,获取提交表单返回值
  6. AD4003 VHDL设计及仿真
  7. LAMP架构调优(一)——隐藏Apache版本信息
  8. H3C DHCP中继和RIP配置实验
  9. visio防火墙可以连接什么_分享一款网工必备网络拓扑图绘制工具Visio以及大量厂商图标库...
  10. 视频处理中各个分辨率/数字电视系统显示格式 的介绍(QCIF,CIF,4CIF,D1,720P,1080I,1080P等)
  11. contextcapture多区块点云_Smart 3D (ContextCapture) 4.4.6版本最新功能
  12. 如何用 Python 打飞机 ?
  13. 虚拟化之Proxmox VE虚拟机创建及模板制作
  14. 秒杀活动,怎么设计全套技术方案
  15. io.fabric8.docker-maven-plugin插件使用
  16. Compact Multi-Signatures for Smaller Blockchains学习笔记
  17. 炉石传说 疯狂爆破者空场炸死2个精灵龙的概率
  18. 【论文阅读笔记】语义三维重建CVPR2011:Semantic Structure from Motion
  19. |spoj 694|后缀数组|Distinct Substrings
  20. 高仿 IOS遨游哈哈最新版

热门文章

  1. #Java学习总结面向对象+抽象+接口
  2. 杰里之测试盒使用说明篇
  3. 疯狂python讲义学习笔记——后十章完结
  4. HDU - 3709 (Balanced Number)
  5. 小程序敏感词汇过滤之输入什么都返OK
  6. 打印机有墨水却打印不了,显示end of service
  7. 实习工作中的对于ubuntu系统命令使用的一个总结
  8. 网站、服务器速度测试方法
  9. 【MariaDB】浅谈 MariaDB 数据库
  10. java计算机毕业设计ssm云共享知识交流平台e36ho(附源码、数据库)