FIFO是FPGA内部一种常用的资源,可以通过FPGA厂家的的IP生成工具生成相应的FIFO。FIFO可分为同步FIFO和异步FIFO,其区别主要是,读写的时钟是否为同一时钟,如使用一个时钟则为同步FIFO,读写时钟分开则为异步FIFO。一般来说,较大的FIFO可以选择使用内部BLOCK RAM资源,而小的FIFO可以使用寄存器资源例化使用。
一般来说,FIFO的主要信号包括:
信号 |
数据信号 |
读信号 |
rd_en |
读数据 |
dout |
读空信号 |
empty |
写信号 |
wr_en |
写数据 |
din |
写满信号 |
full |
实际使用中,可编程满的信号(XILINX 的FIFO)较为常用,ALTERA的FIFO中,可以通过写深度(即写入多少的数据值)来构造其可编程满信号。通过配置threshold(门限)的值可以设定可编程满起效时的FIFO深度。
上图所示为FIFO的模型,可以看做一个漏桶模型,其中输入、输出、满信号、空信号、可编程满等信号如图所示,一目了然。 其中threshold信号可以看做水位线,通过此信号可以设置可编程满信号。FIFO的其他的信号也大都与其深度相关,如有特殊需求,可通过厂商提供的IP生成工具的图形界面进行选择使用。
FIFO的使用场景有多种,其中主要如下所示:
(1)数据的缓冲,如模型图所示,如果数据的写入速率高,但间隔大,且会有突发;读出速率小,但相对均匀。则通过设置相应深度的FIFO,可以起到数据暂存的功能,且能够使后续处理流程平滑,避免前级突发时,后级来不及处理而丢弃数据。
(2)时钟域的隔离。对于不同时钟域的数据传递,则数据可以通过FIFO进行隔离,避免跨时钟域的数据传输带来的设计与约束上的复杂度。
FIFO设计中有两个需要注意事项,首先,不能溢出,即满后还要写导致溢出,对于数据帧的操作来说,每次写入一个数据帧时,如果每写一个宽度(FIFO的宽度)的数据,都要检查满信号,则处理较为复杂,如果在写之前没满,写过程不检查,则就容易导致溢出。因此可编程满的设定尤为必要,通过设置可编程满的水位线,保证能够存储一个数据帧,这样写之前检查可编程满即可。
其次,另一更容易出错的问题,就是空信号。对于FIFO来说,在读过程中出现空信号,则其没有代表该值没有被读出,对于读信号来说,如设定读出一定长度的值,只在一开始检测非空,如状态机的触发信号,容易出现过程中间也为空的信号,会导致某些数据未读出,特别是写速满而读速快的场景下。 因此rden与!empty信号要一起有效才算将数据读出。
空信号处理相对容易出错,懒人自有笨方法,下面介绍一种应用于数据帧处理的FIFO使用方式,只需在读开始检测空信号即可,可以简化其处理读数据的流程:
其处理结构如上图所示,数据缓存以大FIFO(BLOCK RAM实现)为主,而每存储完毕一个数据帧向小FIFO(寄存器实现)内写入值。当小FIFO标示非空时,则标示大FIFO中已存储一个整帧。则下一级模块可以只需检测小FIFO非空时,从而读出一个整帧,过程中大FIFO一直未非空,可以不用处理非空信号,从而简化设计和验证的流程。
FIFO在FPGA设计中除了上篇所介绍的功能之外, 还有以下作为以下功能使用:
(1)内存申请
在软件设计中,使用malloc()和free()等函数可以用于内存的申请和释放。特别是在有操作系统的环境下,可以保证系统的内存空间被动态的分配和使用,非常的方便。如果在FPGA内部实现此动态的内存分配和申请,相对来说较为复杂,例如某些需要外部数据存储且需动态改变的应用需求下,需要对FPGA外部DDR(或SRAM等)的存储空间,进行动态的分配和释放。通过使用FIFO作为内存分配器,虽然比不上软件的灵活和方便,但是使用也较为简便。
举例说明假设外部存储空间为8Mbyte,可将其划分为8192个1Kbyte空间。并将数值0-8191存储FIFO中,FIFO内部存储所标示可用的内存空间。如下图所示。
首先,进行内存的初始化,即将0-8191写入FIFO中。
如需申请内存后,从FIFO中读取值A,然后根据A的标示,写入A所指示的外部存储区(DDR)中相应的位置,即申请{A,10’h0_00} ->{A,10’h3_FF}的空间区域。
如释放内存后,即可向FIFO中写入相应的值。即可保证下次该空间能够被设计使用。
在此种设计中,FIFO承担了内存分配和释放器的角色。此时只能申请或释放最小单元倍数的内存空间,如本例所示:为1Kbit。如FIFO读空,则代表申请内存失败,需要等待其他块内存释放后再写入FIFO中,才能再次申请。
(2)串并转换
对于串并转换,可能对于FPGA工程师来说,非常常见,但是如果有专门的IP实现此功能,可简化设计,减少出错及验证的工作量。例如:对于外部输入的需要进行串并转换的信号,并进行存储的信号,如设计进行串并转换在存储等操作,设计,可以直接通过例化读写位宽不一致的FIFO,例如1入8出的FIFO,可直接将外部输入信号直接转换成8BIT信号并进行存储后,供后续处理使用(其他的)。
(3)业务优先级划分
通过FIFO设置不同水位线,可以划分不同的业务优先级,保证高业务优先级数据流在带宽受限时,优先通过,而低业务优先级只能在满足高优先级需求后有多余的带宽时才能通过。并且可以划分多个优先级,满足多种业务的需求。设计将在以后篇幅中详述。
(4)固定带宽设定
通过对FIFO接口的读出使能,能够保证实现固定带宽的输出,例如FIFO读接口为32bit,而读时钟为50Mhz,则输出为1.6Gbit/S。如实现固定带宽的输出(如1Gbit/S),有两种方式,一种可以通过降低时钟频率到31.25Mhz。另一种方式,可通过读信号中间插入等待周期,如果读出长度为N的数据所需时钟周期为M,则需等待(3M/5)的周期,从而降低至1Gbit/S的处理能力,这在某些需要进行流量限制的业务方式中使用。
对于FIFO来说,作为FPGA内部资源的一个常用器件,最常见应用于异步时钟域划分和缓冲数据,但不仅限于此,简化设计、减少耦合、输入输出接口固定,便于仿真和验证,都是使用FIFO带来的设计上的益处。
作者: 阿昏豆, 来源:面包板社区
链接:https://www.mianbaoban.cn/blog/787532-366580.html
点击 “阅读原文”申请!