博文目录
-
- 写在前面
- 正文
-
- 同步FIFO回顾
-
- $clog2()系统函数使用
- 综合属性控制资源使用
- 异步FIFO设计
-
- FIFO用途回顾
- 异步FIFO原理回顾
- 异步FIFO设计
- 异步FIFO仿真
- 参考资料
- 交个朋友
写在前面
一开始是想既然是极简教程,就应该只给出FIFO的概念,没想到还是给出了同步以及异步FIFO的设计,要不然总感觉内容不完整,也好,自己设计的FIFO模块不用去担心因IP核跨平台不通用的缺陷!那我们开始吧。
- 个人博客首页
- 注:学习交流使用!
正文
同步FIFO回顾
上一篇博客讲了同步FIFO的概念以及同步FIFO的设计问题,并给出了同步FIFO的Verilog代码以及VHDL代码,并经过了行为仿真测试,链接如下:
FPGA基础知识极简教程(3)从FIFO设计讲起之同步FIFO篇
$clog2()系统函数使用
这里简单提一下,同步FIFO的代码中用到了一个系统函数$clog2(),这个系统函数的使用方法很简单:
例如我定义了FIFO缓冲区的深度为DATA_DEPTH = 8,那么其地址(指针)位宽是多少呢br> 这时候就可以使用系统函数$clog2()了,位宽可以表示为:
指针就可以定义为:
综合属性控制资源使用
还有一点需要提的是,我们都知道在FPGA中FIFO的实现可以使用分布式资源或者BLOCK RAM,那么如何掌控呢br> 当使用FIFO缓冲空间较小时,我们选择使用Distributed RAM;当使用FIFO缓冲空间较大时,我们选择使用BLOCK RAM资源;这是一般的选择原则。
我们可以通过在设计代码中加入约束条件来控制,之前有写过
Vivado 随笔(1) 综合属性之 ram_style & rom_style/p>
就上述同步FIFO而言,我们可以在缓冲区定义时候添加如下约束:
为了验证是否有用,我们在Vivado中进行验证如下:
当设计中使用BLOCK RAM约束:
综合后的电路图如下,可见FIFO缓存区使用的资源为BLOCK RAM;
当使用Distributed RAM约束时:
综合后电路图FIFO缓冲区部分:
异步FIFO设计
FIFO用途回顾
再设计异步FIFO电路之前,有必要说明一下FIFO的用途,上篇博文提到:
- 跨时钟域
FPGA或者ASIC设计内部电路多位数据在不同的时钟域交互,为了数据安全、正确、稳定交互,我们需要设计异步FIFO进行跨时钟域交互。正如之前博客所写:漫谈时序设计(1)跨时钟域是设计出来的,而非约束出来的!
我们在时序分析时候,通常都将跨时钟域路径进行伪路径约束,因此我们必须在设计时候解决跨时钟域数据传输问题,异步FIFO在此起到关键作用。
- 在将数据发送到芯片外之前将其缓冲(例如,发送到DRAM或SRAM)
- 缓冲数据以供软件在以后查看
- 存储数据以备后用
这三条大概讲的都是一个意思,总结起来就是FIFO可以起到数据缓冲或缓存的作用,例如突然数据,我们就需要先将其缓存起来,之后再从FIFO中读出出来进行处理,这样也可以保证数据不会丢失。
引用互联 上其他说法就是:数据写入过快,并且间隔时间长,也就是突发写入。那么通过设置一定深度的FIFO,可以起到数据暂存的功能,且使得后续处理流程平滑。
异步FIFO原理回顾
无论是同步FIFO还是异步FIFO,其大致原理都是一致的,先入先出自然不必多说,关于空满的判断都是通过读写指针之间的关系来判断;还有就是异步FIFO的指针需要进行一定的处理,例如格雷码处理,这样可以减小读指针同步到写指针时钟域,或者写指针同步到读指针时钟域时出现亚稳态的概率,这是因为格雷码每次只有一位变化,这样一位数据在进行跨时钟域传输的时候亚稳态出现的概率会大大减小。同步之后便进行对比,以此来判断FIFO的空满。
那异步FIFO如何判断空满呢br> 回答这个问题之前,我想先统一的说明FIFO(同步或者异步)是如何判断空满的br>
- 第一行:写入1个数据,计数值为1;
- 第二行:写入5个数据,计数值为6;
- 第三行:读出3个数据,计数值为3;
- 第四行:写入3个数据,计数值为6;
- 第五行:写入2个数据,计数值为8,等于FIFO深度,则表示写满;
- 第六行:读出6个数据,计数值为2,表示还剩下两个数据缓存在FIFO中。
如果再接着读2个 数据,则计数值为0,FIFO就被读空了。
好了,我们分析完了同步FIFO是如何判断空满的,下面重点放在异步FIFO的原理上。
我曾写过一篇CDC问题的博客,谈到了异步FIFO的设计:
谈谈跨时钟域传输问题(CDC)
这篇博客中说,同步FIFO可以使用计数方式来判断空满,但是异步FIFO不能,因为写指针和读指针根本不在同一个时钟域,计数器无法处理这样的计数。
那么怎么处理呢br> 博客里采用的方法是对读写指针的位宽多添1位,这样可以在读写指针相等时,表示FIFO空,而在写指针和读指针最高位不同,而其他位相等时,也即写指针大于读指针一个FIFO深度的数值,表示FIFO满,这不就是意味着写指针绕了一圈,又追上了读指针了吗br> 恰是如此,用来解决不用计数而具体判断FIFO空满的问题。
这只是解决了判断空满的一个问题,也就是确定指针的关系!
那下一个问题就是如何判断br> 由于读写指针不在同一个时钟域,二者需要同步到同一个时钟域后进行判断大小。
上面是写满判断的情况,下面给出读空判断的可能情形分析:
- 当判断是否读空时,需要把写指针同步到读时钟域,具体过程是先将写指针转换为格雷码,再同步到读时钟域,之后和读指针比较,如果二者相等,则空标志置位!
还是和第一种情况有同样的插曲,当写指针转换成格雷码以及同步到读时钟域的过程中,写指针和读指针都可能还在递增,这样当二者判断相等的时候,则写指针可能还多写了几个空间,实际上并没有读空。
那问题来了,这样操作就有问题了吗样没有问题,这样也保证来了FIFO的安全,防止被读空。
下面给出手绘示意图:
注意事项
- 读写指针宽度要是$clog2(DATA_DEPTH) + 1,定义的时候应该定义为:
- 其次,判断空的时候要拿转换为格雷码并且同步到读时钟域之后的写指针与读指针比较,比较代码如下:
一定要二者相等的下一个读周期empty信 为1;
- 对于满full信 ,一定要用转换为格雷码且同步到写时钟域之后的读指针与转换为格雷码之后的写时钟比较,比较的条件是最高位不同,但是其他位相同。
- 最后提出的是转换为格雷码的方式是组合逻辑的方式,即:
//wr_pointer and rd_pointer translate into gray code wire 声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!