FIFO 简介
FIFO 是英文 First In First Out 的缩写,是一种先进先出的数据缓存器,它与一般存储器的区别是没有外部读写地址线,这样运用起来十分简略,但缺陷便是只能次序写入数据,次序的读出数据,其数据地址由内部读写指针主动加 1 完结,不能像一般存储器那样能够由地址线决议读取或写入某个指定的地址。
用处 1:
异步 FIFO 读写别离选用彼此异步的不同时钟。在现代集成电路芯片中,跟着规划规划的不断扩大,一个体系中往往含有数个时钟,多时钟域带来的一个问题便是,怎么规划异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简洁、方便的处理方案,运用异步 FIFO 能够在两个不一起钟体系之间快速而方便地传输实时数据。
用处 2:
关于不同宽度的数据接口也能够用 FIFO,例如单片机位 8 位数据输出,而 DSP 或许是 16 位数据输入,在单片机与 DSP 衔接时就能够运用 FIFO 来到达数据匹配的意图。
分类
同步 FIFO 是指读时钟和写时钟为同一个时钟,在时钟沿来暂时一起发生读写操作;
异步 FIFO 是指读写时钟不共同,读写时钟是相互独立的。
FIFO 的常见参数
FIFO 的宽度:即 FIFO 一次读写操作的数据位;
FIFO 的深度:指的是 FIFO 能够存储多少个 N 位的数据(假如宽度为 N)。
满标志:FIFO 已满或即将满时由 FIFO 的状况电路送出的一个信号,以阻挠 FIFO 的写操作持续向 FIFO 中写数据而形成溢出(overflow)。
空标志:FIFO 已空或即将空时由 FIFO 的状况电路送出的一个信号,以阻挠 FIFO 的读操作持续从 FIFO 中读出数据而形成无效数据的读出(underflow)。
读时钟:读操作所遵从的时钟,在每个时钟沿来暂时读数据。
写时钟:写操作所遵从的时钟,在每个时钟沿来暂时写数据。
1. 读写指针的作业原理
读指针:总是指向下一个即将被写入的单元,复位时,指向第 1 个单元(编号为 0)。
写指针:总是指向当时要被读出的数据,复位时,指向第 1 个单元(编号为 0)
2.FIFO 的“空”/“满”检测
FIFO 规划的要害:发生牢靠的 FIFO 读写指针和生成 FIFO“空”/“满”状况标志。
当读写指针持平时,标明 FIFO 为空,这种状况发生在复位操作时,或许当读指针读出 FIFO 中最终一个字后,追逐上了写指针时,如下图所示:
当读写指针再次持平时,标明 FIFO 为满,这种状况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:
为了区别到底是满状况仍是空状况,能够选用以下办法:
办法 1:在指针中添加一个额定的位(extra bit),当写指针添加并跳过最终一个 FIFO 地址时,就将写指针这个未用的 MSB 加 1,其它位回零。对读指针也进行相同的操作。此刻,关于深度为 2n 的 FIFO,需求的读 / 写指针位宽为(n+1)位,如关于深度为 8 的 FIFO,需求选用 4bit 的计数器,0000~1000、1001~1111,MSB 作为折回标志位,而低 3 位作为地址指针。
假如两个指针的 MSB 不同,阐明写指针比读指针多折回了一次;如 r_addr=0000,而 w_addr = 1000,为满。
假如两个指针的 MSB 相同,则阐明两个指针折回的次数持平。其他位持平,阐明 FIFO 为空;
3. 二进制 FIFO 指针的考虑
将一个二进制的计数值从一个时钟域同步到另一个时钟域的时分很简单呈现问题,因为选用二进制计数器时一切位都或许一起改变,在同一个时钟沿同步多个信号的改变会发生亚稳态问题。而运用格雷码只要一位改变,因而在两个时钟域间同步多个位不会发生问题。所以需求一个二进制到 gray 码的转化电路,将地址值转化为相应的 gray 码,然后将该 gray 码同步到另一个时钟域进行比照,作为空满状况的检测。
4. 运用 gray 码进行比照,怎么判别“空”与“满”
运用 gray 码处理了一个问题,但一起也带来另一个问题,即在格雷码域怎么判别空与满。
关于“空”的判别仍然根据二者彻底持平(包含 MSB);
而关于“满”的判别,如下图,因为 gray 码除了 MSB 外,具有镜像对称的特色,当读指针指向 7,写指针指向 8 时,除了 MSB,其他位皆相同,不能说它为满。因而不能单纯的只检测最高位了,在 gray 码上判别为满有必要一起满意以下 3 条:
wptr 和同步过来的 rptr 的 MSB 不持平,因为 wptr 有必要比 rptr 多折回一次。
wptr 与 rptr 的次高位不持平,如上图方位 7 和方位 15,转化为二进制对应的是 0111 和 1111,MSB 不同阐明多折回一次,111 相同代表同一方位。
剩余的其他位彻底持平。
5. 整体完成
体系的整体框图如下:
1)顶层模块
module AsyncFIFO
#(parameter ASIZE = 4, // 地址位宽
parameter DSIZE = 8) // 数据位宽 ( input [DSIZE-1:0] wdata, input winc, wclk, wrst_n, // 写恳求信号,写时钟,写复位
input rinc, rclk, rrst_n, // 读恳求信号,读时钟,读复位
output [DSIZE-1:0] rdata, output wfull, output rempty
);wire [ASIZE-1:0] waddr, raddr;wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr; /************************************************************
* In order to perform FIFO full and FIFO empty tests using
* this FIFO style, the read and write pointers must be
* passed to the opposite clock domain for pointer comparison
*************************************************************//*在检测“满”或“空”状况之前,需求将指针同步到其它时钟域时,运用格雷码,能够下降同步过程中亚稳态呈现的概率*/sync_r2w I1_sync_r2w(
.wq2_rptr(wq2_rptr),
.rptr(rptr),
.wclk(wclk),
.wrst_n(wrst_n));
sync_w2r I2_sync_w2r (
.rq2_wptr(rq2_wptr),
.wptr(wptr),
.rclk(rclk),
.rrst_n(rrst_n));/** DualRAM
*/DualRAM #(DSIZE, ASIZE) I3_DualRAM(
.rdata(rdata),
.wdata(wdata),
.waddr(waddr),
.raddr(raddr),
.wclken(winc),
.wclk(wclk)); /** 空、满比较逻辑*/rptr_empty #(ASIZE) I4_rptr_empty(
.rempty(rempty),
.raddr(raddr),
.rptr(rptr),
.rq2_wptr(rq2_wptr),
.rinc(rinc),
.rclk(rclk),
.rrst_n(rrst_n));
wptr_full #(ASIZE) I5_wptr_full(
.wfull(wfull),
.waddr(waddr),
.wptr(wptr),
.wq2_rptr(wq2_rptr),
.winc(winc),
.wclk(wclk),
.wrst_n(wrst_n));endmodule
2)DualRAM 模块
module DualRAM
#( parameter DATA_SIZE = 8, // 数据位宽
parameter ADDR_SIZE = 4 // 地址位宽)
( input wclken,wclk, input [ADDR_SIZE-1:0] raddr, //RAM read address
input [ADDR_SIZE-1:0] waddr, //RAM write address
input [DATA_SIZE-1:0] wdata, //data input
output [DATA_SIZE-1:0] rdata //data output); localparam RAM_DEPTH = 1 《《 ADDR_SIZE; //RAM 深度 = 2^ADDR_WIDTH
reg [DATA_SIZE-1:0] Mem[RAM_DEPTH-1:0]; always@(posedge wclk)begin
if(wclken)
Mem[waddr] 《= wdata;endassign rdata = Mem[raddr];endmodule
3)同步模块
module sync_r2w
#(parameter ADDRSIZE = 4)
( output reg [ADDRSIZE:0] wq2_rptr, input [ADDRSIZE:0] rptr, input wclk, wrst_n
);reg [ADDRSIZE:0] wq1_rptr;always @(posedge wclk or negedge wrst_n) if (!wrst_n)
{wq2_rptr,wq1_rptr} 《= 0; else
{wq2_rptr,wq1_rptr} 《= {wq1_rptr,rptr};endmodule
4)同步模块 2
module sync_w2r
#(parameter ADDRSIZE = 4)
( output reg [ADDRSIZE:0] rq2_wptr, input [ADDRSIZE:0] wptr, input rclk, rrst_n
); reg [ADDRSIZE:0] rq1_wptr;always @(posedge rclk or negedge rrst_n) if (!rrst_n)
{rq2_wptr,rq1_wptr} 《= 0; else
{rq2_wptr,rq1_wptr} 《= {rq1_wptr,wptr};endmodule
5)空判别逻辑
module rptr_empty
#(parameter ADDRSIZE = 4)
( output reg rempty, output [ADDRSIZE-1:0] raddr, output reg [ADDRSIZE :0] rptr, input [ADDRSIZE :0] rq2_wptr, input rinc, rclk, rrst_n);
reg [ADDRSIZE:0] rbin;wire [ADDRSIZE:0] rgraynext, rbinnext;wire rempty_val;//——————-// GRAYSTYLE2 pointer: gray 码读地址指针 //——————-always @(posedge rclk or negedge rrst_n) if (!rrst_n)
begin
rbin 《= 0;
rptr 《= 0; end
else
begin
rbin 《= rbinnext ;
rptr 《= rgraynext; end// gray 码计数逻辑 assign rbinnext = !rempty ? (rbin + rinc) : rbin;assign rgraynext = (rbinnext》》1) ^ rbinnext; // 二进制到 gray 码的转化
assign raddr = rbin[ADDRSIZE-1:0];//—————————————————————// FIFO empty when the next rptr == synchronized wptr or on reset//—————————————————————/** 读指针是一个 n 位的 gray 码计数器,比 FIFO 寻址所需的位广大一位
* 当读指针和同步过来的写指针彻底持平时(包含 MSB),阐明二者折回次数共同,FIFO 为空
*
*/assign rempty_val = (rgraynext == rq2_wptr); always @(posedge rclk or negedge rrst_n)if (!rrst_n)
rempty 《= 1‘b1;else
rempty 《= rempty_val;endmodule
6)满判别逻辑
module wptr_full
#( parameter ADDRSIZE = 4)
( output reg wfull, output [ADDRSIZE-1:0] waddr, output reg [ADDRSIZE :0] wptr, input [ADDRSIZE :0] wq2_rptr, input winc, wclk, wrst_n);
reg [ADDRSIZE:0] wbin;wire [ADDRSIZE:0] wgraynext, wbinnext;wire wfull_val;// GRAYSTYLE2 pointeralways @(posedge wclk or negedge wrst_n) if (!wrst_n)
begin
wbin 《= 0;
wptr 《= 0; end
else
begin
wbin 《= wbinnext;
wptr 《= wgraynext; end//gray 码计数逻辑 assign wbinnext = !wfull ? wbin + winc : wbin;assign wgraynext = (wbinnext》》1) ^ wbinnext; assign waddr = wbin[ADDRSIZE-1:0]; /*因为满标志在写时钟域发生,因而比较安全的做法是将读指针同步到写时钟域*//**///——————————————————————// Simplified version of the three necessary full-tests:// assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) &&// (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) &&// (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));//——————————————————————assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
wq2_rptr[ADDRSIZE-2:0]});always @(posedge wclk or negedge wrst_n)if (!wrst_n)
wfull 《= 1’b0;else
wfull 《= wfull_val;endmodule
P.S :在 quartus 中有异步 FIFO IP 核,为安全起见引荐运用 IP 核定制 FIFO,本文的意图仅仅作为思路参阅。
责任编辑:gt