一、什么是FIFO
FIFO 是 First In First Out 的简称。
是指在FPGA内部用逻辑资源实现的能对数据的存储具有先进先出特性的一种缓存器。
FIFO 与 FPGA 内部的 RAM 和 ROM 的区别是 FIFO 没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,其数据地址由内部读写指针自动加1完成。
FIFO 使用起来简单方便,由此带来的缺点是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。
二、为什么要用FIFO
FPGA内的程序实现的电路实际上是由一个个独立的功能模块组成的,各个模块又通过相关信号关联在一起,当存在模块间处理数据速度不同(有快有慢)时,处理得快的模块就需要等一等处理得慢的模块,这个等待其实就是缓存的实现。
我们可以采用FIFO来解决数据的缓存。
打个比方,就像水龙头放水慢(输入慢),但我们人提水的时候是一次处理一桶水(输出快),所以需要一个水桶作为缓存,等存满一桶水,再一次被人提走。
又或者像我们人喝水,一次接一杯水(输入快), 渴的时候喝两口(输出慢)。这里,杯子作为缓存。
另外,在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,此时,异步时钟之间的接口电路的设计将成为关键。
而使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。
三、什么时候用FIFO
- 数据缓存、
- 协议处理、
- 串并转换、
- 跨时钟域数据处理。
四、FIFO分类
FIFO根据读写时钟是否为同一时钟分为同步FIFO和异步FIFO。
-
同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时可同时发生读写操作。
-
异步FIFO是指读写时钟不一致,读写时钟是互相独立的2个时钟。
-
同步FIFO在实际应用中比较少见,常用的是异步FIFO,但基于学习的目的,下文对两种FIFO都进行讲解。
五、同步FIFO
1. 同步FIFO电路框图
简单来说,同步FIFO其实就是一个双口RAM加上两个读写控制模块。FIFO的常见参数和信号如下:
- FIFO的宽度:即FIFO一次读写操作的数据位;
- FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
- 满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
- 空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
- 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。(同步FIFO 读写只有一个时钟)
- 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。(异步FIFO读写时钟分开)
- 读指针:总是指向下一个将要被写入的单元,写完后自动加1,复位时,指向第1个单元(编号为0)。
- 写指针:总是指向下一个将要被读出的单元,读完后自动加1,复位时,指向第1个单元(编号为0)
其实可以把FIFO比作一个单向行驶的隧道,隧道两端都有一个门进行控制,FIFO宽度就是这个隧道单向有几个车道,FIFO的深度就是一个车道能容纳多少辆车,当隧道内停满车辆时,这就是FIFO的写满状态,当隧道内没有一辆车时,这便是FIFO的读空状态。
2. 同步FIFO空满判断
FIFO 的设计原则是任何时候都不能向满FIFO中写入数据(写溢出),任何时候都不能从空FIFO中读取数据(读溢出)。
FIFO 设计的核心是空满判断。FIFO设置读,写地址指针,FIFO初始化的时候 读指针和写指针都指向地址为0的位置, 当往FIFO里面每写一个数据,写地址指针自动加1指向下一个要写入的地址。
当从FIFO里面每读一个数据,读地址指针自动加1指向下一个要读出的地址,最后通过比较读地址指针和写地址指针的大小来确定空满状态。
当读地址指针追上写地址指针,写地址指针跟读地址指针相等,此时FIFO是读空状态。
当写地址指针再次追上读地址指针,写指针跟读地址指针再次相等的时候,此时FIFO是写满状态。
可以设置一个计数器,当写使能有效的时候计数器加一;
当读使能有效的时候,计数器减一,将计数器与FIFO的size进行比较来判断FIFO的空满状态。
这种方法设计比较简单,但是需要的额外的计数器,就会产生额外的资源,而且当FIFO比较大时,会降低FIFO最终可以达到的速度。
3. 同步FIFO设计代码
同步FIFO基本接口:
信号 | 描述 |
---|---|
clk | 系统时钟 |
rstn | 系统复位信号 |
wr_en | 写使能端 |
wr_data | FIFO写数据 |
fifo_full | FIFO的满标志位 |
rd_en | 读使能端 |
rd_data | FIFO读数据 |
fifo_empty | FIFO的空标志位 |
同步FIFO实现代码如下:
module sync_fifo#(parameter BUF_SIZE=8, BUF_WIDTH=8) (
//FIFO的数据位宽默认为8bit
//FIFO深度默认为8
input i_clk,//输入时钟
input i_rst,//复位信号
input i_w_en,//写使能信号
input i_r_en,//读使能信号
input [BUF_WIDTH-1:0] i_data,//写入数据
output reg [BUF_WIDTH-1:0] o_data,//读出数据
output o_buf_empty,//FIFO空标志
output o_buf_full );//FIFO满标志
reg [3:0] fifo_cnt; //记录FIFO数据个数
reg [$clog2(BUF_SIZE)-1:0] r_ptr,w_ptr; //数据指针为3位宽度,0-7索引,8个数据深度,循环指针0-7-0-7
reg [BUF_WIDTH-1:0] buf_mem[0:BUF_SIZE-1]; //定义FIFO大小
//判断空满
assign o_buf_empty=(fifo_cnt==4'd0)?1'b1:1'b0;
assign o_buf_full=(fifo_cnt==4'd8)?1'b1:1'b0;
always@(posedge i_clk or posedge i_rst) //用于修改计数器
begin
if(i_rst)
fifo_cnt<=4'd0;
else if((!o_buf_full&&i_w_en)&&(!o_buf_empty&&i_r_en))