掰开揉碎讲 FIFO(同步FIFO和异步FIFO) |
您所在的位置:网站首页 › 慢出特性有什么用 › 掰开揉碎讲 FIFO(同步FIFO和异步FIFO) |
一、什么是FIFO
FIFO 是 First In First Out 的简称。是指在FPGA内部用逻辑资源实现的能对数据的存储具有先进先出特性的一种缓存器。
FIFO 与 FPGA 内部的 RAM 和 ROM 的区别是 FIFO 没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,其数据地址由内部读写指针自动加1完成。FIFO 使用起来简单方便,由此带来的缺点是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。 二、为什么要用FIFOFPGA内的程序实现的电路实际上是由一个个独立的功能模块组成的,各个模块又通过相关信号关联在一起,当存在模块间处理数据速度不同(有快有慢)时,处理得快的模块就需要等一等处理得慢的模块,这个等待其实就是缓存的实现。我们可以采用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实现代码如下: 1 module sync_fifo#(parameter BUF_SIZE=8, BUF_WIDTH=8) ( 2 //FIFO的数据位宽默认为8bit 3 //FIFO深度默认为8 4 5 6 input i_clk,//输入时钟 7 input i_rst,//复位信号 8 input i_w_en,//写使能信号 9 input i_r_en,//读使能信号 10 input [BUF_WIDTH-1:0] i_data,//写入数据 11 12 output reg [BUF_WIDTH-1:0] o_data,//读出数据 13 output o_buf_empty,//FIFO空标志 14 output o_buf_full );//FIFO满标志 15 16 reg [3:0] fifo_cnt; //记录FIFO数据个数 17 reg [$clog2(BUF_SIZE)-1:0] r_ptr,w_ptr; //数据指针为3位宽度,0-7索引,8个数据深度,循环指针0-7-0-7 18 reg [BUF_WIDTH-1:0] buf_mem[0:BUF_SIZE-1]; //定义FIFO大小 19 20 21 //判断空满 22 assign o_buf_empty=(fifo_cnt==4'd0)?1'b1:1'b0; 23 assign o_buf_full=(fifo_cnt==4'd8)?1'b1:1'b0; 24 25 26 always@(posedge i_clk or posedge i_rst) //用于修改计数器 27 begin 28 if(i_rst) 29 fifo_cnt>1) ^ bincode;如果二进制变化没有任何规律,那么采用格雷码也可能发生多 bit 的跳变,而 FIFO 设计中的读写地址都是连续变化的,因此格雷码适用于 FIFO 的地址处理。 5、如何判断异步FIFO的空满 (1)空判断 当读地址指针追上写地址指针,写地址指针跟读地址指针相等,此时FIFO是读空状态。
(2)满判断 当写地址指针再次追上读地址指针,写指针跟读地址指针再次相等的时候,此时FIFO是写满状态。
(3)虚空、虚满 当发现计数器不准。 当写地址同步到读时钟域时 这个地址需要在读时钟域打两拍,而这两拍的过程中写控制端还可以继续向FIFO里面写 数据,如果此时判断FIFO为空的话,这个空属于虚空。
当读地址同步到写时钟域时 这个地址需要在写时钟域打两拍,而这两拍的过程中读控制端还可以继续从FIFO里面读取 数据,如果此时判断FIFO为满的话,这个满属于虚满。
虚空虚满不会产生错误, 只是影响FIFO 效率。 理解这些原理后,分析问题就知道去哪里分析。 (4)FIFO的乒乓操作假设异步FIFO是同时进行读写, 写时钟域比读时钟域慢,当发生亚稳态时, 采用乒乓操作。两个FIFO交替进行,满足同时读写需求。 写比读快 第一种情况,已知连续写数据的长度(Burst Length),那么只需要考虑这段时间内最多会写进多少个数,以及会读走多少个数,二者只差就是FIFO的深度。
读比写快 FIFO的深度为1就可以了。
读写一样 FIFO的深度为1就可以了。 7、异步FIFO的设计代码 (1)顶层模块 1 module async_fifo#(parameter BUF_SIZE=8, BUF_WIDTH=8) 2 //FIFO深度默认为8 3 //FIFO的数据位宽默认为8bit 4 5 ( 6 input [BUF_WIDTH-1:0] i_wdata, 7 input i_w_en, i_wclk, i_wrst_n, //写请求信号,写时钟,写复位 8 input i_r_en, i_rclk, i_rrst_n, //读请求信号,读时钟,读复位 9 output [BUF_WIDTH-1:0] o_rdata, 10 output o_buf_full, 11 output o_buf_empty 12 ); 13 wire [$clog2(BUF_SIZE)-1:0] waddr, raddr; 14 wire [$clog2(BUF_SIZE):0] wptr, rptr, wq2_rptr, rq2_wptr; 15 16 17 /*在检测“满”或“空”状态之前,需要将指针同步到其它时钟域时,使用格雷码,可以降低同步过程中亚稳态出现的概率*/ 18 19 sync_r2w I1_sync_r2w( 20 .wq2_rptr(wq2_rptr), 21 .rptr(rptr), 22 .wclk(i_wclk), 23 .wrst_n(i_wrst_n)); 24 sync_w2r I2_sync_w2r ( 25 .rq2_wptr(rq2_wptr), 26 .wptr(wptr), 27 .rclk(i_rclk), 28 .rrst_n(i_rrst_n)); 29 30 /* DualRAM */ 31 32 dualram #(BUF_WIDTH, BUF_SIZE) I3_DualRAM( 33 .rdata(o_rdata), 34 .wdata(i_wdata), 35 .waddr(waddr), 36 .raddr(raddr), 37 .wclken(i_w_en), 38 .wclk(i_wclk)); 39 40 41 /*空、满比较逻辑*/ 42 43 rptr_empty #(BUF_SIZE) I4_rptr_empty( 44 .rempty(o_buf_empty), 45 .raddr(raddr), 46 .rptr(rptr), 47 .rq2_wptr(rq2_wptr), 48 .rinc(i_r_en), 49 .rclk(i_rclk), 50 .rrst_n(i_rrst_n)); 51 wptr_full #(BUF_SIZE) I5_wptr_full( 52 .wfull(o_buf_full), 53 .waddr(waddr), 54 .wptr(wptr), 55 .wq2_rptr(wq2_rptr), 56 .winc(i_w_en), 57 .wclk(i_wclk), 58 .wrst_n(i_wrst_n)); 59 endmodule (2)双端口RAM模块双端口RAM模块用于存储数据。 1 module dualram 2 #( 3 parameter BUF_WIDTH = 8, // 数据位宽 4 parameter BUF_SIZE = 8 // FIFO深度 5 ) 6 ( 7 input wclken,wclk, 8 input [$clog2(BUF_SIZE)-1:0] raddr, //RAM 读地址 9 input [$clog2(BUF_SIZE)-1:0] waddr, //RAM 写地址 10 input [BUF_WIDTH-1:0] wdata, //写数据 11 output [BUF_WIDTH-1:0] rdata //读数据 12 ); 13 14 reg [BUF_WIDTH-1:0] Mem[BUF_SIZE-1:0]; 15 16 17 always@(posedge wclk) 18 begin 19 if(wclken) 20 Mem[waddr] |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |