0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FPGA的异步FIFO的实现

FPGA学习交流 来源:互联网 作者:佚名 2018-06-21 11:15 次阅读

大家好,又到了每日学习的时间了,今天我们来聊一聊基于FPGA的异步FIFO的实现。

一、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个单元(编号为0)。
读指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)

FIFO的“空”/“满”检测
FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。

当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:
123118sawxcouuaacy3ck4.png


当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:
122509xslrjygkggjyghr5.png


为了区分到底是满状态还是空状态,可以采用以下方法:

方法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码同步到另一个时钟域进行对比,作为空满状态的检测。
122509e29dmvv62g2bg4os.png


4.
使用gray码进行对比,如何判断“空”与“满”
使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。

对于“空”的判断依然依据二者完全相等(包括MSB);

而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:

* wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
* wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
* 剩下的其余位完全相等。
122510vqu8elbujwakye4n.png

5.总体实现
系统的总体框图如下:
122510pdyt99tfhddmob5t.png


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;
end
assign 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 pointer
always @(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

quartus中有异步FIFO IP核,为安全起见推荐使用IP核定制FIFO,本文的目的只是作为思路参考。

今天就聊到这里,各位,加油。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1600

    文章

    21281

    浏览量

    592853
收藏 人收藏

    评论

    相关推荐

    异步FIFO结构设计

    电子发烧友网站提供《异步FIFO结构设计.pdf》资料免费下载
    发表于 02-06 09:06 0次下载

    FPGA学习-异步FIFO原型设计与验证

      点击上方 蓝字 关注我们   第一节:fifo基础     内容: 1. 掌握FPGA设计中关于数据缓存的使用 2. 掌握FIFO工作原理
    的头像 发表于 11-17 14:00 204次阅读

    FIFO为什么不能正常工作?

    FIFO的情形。 在FPGA设计中,我们会经常用到异步FIFO进行跨时钟域隔离。作为已经非常成熟的设计,AMD提供
    的头像 发表于 11-02 09:25 544次阅读
    <b class='flag-5'>FIFO</b>为什么不能正常工作?

    异步FIFO设计之格雷码

    相邻的格雷码只有1bit的差异,因此格雷码常常用于异步fifo设计中,保证afifo的读地址(或写地址)被写时钟(或读时钟)采样时最多只有1bit发生跳变。
    的头像 发表于 11-01 17:37 832次阅读
    <b class='flag-5'>异步</b><b class='flag-5'>FIFO</b>设计之格雷码

    请问异步FIFO的溢出操作时怎么样判断的?

    请问异步FIFO的溢出操作时怎么样判断的? 异步FIFO是数据传输的一种常用方式,在一些储存器和计算机系统中,常常会用到异步
    的头像 发表于 10-18 15:28 356次阅读

    同步FIFO异步FIFO的区别 同步FIFO异步FIFO各在什么情况下应用

    简单的一种,其特点是输入和输出都与时钟信号同步,当时钟到来时,数据总是处于稳定状态,因此容易实现数据的传输和存储。 而异步FIFO则是在波形的上升沿和下降沿上进行处理,在输入输出端口处分别增加输入和输出指针,用于管理数据的读写。
    的头像 发表于 10-18 15:23 991次阅读

    采用格雷码异步FIFO跟标准FIFO有什么区别

    异步FIFO包含"读"和"写“两个部分,写操作和读操作在不同的时钟域中执行,这意味着Write_Clk和Read_Clk的频率和相位可以完全独立。异步FIFO
    的头像 发表于 09-14 11:21 585次阅读
    采用格雷码<b class='flag-5'>异步</b><b class='flag-5'>FIFO</b>跟标准<b class='flag-5'>FIFO</b>有什么区别

    XILINX FPGA IP之FIFO Generator例化仿真

    上文XILINX FPGA IP之FIFO对XILINX FIFO Generator IP的特性和内部处理流程进行了简要的说明,本文通过实际例子对该IP的使用进行进一步的说明。本例子例化一个读数据位宽是写数据位宽两倍的
    的头像 发表于 09-07 18:31 867次阅读
    XILINX <b class='flag-5'>FPGA</b> IP之<b class='flag-5'>FIFO</b> Generator例化仿真

    异步FIFO-格雷码

    很多人在面试时被问到为什么异步FIFO中需要用到格雷码,可能大部分的答案是格雷码可以消除亚稳态。这种回答比较模糊,今天我们就针对这个来深入探讨一下。
    的头像 发表于 08-26 14:20 621次阅读
    <b class='flag-5'>异步</b><b class='flag-5'>FIFO</b>-格雷码

    跨时钟设计:异步FIFO设计

    在ASIC设计或者FPGA设计中,我们常常使用异步fifo(first in first out)(下文简称为afifo)进行数据流的跨时钟,可以说没使用过afifo的Designer,其设计经历是不完整的。废话不多说,直接上接
    的头像 发表于 07-31 11:10 1332次阅读
    跨时钟设计:<b class='flag-5'>异步</b><b class='flag-5'>FIFO</b>设计

    同步FIFO设计详解及代码分享

    FIFO (先入先出, First In First Out )存储器,在 FPGA 和数字 IC 设计中非常常用。 根据接入的时钟信号,可以分为同步 FIFO异步
    发表于 06-27 10:24 1298次阅读
    同步<b class='flag-5'>FIFO</b>设计详解及代码分享

    FPGA零基础学习之Vivado-FIFO使用教程

    ,这样我们可以实现读写不同速度。 那么接下来,我们就来实现一下异步FIFO的读写过程。 上图为选择异步
    发表于 06-16 17:50

    FIFO设计—异步FIFO

    异步FIFO主要由五部分组成:写控制端、读控制端、FIFO Memory和两个时钟同步端
    发表于 05-26 16:17 1024次阅读
    <b class='flag-5'>FIFO</b>设计—<b class='flag-5'>异步</b><b class='flag-5'>FIFO</b>

    FIFO设计—同步FIFO

    FIFO异步数据传输时常用的存储器,多bit数据异步传输时,无论是从快时钟域到慢时钟域,还是从慢时钟域到快时钟域,都可以使用FIFO处理。
    发表于 05-26 16:12 1040次阅读
    <b class='flag-5'>FIFO</b>设计—同步<b class='flag-5'>FIFO</b>

    FIFO使用及其各条件仿真介绍

    FIFO(First In First Out )先入先出存储器,在FPG设计中常用于跨时钟域的处理,FIFO可简单分为同步FIFO异步FIFO
    的头像 发表于 04-25 15:55 2993次阅读
    <b class='flag-5'>FIFO</b>使用及其各条件仿真介绍