【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中遵循位宽最大法则。

### 回答1: Xilinx 7系列FPGA是一种常用的FPGA系列之一,适合初学者入门。以下是关于基于Xilinx 7系列FPGA基础入门的回答: 首先,了解Xilinx 7系列FPGA的核心特点是很重要的。这个系列的FPGA具有良好的性能、可靠性和灵活性,可以适用于各种不同的应用。同时,它还有一种称为“逻辑单元”的资源,可以实现各种数字逻辑功能。 为了开始基于Xilinx 7系列FPGA的入门学习,你需要熟悉使用Xilinx工具套件。其中最重要的是Vivado设计套件,它为FPGA的开发和实现提供了一个全面的解决方案。你可以通过下载并安装Vivado软件来开始你的学习。 一旦你熟悉了Vivado软件,你可以创建一个新的工程,并选择适合你的FPGA型号。然后,你可以使用Vivado设计套件的图形界面来设计你的电路。你可以使用逻辑模块、FIFO、RAM等等来构建你的电路。此外,Vivado还提供了一些示例设计,你可以借鉴和修改这些设计,以适应你的应用需求。 在设计完成后,你需要将设计编译成比特流文件。这个文件包含了FPGA配置的信息。接下来,你需要使用编程器将比特流文件加载到FPGA芯片中。Xilinx的编程器通常是通过JTAG接口与FPGA连接,并将比特流文件写入FPGA。 一旦比特流文件被加载到FPGA中,你就可以对你的设计进行测试和验证。你可以使用外部输入和输出接口,以及其他周边设备与FPGA交互。你可以编写测试程序来验证电路的功能和性能。 总的来说,基于Xilinx 7系列FPGA基础入门需要掌握Vivado设计套件的使用和FPGA设计的基本原理。通过学习和实践,你可以逐渐掌握FPGA的开发过程,并能够设计和实现自己的数字逻辑电路。 ### 回答2: Xilinx 7系列FPGA(可编程逻辑器件)是Xilinx公司推出的一款适用于各类应用的FPGA产品系列。它具有良好的性能和灵活的可编程性,适用于用于高性能计算、通信、图像处理、以及其他嵌入式系统的开发。 在学习7系列FPGA之前,我们需要了解FPGA的基本概念和工作原理。FPGA是可编程逻辑门阵列,它可以通过重新编程来实现不同的电路功能。它由大量的可编程逻辑单元(LUTs)、寄存器、开关、时钟网和输入输出引脚组成。 基于7系列FPGA的入门步骤如下: 1. 学习FPGA基础知识:了解FPGA的基本概念、工作原理和编程模型。 2. 学习VHDL或Verilog语言:VHDL和Verilog是用于FPGA设计的硬件描述语言,学习其中一种语言可以帮助我们理解和编写FPGA设计。 3. 安装开发工具:Xilinx公司提供了Vivado设计套件作为开发工具,我们需要下载并安装它。 4. 学习Vivado的使用:Vivado是用于FPGA设计和实现的集成开发环境,可以实现硬件设计、仿真、综合和布局布线等功能。 5. 编写并仿真设计:使用VHDL或Verilog语言编写我们的FPGA设计,并进行仿真以验证设计的正确性。 6. 实现设计:将设计综合成门级网表,并通过布局布线生成位流文件。 7. 下载位流文件至FPGA:将位流文件下载到7系列FPGA开发板上,使之运行我们的设计。 基于7系列FPGA的入门学习可以帮助我们理解和使用FPGA技术,为进一步深入设计和开发提供基础。通过不断的实践和实际项目应用,我们可以逐步提升对FPGA的理解和掌握,并实现更复杂的硬件设计。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值