您的位置:首页 > 其它

FIFO学习:FIF 20000 O深度的计算、通过Verilog实现FIFO以及利用SRAM设计FIFO

2020-06-04 07:08 197 查看

本文档将记录对FIFO的学习以及通过verilog实现FIFO(同步、异步)的过程。

首先记录一个verilog语法知识:
memory型变量:可以通过reg型变量建立数组,来对存储器建模,可以描述RAM、ROM存储器。语言格式如下:

reg[n-1:0] 存储器名[m-1:0];

在语法中reg[n-1:0]定义了存储器中每一个存储单元的大小,即n位寄存器,在FIFO中对应宽度;[m-1:0]定义了存储器中n位寄存器的个数,在FIFO中对应深度。如
reg [7:0] memoryA[255:0];
定义了一个名为memoryA的存储器,其中有256个8位寄存器。存储器的地址范围为0~255。

一.FIFO简介

FIFO为First In First Out的缩写,是一种先进先出的数据缓冲器。和普通存储器的区别为没有外部读写地址线。数据地址由内部读写指针自动加一完成,不能读取指定地址的数据。
用途1:异步FIFO采用读写两个不同的时钟,能解决跨时钟域系统的数据传输。
用途2:可以起到数据匹配的功能。用来对接不同宽度的数据接口。

二.FIFO分类

同步FIFO、异步FIFO,区别为读写时钟是否一致。

三.常见参数

宽度:一次读写操作的数据位。
深度:FIFO最多可以存储多少个N位数据(宽度为N)。
满标志:当FIFO已满或者将要满时拉高,阻止继续进行写操作而造成溢出overflow。
空标志:当FIFO已空或者将要空时拉高,阻止继续进行读操作而造成读出无效数据underflow。
读时钟:读操作时遵循的时钟,一个周期读一次数。
写时钟:写操作时遵循的时钟,一个周期写一次数。
读指针:指向下一次读操作要读取的单元,在读数的过程中自动加一。复位时指向第一个单元。
写指针:指向下一次写操作要写入的单元,在写数的过程中自动加一。复位时指向第一个单元。

四.FIFO深度的计算

这类问题在求职面试中很常见,问题有前提条件,即写时钟大于读时钟,且写操作并不连续,因为如果写操作连续而读操作速度过慢,不管多大的FIFO都会溢出。所以题目应理解为写操作是一个突发行为。
在数据传输的整个过程中,”写数据=读数据“才是一个有效的FIFO,但是当把观察范围缩小,比如在Burst突发情况下,写数据很有可能大于读数据,但是并没有影响,因为如果FIFO的深度足够,当写操作停止,一直到下一个Burst突发之前,读操作可以在这个时间间隙中把FIFO中的数据读出。下面看几种类型的例题。

  1. 单次发送,求FIFO最小深度。

e.g.1 一个8bit宽的FIFO,写时钟为100MHz,读时钟为95MHz,假设一个package为为4Kbit,且两个package中间发送间隔足够大,求FIFO最小深度。

思路:题中假设两个package中间间隔足够大,即可以理解为考虑发送一个package。那么整个传输过程就分为Burst和Idle两个阶段。在Burst阶段,读写操作同时进行,在Idle阶段,写操作结束,读操作继续直到将整个package中的数全部读出。

因此在计算FIFO最小深度时,只需计算在Burst中,写操作比读操作多写了多少数即可,多写入的数可以在IDLE阶段慢慢读出。
首先计算Burst的时间,因为在Burst阶段需要写完整个package的数,则时间为4000/8/100M =5us,在这5us中,读操作进行了5*95=475次,则FIFO深度为4000/8-475=25。

  1. 异步FIFO,考虑极端背靠背情况。

e.g.2 FIFO写时钟频率wclk,读时钟频率rclk,在写周期里,B个周期可以写入A个数,读周期中,Y个周期读出X个数,求FIFO最小深度。

思路:题中没有说明发送package的大小,我们假设一个Burst写入的数据长度为0.5BL(BurstLength) ,并且我们可以理解为默认读写宽度一致。由于每个Burst操作一定要伴随IDLE,所以我们能考虑的极端情况就是两个Burst背靠背,而最多只能有两个Burst背靠背,如果有三个Burst,则中间的Burst没有IDLE,又可以看作是两个背靠背的Burst组合。

可以理解为,一次写操作一共为B个周期,但是只有A个周期为Burst操作,即效率为A/B,但是在两个背靠背的Burst期间,传输效率为100%,即2A个周期写入2A个数。我们需要考虑的就是在这两个Burst期间,写入比读出多写入的数据可以全部存入到FIFO中而不丢失。
两个Burst期间,传输数据长度为BL,所用时间为BL/wclk,而在这段时间之内,读出的数据量为(BL/wclk)⋅(x/y)⋅rclk,因此剩余未被读出的数据量为BL-BL/wclk⋅(x/y)⋅rclk,即为FIFO最小深度。将公式变换即为网上常见的公式FIFODepth=BurstLength−BurstLength⋅(X/Y)⋅(rclk/wclk)FIFODepth=BurstLength-BurstLength⋅(X/Y)⋅(rclk/wclk)FIFODepth=BurstLength−BurstLength⋅(X/Y)⋅(rclk/wclk)

五.通过Verilog实现FIFO

在Quartus II中,如果单纯的想使用FIFO,完全可以不用这么麻烦,直接调用FIFO的IP,软件会直接生成一个FIFO,完全不用自己编写逻辑,在设置界面可以设置FIFO的属性,如深度、宽度、同步/异步、usedw信号、满/空标志位等等,这里放置一个设置界面,不过多描述。

然而一片FPGA的资源有限,有时无法满足FIFO深度的要求,因此需要外接RAM,把RAM当成FIFO来用,比如SRAM,FIFO的数据读写操作与SRAM的数据读写操作基本相同,只是FIFO没有地址,所以如果外扩SRAM的关键点是如何产生正确的SRAM地址,这个会在我的学习过程中逐渐介绍。
还有一点,就是在公司的面试过程中,面试官如果问你怎样编写一个FIFO,如果你回答在Quartus II中直接调用就可以生成一个FIFO ,这样肯定不行,所以我们还要通过理解FIFO的工作过程来自己编写一个FIFO逻辑。

5.1 判断FIFO当前状态(满/空)

我们可以把FIFO理解成一个环形数组,并且有两个指针:读指针(fifo_rp),写指针(fifo_wp)。其中,读指针指向下一次读操作要读取的单元,每完成一次读操作,自动加一;写指针指向下一次写操作要写入的单元,每完成一次写操作,自动加一。
这样我们可以想象,当fifo_rp和fifo_wp指向同一单元时,FIFO可能被写满或者被读空。当状态为fifo_wp追赶上fifo_rp时,为写满;当状态为fifo_rp追赶上fifo_wp时,为读空。

因此仅仅凭借观察两指针是否相等无法判断为写满或者读空,但是我们可以通过其他方法来判断。

//----------------------------------------------------------------------------分割线--------------------------------------------------------------------------------------------//

未完待续

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐