作者: 饿狼传说,文章来源: FPGA的现今未微信公众号
注:本文由作者授权转发,如需转载请联系作者本人
说明:这里主要总结FIFO应用相关的问题,FIFO的设计原理有很多资料描述,这里不再说明。
FIFO和RAM可以说是一对孪生兄弟(RAM的应用),FIFO的应用和RAM的应用有很多相似的地方。这里重点介绍下FIFO和RAM不一样的地方,以及在工程使用中的一些问题。
1、什么是FIFO
顾名思义,先进先出。它不可以随机访问,这个特性和RAM最大的区别,也决定了他们使用场景的不同。
2、FIFO特性
FIFO从逻辑上讲,就是RAM和控制逻辑组成(同步FIFO和异步FIFO的设计也是一个比较经典的问题,这里不展开),所以这也就决定了FIFO的特性和前面提到的RAM的特性有很多相似的地方。这里主要简单说下不同点:
(1)、FIFO分类
从使用的RAM的资源类型来讲,和RAM是一样的。即FIFO中的缓存可以是DRAM、BRAM或者URAM。
从端口开来讲,FIFO没有分类,因为是固定的2个端口,写端口和读端口。
从读写端口时钟是否同源分为同步FIFO和异步FIFO,本文所描述的内容对同步FIFO和异步FIFO都适用。
(2)、接口时序
和RAM的时序类似,少了读写地址信号,多了一个空信号和满信号。
其他比如大小配置、工作频率等特性上和RAM都是一致的。
3、FIFO的使用场景
在FPGA的设计中,很多时候是即可以使用RAM,又可以使用FIFO,就个人习惯而言,作者更加喜欢使用FIFO,原因就是一个,不用控制地址,尤其是xilinx的预读FIFO,读时序上使用起来更加方便。
(1)模块缓存
在高性能的系统设计中,我们有一个基础的原则就是“尽力而为”,即在本模块中,在资源和复杂度没有显著增加的情况下,应该将本模块的性能做的尽可能的高(对性能有特殊要求的除外),由于不同模块之间的性能处理是有差异的,如何处理模块之前的衔接呢?最简单高效的方法就是用FIFO来隔离。如下图所示:
模块A的处理不需要考虑模块B的性能,只需要看模块B中的FIFO是否将满,只要不满,就可以不断的向模块B写数据。这样的好处是模块A和模块B只有一个信号耦合,即该FIFO的将满(afull)信号。
该场景在ASIC设计中可能并不是这样,考虑到面积,需要将缓存用到极致,而不是这样的“浪费”。
(2)数据缓存
这也是一个很常用的场景,在模块内部处理数据的过程中,当前后处理的性能不一致的时候,往往会用一个fifo做为前面处理数据的中间缓存,平衡前后的性能。其实和上面(1)的应用是类似的,只不过一个是用于模块间,一个用于模块内部。
(3)跨时钟域
在不同时钟域传递多bit数据的时候,最简单可靠的方案就是采用异步FIFO。
4、FIFO使用的注意事项
(1)空满信号的使用
FIFO的空满信号一般有4个,满(full)、将满(afull)、空(empty)、将空(aempty)。但是在实际应用中最常用的是将满和空。
为什么用将满了?如应用场景3.1中描述的A模块在FIFO没有给出反压的时候会一直发送数据,当遇到B模块给出的反压后,A应该停止传输数据。但是A模块为了简化设计,数据传输可能是基于“一段”数据来实现的。即A不能马上停止发送数据,需要把当前这“一段”数据发送完后才停止发送数据,B模块需要有空间把当前这一段数据接收完。所以B采用afull信号更合理,至于afull的水限是多少,则需要AB两个模块协商好。
为什么用空了?设计中把FIFO的数据读完,一般也是2种方案,1、根据长度来读,即有长度信息后,按照长度信息来读取;2、非空就读。遇到FIFO空就停下来。所以一般用空信号来保证FIFO中数据能被读完。
(2)遇到空停止读数据时多读一拍问题
前面说的读FIFO中数据采用“非空即读”的方案时,遇到empty信号后,就停止读FIFO,但是这样会多读一拍数据,产生溢出。如下图所示:
在读使能(ren)是时序逻辑产生的时候,遇到empty信号后停止后,会在红色的位置多读一拍。所以给FIFO真正的读使能(fifo_ren)必须用组合逻辑产生,用ren & (~empty)来产生,消除最后多读的一拍数据。
(3)将满(afull)水限
该问题在前面提过了,这里再单独说明。因为这个问题非常重要。遇到过太多因为将满水位设置不合理导致FIFO写溢出的案例。如何设置FIFO afull的水位,抓住一点就可以:给出afull信号后,前级模块还需要写多少数据、路上已经有多少数据。如下图所示:
4个红色的FIFO中存放的是数据长度,先将4个长度信息调度到黄色FIFO中,然后根据长度信息读DDR,从DDR返回的数据发给4个不同的队列。队列和前面红色FIFO是一一对应的。这样的设计会存在一个问题,就是当有一个后继队列反压(紫色信号),会阻塞其他队列数据的发送。这个问题解决的方法有很多,这里说下不改变上述架构的情况下的最简单的方案。
紫色的反压信号可以在3个地方起作用。读蓝色数据FIFO、读黄色命令FIFO、读红色命令FIFO。但是前2种方案都不能解决该问题。唯一的办法就是反压到红色的FIFO中。这种方案的问题就是反压后,在路上的数据比较复杂。一般情况下,红色FIFO中命令对应的最长数据是已知的,这个时候,可以控制黄色FIFO的深度来控制在路上数据,即保证在路上数据全部写入后继FIFO后,也不会溢出即可。假如一个红色命令对应的长度最大值为1Kbyte。我们控制黄色FIFO的深度为6,即同一个队列数据在路上最多有6Kbyte数据还会继续写入后继FIFO。后继FIFO保证6kbyte以上的水位后,即可解决上面阻塞问题。
FIFO的应用是FPGA设计中的基石,应用的场景有很多,但是基本都在上述框架内,以上是一些工程经验的总结,文章中有不对的地方,也欢迎留言一起交流讨论。