一. IP概述
以下翻译自官网此IP的概述。
产品描述:
LogiCORE™IP FIFO生成器内核生成经过充分验证的先进先出(FIFO)内存队列,非常适合需要按顺序存储和检索数据的应用。
该内核为所有FIFO配置提供了优化的解决方案,并在利用最少资源的同时提供了最高性能(高达500 MHz)。通过Vivado®Design Suite提供的结构可以由用户自定义,包括宽度,深度,状态标志,存储器类型以及写/读端口的宽高比。
主要功能和优势:
-
FIFO深度高达4,194,304字 -
FIFO数据宽度从1到1024位(对于本机FIFO配置),最大4096位(对于AXI FIFO配置) -
非对称纵横比(读写端口比率范围为1:8至8:1) -
支持独立或通用时钟域 -
可选的存储器类型(块RAM,分布式RAM,移位寄存器或内置FIFO) -
本机或AXI接口(AXI4,AXI4-Lite或AXI4-Stream) -
同步或异步重置选项 -
支持分组模式 -
支持某些配置的纠错(ECC)和注入功能 -
支持首字直通(FWFT) -
支持用于Block RAM和基于内置FIFO原语的实现的Embedded Register选项 -
支持–空/满,几乎空/满和可编程的空/满信号
二. IP产品手册
可下载PG057 – FIFO Generator v13.2 Product Guide (v13.2)。
三. IP框图与信号端口
XILINX的FIFO Generator IP提供了三种接口的FIFO,分别是Native,AXI Memory Mapped 以及 AXI Sream,我个人只用过Native即普通FIFO,所以下文只介绍这一种FIFO类型。
3.1 IP模块框图(普通FIFO)
3.2 IP信号列表
信号名 | 位宽 | 必须/可选 | 说明 |
---|---|---|---|
wr_clk | 1 | 必须 | 写时钟,提供给写端口使用的时钟 |
wr_en | 1 | 必须 | 写使能,高有效, 在每个写时钟上升沿被捕捉,捕捉一次写入一次数据 |
full | 1 | 必须 | 满信号,高有效, 在FIFO内部写数据个数 = FIFO写深度的第一个时钟上升沿置高, 在FIFO内部写数据个数 < FIFO写深度的第一个时钟上升沿拉低。 在full有效时,FIFO会关闭写功能,如果此时wr_en有效,full会置高overflow即输出满溢信号,FIFO中的数据没有变化,往满FIFO中写不是破坏性的。 |
din | 1, 2, … , 1024 | 必须 | 写入的数据,当wr_en高被捕捉时,din被写入到FIFO中 |
almost_full | 1 | 可选 | 快满信号,高有效, 在FIFO内部写数据个数 = FIFO深度 – 1的第一个写时钟上升沿置高, 在FIFO内部写数据个数 < FIFO深度 – 1的第一个写时钟上升沿拉低 |
prog_full | 1 | 可选 | 设定满信号,高有效, 参考4.3.3 Programmable Flags —— 可编程标志 |
wr_rst | 1 | 可选 | 写复位,高有效, 在写复位信号有效后的第一个写时钟上升沿,FIFO被清空,并不再响应写使能, 直到写复位信号失效后下一个写时钟上升沿(包括)开始再去响应写使能 |
— | — | — | — |
rd_clk | 1 | 可选 | 读时钟, 对于同步FIFO,FIFO读写时钟合一 对于异步FIFO,FIFO会有分开的写时钟和读时钟 |
rd_en | 1 | 必须 | 读使能,高有效,在每个读时钟上升沿被捕捉,捕捉一次读出一次数据 |
empty | 1 | 必须 | 空信号,高有效, 在FIFO内部读数据个数 = 0的第一个读时钟上升沿置高, 直到FIFO内部读数据个数 > 0的第一个读时钟上升沿拉低。 在empty有效时,FIFO会关闭读功能,如果此时rd_en有效,FIFO会置高underflow即输出空溢信号,FIFO内部的数据没有变化,读取空FIFO不是破坏性的。 |
dout | 可设定位宽 = din位宽 *(1/8,1/4,1/2,1,2,4,8) |
必须 | 读数据,读数据位宽 * 读深度 = FIFO容量 = 写数据位宽 * 写深度, 对于FWFT FIFO,dout预先有效,在读使能被捕捉的同时更新下一个读数据, 对于标准FIFO,在读使能被捕捉的时钟过后的第二个时钟上升沿,dout才是读出的数据 |
almost_empty | 1 | 可选 | 快空信号,高有效, 在FIFO内部读数据个数 = 1的第一个读时钟上升沿置高, 在FIFO内部读数据个数 > 1的第一个读时钟上升沿被拉低 |
prog_empty | 1 | 可选 | 设定空信号,高有效, 参考4.3.3 Programmable Flags —— 可编程标志 |
其它信号很少使用,这里不做介绍。
3.3 设计注意事项
3.3.1 rst —— 复位
在FPGA配置完成后,读写操作开始之前,FIFO必须被复位,有同步/异步两种复位可用。
3.3.2 clk —— 时钟
Xilinx建议,不要通过改变clk来改变FIFO的读写行为,而应该去控制读写使能信号,当时钟未稳定时,不要去操作FIFO,当时钟稳定后,先对FIFO进行复位,然后再去操作FIFO。
3.3.2 wr_en 和 rd_en
虽然写满和读空行为都不是破坏性的,但仍然强烈建议不要在FIFO满时置高写使能,不要在FIFO空时置高读使能。也就是wr_en的用户逻辑需要考虑full信号,rd_en的用户逻辑需要考虑empty信号。
四. IP配置
4.1 Basic
4.1.1 FIFO接口类型
最常用的就是Native接口类型的FIFO,即普通FIFO。
4.1.2 FIFO实现
FIFO实现有两个方面的含义:
① FIFO时钟,公共时钟就是同步FIFO,此时读写操作共用一个时钟;独立时钟就是异步FIFO,此时写操作用写时钟,读操作用读时钟。
当选择异步FIFO时,会出现Synchronization stages —— 同步级数的选项,此选项的意思是,当FIFO中写入数据后,empty信号并不会立刻拉低,因为写入数据是基于写时钟的,而empty信号是基于读时钟的,如果Synchronization stages设定为2,则意味着empty会在FIFO中写入数据成功后的2个读时钟周期后拉低。选3就是3个读时钟周期后,以此类推。
最小的Synchronization stages值通常和具体的器件型号有关,器件频率越快,则此选项最小选值越大,例如200MHz器件最低可以选2,而400MHz器件最低只能选4。这也告诉我们,速度越快,跨时钟域的同步越可能不稳定,需要更多级数的D触发器。
总的来说,Synchronization stages貌似是一个不那么关键的参数,通常不需要修改。
② 存储类型,有以下四种:
1)Block RAM,块RAM,简称BRAM,是在FPGA内部嵌入的硬核存储器,BRAM数量是衡量FPGA性能的重要指标,采用BRAM做FIFO是性能最优的,但一个BRAM是18Kb,即使FIFO容量很小,也最少使用一个BRAM,这就造成了存储容量的浪费。
2)Distributed RAM,分布式RAM,是使用FPGA内部的LUT即查找表资源搭建的FIFO,它的性能不及BRAM FIFO,但好处是它容量可以很灵活的配置,需要多少容量就用多少LUT去搭建,不会存在容量浪费的情况。
3)Shift Register,移位寄存器,此存储类型只支持公共时钟,没用过。
4)Built-in FIFO,内置FIFO,没用过。
说明,BRAM和分布式RAM是创建FIFO最常选用的存储类型,一般来说,FIFO容量超过1024个字节就考虑使用BRAM,没超过1024字节选择分布式RAM。当然,如果芯片BRAM资源很富余的话,全部采用BRAM也是可以的。后两种基本用不到。
4.1.3 FIFO支持的功能
不同的芯片型号与不同的实现形式的FIFO会支持不同的功能,不必去记忆这些,在Basic界面会将对应实现的功能展示出来。功能含义如下:
(1)non-symmetric aspect ratios(different read and write data widths),非对称纵横比 即 不同的写入与读取数据位宽,支持此功能允许读取数据位宽与写入数据位宽不同,具体的读取位宽可为输入位宽的1/8,1/4,1/2,1倍,2倍,4倍 或 8倍,当然位宽必须是整数,例如写入数据位宽=3时,读取数据位宽就只能取3,6,12,24。
(2)First-Word Fall-Through,首字直通,简称FWFT。FIFO按是否支持此功能分别两种:
1)Standard FIFO,标准FIFO,写入在wr_en有效的第一个wr_clk时钟上升沿完成。读数据时,rd_en有效,rdata端口经过几个读时钟周期的延迟才装载完成最外侧读数据,具体延迟周期数跟FIFO设置有关,下面会讲到。
2)FWFT FIFO,首字直通FIFO,写入和标准FIFO完全相同,但FWFT FIFO会将读数据预先就装载到rdata端口上,rd_en并不是控制FIFO去输出读数据,而是控制FIFO去更新下一个读数据到rdata端口上。
通常来说,FWFT FIFO是更易使用的,读取无延迟的优势使得读时序很容易控制。
(3) Uses Bulit-in FIFO primitives,使用内置FIFO原语。
(4)ECC support,ECC是Error Injection and Correction的简称,意思是错误注入和纠正。
(5)Dynamic Error Injection,动态错误注入。
4.1.4 Using Block RAM FIFOs Versus Built-in FIFOs,BRAM FIFO与 内置FIFO使用对比
参考手册16页:
内置FIFO解决方案旨在利用内置FIFO宏内部的逻辑。对于快满,快空以及其它几个特性,内置FIFO没有实现,因为它们不是宏的原生特性,需要额外逻辑来实现。
手册中写道:基准测试表明,采用内置FIFO + 外部逻辑实现宏的非原生特性 的解决方案与直接使用BRAM FIFO相比,逻辑资源消耗小的优势在减弱。特别是对于大容量的FIFO,BRAM更具优势,所以我们强烈建议,当需要FIFO具备内置FIFO的非原生特性时,采用BRAM的实现形式而不是内置FIFO。
4.2 Native Ports
4.2.1 Read Mode —— 读取模式
选择标准FIFO与FWFT FIFO,推荐总是使用FWFT FIFO。
4.2.2 Data Port Parameters —— 数据端口参数
选择写位宽与写深度,以及读位宽与读深度。只有支持读写位宽不一致功能的FIFO,读位宽才是可选的,不支持此功能的话,读位宽必须等于写位宽。
4.2.3 ECC,Output Register and Power Gating Options —— 错误注入与纠正,输出寄存器 和 功率控制选项
ECC:只有BRAM FIFO 和 内置FIFO支持ECC功能,
Output Register:输出寄存器,勾选此选项意思是在输出端口再插入寄存器,插入寄存器会增加输出延迟,这点在设计读取时序时需要特别注意。共有三种输出寄存器可以选择:
1)Embedded Registers,嵌入式寄存器
2)Fabric Registers,光纤寄存器
3)Embedded Reg And Fabric Reg,嵌入式寄存器 + 光纤寄存器
Power Gating:功率控制,只有UltraScale类型芯片的built-in FIFO才支持功率控制功能。当勾选Dynamic Power Gating(动态功率控制)时,输入端口会增加一个sleep信号,当sleep信号高有效时,FIFO会进入省电模式,省电模式禁止写和读,此时wr_en与rd_en都应该为低,FIFO保留内部的数据不改变,直到sleep低电平失效。
Initialization:初始化,其实就是复位,复位总是高电平有效,无法修改FIFO的复位电平。同步FIFO支持同步复位和异步复位,而异步FIFO只支持异步复位。
异步复位可控制full信号在复位期间的值。可配置复位期间读数据端口的值,不配置的话读数据端口默认为0。
Read Latency:读延迟,只有标准FIFO才有读延迟,且会受Output Register的配置影响;FWFT FIFO读延迟始终为0。
4.3 Status Flags
4.3.1 Optional Flag —— 可选标志
Almost Full Flag:快满信号,高有效,在FIFO内部写数据个数 >= FIFO深度 – 1之后的第一个写时钟上升沿置高,直到FIFO内部写数据个数 < FIFO深度 – 1后的第一个写时钟上升沿拉低。
Almost Empty Flag:快空信号,高有效,在FIFO内部读数据个数 <= 1之后的第一个读时钟上升沿置高,直到FIFO内部读数据个数 > 1后的第一个读时钟上升沿拉低。
4.3.2 Handshaking Options —— 握手选项
Write Port Handshaking:写端口握手
Write Ackongledge:写应答,在每次写完成后的第一个时钟上升沿置高/拉低(有效电平可配置),一次成功写入对应一个写时钟周期的写应答。
Overflow:满溢出,在FIFO满之后仍有wr_en信号使能的第一个时钟上升沿置高/拉低(有效电平可配置),一次满溢出有效电平持续一个写时钟。
Read Port Handshaking:读端口握手
Valid Ackongledge:数据有效应答,在每次读完成后的第一个时钟上升沿置高/拉低(有效电平可配置),一次成功读取对应一个读时钟周期的数据有效应答。
Underflow:空溢出,在FIFO空之后仍有rd_en信号使能的第一个时钟上升沿置高/拉低(有效电平可配置),一次空溢出有效电平持续一个读时钟。
4.3.3 Programmable Flags —— 可编程标志
Programmable Full Type:可编程满类型,有五个选项可选:
No Programmable Full Threshold:无可编程满临界点
Single Programmable Full Threshold Constant:单个可编程满临界点常量
Multiple Programmable Full Threshold Constant:双向可编程满临界点常量
Single Programmable Full Threshold Input Port:单个可编程满临界点输入端口
Multiple Programmable Full Threshold Input Port:双向可编程满临界点输入端口
下面是Programmable Full Type:可编程空类型,同理,不再赘述。
可编程满标志的产生逻辑:
当FIFO中的写数据 >= Full Therhold Assert Value中规定的数据量(取值范围:13 ~ 写入深度-1)时,可编程满置高;
当FIFO中的写数据 <= Full Therhold Negate Value中规定的数据量(取值范围:12 ~ Full Therhold Assert Value中的值-1)时,可编程满拉低。
可编程空标志的产生逻辑:
同理,取值范围需要注意:
Empty Therhold Assert Value的范围:4 ~ 读取深度-3;
Empty Therhold Negate Value的范围:Empty Therhold Assert Value+1 ~ 读取深度-2。
4.4 Data Counts
同步FIFO对应 Data Count,异步FIFO对应 Write Data Count 以及 Read Data Count。
Data count的位宽是可选的,当选择标准FIFO时,范围是1 ~ log2(数据深度);当选择FWFT FIFO时,范围是1 ~ log2(数据深度)+1。举例说明:
如果数据深度 = 16,Data count位宽选择4,那显然Data count的取值范围是0~15,这意味着计数最多记到15,当FIFO满时计数应该是16,但这最后一个数不会被记。如果此时Data count位宽选为3,那么Data count可表示的数是0~7,那么FIFO中数据量 = 2,Data count才会加1,数据量 = 4,Data count才会加2,也就是说Data count被低位截断了;如果此时Data count位宽选为1,那所有低位都被截断了,意味这FIFO中数据量 = 8,Data count才会加1。
如果勾选More Accurate Data Counts(精确计数),位宽变为1~5可选,5位宽最大可表示31,前面选的FIFO深度是16,意味着FIFO满时的最后计数16也会被记入。还有一点,选择FWFT FIFO时,FIFO的实际深度会大于设定的深度,如上面Native Ports界面的图所示,设定深度16而FWFT FIFO的实际深度是17,精确计数可以计数到实际深度17,这就是精确计数的概念。
当选择同步FWFT FIFO时,默认就应用了精确计数,而无需手动选择。
还需要说明的是,计数是有延迟的,不是FIFO中数据量一改变,计数值马上改变的,这个延时的周期数和器件有关。
另外,计数通常只适用于调试,调试结束后就不在需要了,应该计数会增加额外的资料消耗,而且计数到某一个值的功能可以由可编程满/空信号来代替,显然单比特的信号更易接收和使用。
4.5 Summary
五. IP仿真
仿真框图:
对于以下关心的问题分别仿真:
5.1 复位是否会清空FIFO,清空需要多长时间?
仿真显示:对于同步,复位置高后,会在下一个时钟上升沿清空FIFO;对于异步复位则是立即清空FIFO,无需等待时钟上升沿。复位还会同时置高full和empty。
5.2 复位结束后FIFO能马上进行写和读吗?不能的话需要间隔多少时钟周期?
上图显示,复位结束后即rst=0后,full信号还会一直为高持续多个周期,此时FIFO不能写也不能读(没有数据),这个持续周期数对于16深度的FIFO是13个写时钟周期,对于深度32的FIFO还是13个写时钟周期,对于深度1024的FIFO是14个写时钟周期,可见FIFO从复位结束到恢复读写功能需要大约13个写时钟周期。
5.3 FWFT FIFO的实际深度会大于设定深度,那么full和almost_full信号是当数据量达到设定深度置高,还是达到实际深度才置高?
对于设定深度16,实际深度17的FWFT FIFO,almost_full在写入16个数据后置高,full在写入17个数据后置高,所以满信号是以实际深度为准的。
5.4 FIFO写入一个数据后,多久empty会失效?
仿真结果显示:写入第一个数据后,经过8个读时钟周期,empty才拉低。
5.5 可编程满和可编程空的产生逻辑是什么?
确实如4.3.3 Programmable Flags —— 可编程标志节描述的一致,不好展示,读者可自行仿真验证。
5.6 读写位宽不同时的FIFO数据输出顺序是怎样的
部分类型的FIFO支持读位宽不等于写位宽,写位宽与读位宽的比率可以是:
1:8 | 1:4 | 1:2 | 1:1 | 2:1 | 4:1 | 8:1 |
---|
PG057 – FIFO Generator v13.2 Product Guide (v13.2)的114~116页对FIFO读写位宽不同时的数据顺序有清晰地描述,且可能和我们普遍的认知不同。
情形1:写位宽 < 读位宽,如下例,写位宽为2,读位宽为8
如上图可知,先写入01,然后00,然后11,最后10,最终读到的8位二进制是01_00_11_10,即先写入的是MSB,最后写入的是LSB。
情形2:写位宽 > 读位宽,如下例,写位宽为8,读位宽为2
由上图可知,写入8位二进制11_00_01_11,然后依次读出来的数据是11,00,01,11,可见先读出来的是MSB,最后读出来的是LSB,即先读出高位,后读出低位。
这可能和我们认知的FIFO从左向右写数据不同,Xilinx的FIFO可以理解为是从右往左写数据,下图可帮助理解FIFO的写入读取过程:
简单总结,将FIFO理解为一个从右往左输送数据的先入先出队列就能明白不同读写位宽下FIFO输出数据的顺序。
仿真一:写入位宽为4,读取位宽为16的FIFO数据输出情况,写入的顺序是0, 1, 2, 4, 5, 6, 7,然后读出的数据是0123, 4567,可见先写入的在MSB。
仿真二:写入位宽为16,读取位宽为4的FIFO数据输出情况,写入的顺序是0123, 4567,然后读出的数据是0, 1, 2, 4, 5, 6, 7,可见写入数据的MSB先被读出。
六. FIFO使用建议
1.总是使用FWFT FIFO,无延迟的读便于控制
2.资源足够时总是使用BRAM FIFO;资源紧张时,小型FIFO可使用分布式FIFO
3.FIFO在使用之前必须复位,复位时置高full,禁止一切读写操作
4.在产生wr_en信号时,必须和~full信号进行与操作,且必须是组合逻辑,因为时序逻辑会延迟一个写时钟才起作用,可能造成写满的情况发生,示例代码如下:
reg wr_en_temp;
always @(posedge clk) begin
if (~rstn)
wr_en_temp <= 1'b0;
else if (data_valid)
wr_en_temp <= 1'b1;
else
wr_en_temp <= 1'b0;
assign wr_en = ~full && wr_en_temp;
5.与产生wr_en同理,产生rd_en信号时,必须和~empty信号进行与操作,且必须是组合逻辑,防止读空的情况发生,示例代码如下:
reg rd_en_temp;
always @(posedge clk) begin
if (~rstn)
rd_en_temp <= 1'b0;
else if (ready)
rd_en_temp <= 1'b1;
else
rd_en_temp <= 1'b0;
assign rd_en = ~empty && rd_en_temp;
七. 工程分享
Xilinx IP解析——FIFO Generator V13.2 Vivado 2021.2工程。
欢迎大家关注我的公众号:徐晓康的博客,回复以下代码获取。
3869
建议复制过去不会码错字!
如果本文对你有所帮助,欢迎点赞、转发、收藏、评论让更多人看到,赞赏支持就更好了。
如果对文章内容有疑问,请务必清楚描述问题,留言评论或私信告知我,我看到会回复。
徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。