【FPGA基础篇】Xilinx FIFO详细解析


FIFO作为FPGA岗位求职过程中最常被问到的基础知识点,也是项目中最常被使用到的IP,其意义是非常重要的。本文基于对FIFO Generator的Xilinx官方手册的阅读与总结,汇总主要知识点如下:

类型

FIFO的类型区分主要根据FIFO在实现时利用的是芯片中的哪些资源,其分类主要有以下四种:

  1. shift register FIFO:通过寄存器来实现的,这种类型的FIFO最好少用,因为我们都知道FF资源在FPGA是非常珍贵的。

  2. built-in FIFO:这种类型的FIFO只有7系列之后(包括UltraScale)才有。笔者的理解是一种集成的FIFO硬核

  3. Block Ram FIFO:通过块RAM的资源配置形成的FIFO,其本质是Block RAM+一些外设电路。

  4. Distributed Ram FIFO:通过分布式RAM配置形成的FIFO,与BRAM类似,只是RAM的类型不一样。

Reset

​ shift register FIFO和built-in FIFO的复位信号是不可选的,即一定存在的。对于shift register FIFO和7系列的built-in FIFO,Xilinx只提供了异步复位;而对于UltraScale,复位是同步复位信号,但提供了w_rst_busy和rd_rst_busy输出信号表示FIFO是否已经复位完毕。

​ Block RAM FIFO 和 Distributed RAM FIFO的复位信号是可选的。对于共用时钟的FIFO,选择同步复位和异步复位的区别主要在于是否复位信号是否和共用时钟同步;当选择异步复位时,可以Enable Safety Circuit,顾名思义就是让电路更安全,即通过复位完成信号wr_rst_busy和rd_rst_busy来表示是否FIFO已经复位完成。对于独立时钟的FIFO,即读和写的时钟分开,Enable Reset Synchronication时只有一个rst(实际上这个复位是异步复位),不使能时有两个时钟域的rst,分别为wr_rst和rd_rst,而这两个复位信号实际上是同步于各自的时钟的,同时也能Enable Safety Circuit,是不是觉得好像整错了,其实Xilinx的官方文档就是这么说明的,如果还是觉得有问题,实践一下……

​ 其实上边的如果不理解,我自己总结了一下:看复位是同步复位还是异步复位,只需要看信号的名字,对于srst(Synchronized Reset)和wr_rst/rd_rst,这几个rst是同步复位,一个同步于共用时钟,其余两个同步于各自的时钟域;对于rst,则认为是异步复位。

写操作

​ 写数据只有在din输入同时wr_ack被断言时才写入到FIFO中。这里wr_ack是对数据接收的应答,断言时表示接收成功。当然也可以不使能wr_ack,这样子其实也没什么大问题,只是wr_ack的存在会让数据的写入更加安全。

​ 需要注意的一个关键点是,FIFO被写满时,即使再输入数据,写入请求还是会被忽略的,FIFO中的数据保持不变。

满标志

​ 满标志有full和almost_full两种。full表示不能写数据了,almost_full则表示只能再写入一个数据。当full被断言时,写请求会被忽略掉,同时overflow溢出标志被断言。

​ 【注】built-in FIFO不支持almost_full标志。

写操作时序分析

image-20210906083921164

​ 上图是从datasheet中copy的一个典型的写操作时序图。

  1. 当wr_en被使能为1时,表示写操作开始,并在时钟上升沿开始写入。此时full为低电平表示FIFO未满,可以写入。

  2. 当数据写入成功时,wr_ack被断言,即拉高表示数据写入成功***(此时我们还是得注意写入成功那个时刻,wr_ack实际上还是低电平,真正能检测到高电平的还是得在下个时钟沿)***

  3. 当FIFO只能再支持写一个数据时,almost_full被断言***(应该注意到,这些标志信号都是有一个cycle的延迟的,即实际上当我们检测到amost_full拉高时,最后一个数据也在同时刻写入FIFO,所以FIFO在这个时刻已经满了)***

  4. 当FIFO的almost_full拉高时,再次写入一个数据,则full也被断言。

  5. 当FIFO的full被断言时,再写入数据,wr_ack被拉低,同时overflow溢出信号被断言。

  6. 在FIFO被写满时,如果开始读操作,full信号会被拉低,此时数据又可以再次写入FIFO。

读操作

​ 当read使能且FIFO不是空的时候,数据开始从FIFO读出。同时valid信号被断言表示数据有效。读操作只有在FIFO有数据时才能成功,当FIFO是空的时,此时读信号会被忽略,同时underflow会被断言表示下溢,数据输出不会发生任何变化。

空信号

​ almost_empty表示FIFO即将被读空,只剩下一个数据。empty表示FIFO已经被读空,只有当FIFO再次被写入数据时,empty才会再次被拉低。almost_empty和empty都同步于rd_clk时钟域。当FIFO被读空时,再进行读写则underflow信号会被断言。

【注】built-in FIFO不支持almost_empty信号

当读操作和写操作同时发生在empty被断言时,写操作可以正常执行,读操作会被忽略,在下个时钟,empty和underflow被拉低后,才能继续进行读操作。

读操作时序分析

​ FIFO的读操作有两种模式,Standard Read和First-Word Fall-Through。简单地说,标志读模式下,数据会在请求的下一个时钟给出,而FWFT则在请求的同一个时钟给出。

Standard Read

image-20210906111828367

​ 标准读模式下,当empty没有被断言时,表示FIFO中有数据可读。此时使能rd_en,在下个时钟上升沿即开始读取数据,同时valid被拉高表示读取数据有效。当读到FIFO只剩一个数据时,almost_empty被断言;当读完所有数据时,empty被断言。此时如果再进行读取,则underflow被断言,表示下溢,同时valid拉低表示数据无效。

First-Word Fall-Through

image-20210906124042278

​ 在FWFT模式下,数据总是能被提前获取的,可以这么理解,在我们还不需要读取FIFO的数据的时候,其实我们不在乎FIFO中第一个数据是什么,但FWFT模式下,第一个数据提前进入了准备发送的状态,即我们可以dout处看到即将输出的数据是什么,而当我们开始读的时候,下一个数据就进入准备状态,也被我们提前获取了。

​ 比较两种模式下的flag信号。almost_empty是在FIFO中还剩下一个数据是被断言,empty是在把最后一个数据读取完了之后被断言,在SR模式下,empty是在把最后一个数据读出来的同时拉高,在FWFT模式下,empty是在获取最后一个数据后才拉高,因为我们是提前知道最后一个数据,此时的数据还是停留在FIFO中,只有把它读出来了,FIFO才是空的,empty才会被断言。inderflow都在读空之后还试图继续读取的时候才会拉高,所以underflow总是比empty慢一拍。valid总是和数据是同步的。

同时读写时序分析

​ 下图是标准读写模式下的时序图,原理前面已经讲过了,这里提供给大家验证自己的理解是否正确。

image-20210906125634078

握手信号

FIFO的握手信号主要有wr_ack、valid、underflow和overflow。

wr_ack:write acknowledge,即写操作应答。当写入成功时,wr_ack断言回应表示写入数据成功。

valid:valid信号表示数据有效的意思,它的时序在SR和FWFT模式中均表现为数据有效,但和rd_en的时序相位有区别。

underflow:下溢信号。当FIFO为空且继续读取数据时,则会出现下溢。

overflow:上溢信号。当FIFO写满后还继续写入数据时,则出现上溢。

Programmable Flags

​ 用户自定义阈值标志位。分为Programmable full和Programmable empty两种。前者表示FIFO写满状态的阈值,后者表示FIFO读空状态的阈值。简单地说,就是不管FIFO的深度,你想让它存入多少数据时就认为已经装满了,或者你让他认为还剩多少数据就已经算读空,可以自行设置。

​ 对于prog_full和prog_empty,可以设置为单阈值(single)多阈值(multiple),设置单阈值时,大于或等于(小于或等于)阈值就会断言满(空)信号。而设置多阈值,就可以出现滞留效果,以prog_full为例,当设置assert threshold为100,negate threshold为80时,当FIFO中数据没到100时,只有大于100后才会断言满,而此时断言满之后被读到100以下后,FIFO仍然认为是满的,只有持续读到小于80,才会认为未满,即negate。

​ 其次,对于阈值的设定,既可以设置为常数,也可以设置为一个自定义输入。当定义为自定义输入的时候,FIFO的满空状态就变成自适应的了,随时可以通过阈值输入进行修改。

Data Counts

​ Data Count只能用于共用时钟。对于读写时钟分开的情况,则分别用Read Data Count和Write Data Count来表示FIFO中的数据,异步FIFO中虽然是同一个FIFO,但rd_data_cnt和wr_data_cnt中的数值不一定是相等的,为什么这么设置,因为安全!具体说明:

Read Data Count 和 Write Data Count 都是采用的保守估计的方式。什么是保守估计,意思就是假设FIFO中至少有8个数据可读,但FIFO只跟你说我这里只有8个数据可读,那么你就会认为只能读8个数据,实际上可能能读9个或者10个,这么做的原因时为了保证FIFO读操作的安全性,后面我们分析异步FIFO的时候就能深刻理解这么做的精妙之处,这就是保守估计。类似的,当FIFO中还可以写至少8个数据时,FIFO会说只能写8个数据了,这样子你也只会写8个数据,实际上FIFO可以写9-10个左右。datasheet中的原文表述为Read data count (rd_data_count) pessimistically reports the number of words available for reading. The count is guaranteed to never over-report the number of words available in the FIFO (although it may temporarily under-report the number of words available) to ensure that the user design never underflows the FIFO.

那么,FIFO是怎么实现这种保守估计的呢?结合下图一起分析,我们以Write Data Count为例,由于wr_data_cnt同步于wr_clk,wr_en也同步于wr_clk,所以wr_en使能后,每写入一个data,都可以在下个时钟沿反馈到wr_data_cnt上,写入一个数据,wr_data_cnt就加一;相应的,读出一个数据,wr_data_cnt也需要减一,才能正确反应FIFO中的数据量。但对于rd_en,由于rd_en是同步于rd_clk的,所以rd_en的使能需要通过一系列的时钟同步到wr_clk中才能反映到wr_data_cnt上,通常采用的方式是格雷码和打拍子的方式,如下图所示,可以看到需要将Read Counter通过格雷码转换(格雷码转换可以消除很多中间不定态,大大增强了稳定性),再进行跨时钟域传输,这里跨时钟域传输一般可以选择打拍子的方式,由于已经转化成了格雷码,此时多bit传输就可以变成单bit传输,因为相邻state只有其中一个bit发生了变化,单bit跨时钟域传输就可以采用打拍子的方式进行时钟域转换,这样子就造成了的结果就是传送到wr_clk时钟域时的Read Counter的数值是过去的Read Counter,比最新的Read Counter要小,因此Write Counter-Read Counter偏大,从而认为FIFO中有更多的数据存储(实际上并没有那么多数据),因此wr_data_cnt输出的信息就是FIFO中最多有多少个数据,至少还能写入多少个数据,而我们能知道的就是我们只要写入的数据不超过它的最小值,就能保证FIFO永远不会写满,这样子安全性就提高了。

image-20210906153021708

Non-symmetric Aspect Ratios

​ 非对称比例用于设置写数据和读数据的位宽不同的情况。用例子说明如下:

image-20210906151526916

上图表示1:4的Aspect Ratio,即写一次只能写一个数据,但读可以一次读四个数据,写的时候是从高位写到低位。需要注意的一个点是,对于读写非对称的情况,如上所述,假设读空了FIFO之后,empty被断言,则此时写入一个数据并不能拉低empty,必须写入至少四个才能将empty拉低,即FIFO中遵循位宽最大法则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页
评论 2

打赏作者

mrVillain

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值