本文大部分内容来自Clifford E. Cummings的《Simulation and Synthesis Techniques for Asynchronous FIFO Design》,同时加上一些自己的一些理解,有兴趣的朋友可以阅读原文。

一、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中最后一个字后,追赶上了写指针时,如下图所示:

当读写指针再次相等时,表明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相同代表同一位置。
  • 剩下的其余位完全相等。

<ignore_js_op>

5.总体实现

系统的总体框图如下:

1)顶层模块

  1. module AsyncFIFO
  2. #(parameter ASIZE = 4,    //地址位宽
  3. parameter DSIZE = 8)    //数据位宽
  4. (
  5. input  [DSIZE-1:0] wdata,
  6. input              winc, wclk, wrst_n,  //写请求信号,写时钟,写复位
  7. input              rinc, rclk, rrst_n,  //读请求信号,读时钟,读复位
  8. output [DSIZE-1:0] rdata,
  9. output             wfull,
  10. output             rempty
  11. );
  12. wire [ASIZE-1:0] waddr, raddr;
  13. wire [ASIZE:0]   wptr, rptr, wq2_rptr, rq2_wptr;        /************************************************************
  14. * In order to perform FIFO full and FIFO empty tests using
  15. * this FIFO style, the read and write pointers must be
  16. * passed to the opposite clock domain for pointer comparison
  17. *************************************************************/
  18. /*在检测“满”或“空”状态之前,需要将指针同步到其它时钟域时,使用格雷码,可以降低同步过程中亚稳态出现的概率*/
  19. sync_r2w I1_sync_r2w(
  20. .wq2_rptr(wq2_rptr),
  21. .rptr(rptr),
  22. .wclk(wclk),
  23. .wrst_n(wrst_n));
  24. sync_w2r I2_sync_w2r (
  25. .rq2_wptr(rq2_wptr),
  26. .wptr(wptr),
  27. .rclk(rclk),
  28. .rrst_n(rrst_n));
  29. /*
  30. *  DualRAM
  31. */
  32. DualRAM #(DSIZE, ASIZE) I3_DualRAM(
  33. .rdata(rdata),
  34. .wdata(wdata),
  35. .waddr(waddr),
  36. .raddr(raddr),
  37. .wclken(winc),
  38. .wclk(wclk));
  39. /*
  40. *  空、满比较逻辑
  41. */
  42. rptr_empty #(ASIZE) I4_rptr_empty(
  43. .rempty(rempty),
  44. .raddr(raddr),
  45. .rptr(rptr),
  46. .rq2_wptr(rq2_wptr),
  47. .rinc(rinc),
  48. .rclk(rclk),
  49. .rrst_n(rrst_n));
  50. wptr_full #(ASIZE) I5_wptr_full(
  51. .wfull(wfull),
  52. .waddr(waddr),
  53. .wptr(wptr),
  54. .wq2_rptr(wq2_rptr),
  55. .winc(winc),
  56. .wclk(wclk),
  57. .wrst_n(wrst_n));
  58. endmodule

复制代码

2)DualRAM模块

  1. module DualRAM
  2. #(
  3. parameter DATA_SIZE = 8,   // 数据位宽
  4. parameter ADDR_SIZE = 4   // 地址位宽
  5. )
  6. (
  7. input                       wclken,wclk,
  8. input      [ADDR_SIZE-1:0]  raddr,     //RAM read address
  9. input      [ADDR_SIZE-1:0]  waddr,     //RAM write address
  10. input      [DATA_SIZE-1:0]  wdata,    //data input
  11. output     [DATA_SIZE-1:0]  rdata      //data output
  12. );
  13. localparam RAM_DEPTH = 1 << ADDR_SIZE;   //RAM深度 = 2^ADDR_WIDTH
  14. reg [DATA_SIZE-1:0] Mem[RAM_DEPTH-1:0];
  15. always@(posedge wclk)
  16. begin
  17. if(wclken)
  18. Mem[waddr] <= wdata;
  19. end
  20. assign rdata =  Mem[raddr];
  21. endmodule

复制代码

3)同步模块

  1. module sync_r2w
  2. #(parameter ADDRSIZE = 4)
  3. (
  4. output reg [ADDRSIZE:0] wq2_rptr,
  5. input      [ADDRSIZE:0] rptr,
  6. input                       wclk, wrst_n
  7. );
  8. reg [ADDRSIZE:0] wq1_rptr;
  9. always @(posedge wclk or negedge wrst_n)
  10. if (!wrst_n)
  11. {wq2_rptr,wq1_rptr} <= 0;
  12. else
  13. {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
  14. endmodule

复制代码

4)同步模块2

  1. module sync_w2r
  2. #(parameter ADDRSIZE = 4)
  3. (
  4. output reg  [ADDRSIZE:0] rq2_wptr,
  5. input         [ADDRSIZE:0] wptr,
  6. input         rclk, rrst_n
  7. );        reg [ADDRSIZE:0] rq1_wptr;
  8. always @(posedge rclk or negedge rrst_n)
  9. if (!rrst_n)
  10. {rq2_wptr,rq1_wptr} <= 0;
  11. else
  12. {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
  13. endmodule

复制代码

5)空判断逻辑

  1. module rptr_empty
  2. #(parameter ADDRSIZE = 4)
  3. (
  4. output reg rempty,
  5. output     [ADDRSIZE-1:0] raddr,
  6. output reg [ADDRSIZE :0]  rptr,
  7. input       [ADDRSIZE :0] rq2_wptr,
  8. input       rinc, rclk, rrst_n);
  9. reg  [ADDRSIZE:0] rbin;
  10. wire [ADDRSIZE:0] rgraynext, rbinnext;
  11. wire  rempty_val;
  12. //-------------------
  13. // GRAYSTYLE2 pointer: gray码读地址指针
  14. //-------------------
  15. always @(posedge rclk or negedge rrst_n)
  16. if (!rrst_n)
  17. begin
  18. rbin <= 0;
  19. rptr <= 0;
  20. end
  21. else
  22. begin
  23. rbin <= rbinnext ;
  24. rptr <= rgraynext;
  25. end
  26. // gray码计数逻辑
  27. assign rbinnext = !rempty ? (rbin + rinc) : rbin;
  28. assign rgraynext = (rbinnext>>1) ^ rbinnext;      //二进制到gray码的转换
  29. assign raddr = rbin[ADDRSIZE-1:0];
  30. //---------------------------------------------------------------
  31. // FIFO empty when the next rptr == synchronized wptr or on reset
  32. //---------------------------------------------------------------
  33. /*
  34. *   读指针是一个n位的gray码计数器,比FIFO寻址所需的位宽大一位
  35. *   当读指针和同步过来的写指针完全相等时(包括MSB),说明二者折回次数一致,FIFO为空
  36. *
  37. */
  38. assign rempty_val = (rgraynext == rq2_wptr);
  39. always @(posedge rclk or negedge rrst_n)
  40. if (!rrst_n)
  41. rempty <= 1'b1;
  42. else
  43. rempty <= rempty_val;
  44. endmodule

复制代码

6)满判断逻辑

  1. module wptr_full
  2. #(
  3. parameter ADDRSIZE = 4
  4. )
  5. (
  6. output reg                wfull,
  7. output     [ADDRSIZE-1:0] waddr,
  8. output reg [ADDRSIZE :0]  wptr,
  9. input      [ADDRSIZE :0]  wq2_rptr,
  10. input                     winc, wclk, wrst_n);
  11. reg  [ADDRSIZE:0] wbin;
  12. wire [ADDRSIZE:0] wgraynext, wbinnext;
  13. wire wfull_val;
  14. // GRAYSTYLE2 pointer
  15. always @(posedge wclk or negedge wrst_n)
  16. if (!wrst_n)
  17. begin
  18. wbin <= 0;
  19. wptr <= 0;
  20. end
  21. else
  22. begin
  23. wbin <= wbinnext;
  24. wptr <= wgraynext;
  25. end
  26. //gray 码计数逻辑
  27. assign wbinnext  = !wfull ? wbin + winc : wbin;
  28. assign wgraynext = (wbinnext>>1) ^ wbinnext;
  29. assign waddr = wbin[ADDRSIZE-1:0];
  30. /*由于满标志在写时钟域产生,因此比较安全的做法是将读指针同步到写时钟域*/
  31. /**/
  32. //------------------------------------------------------------------
  33. // Simplified version of the three necessary full-tests:
  34. // assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) &&
  35. // (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) &&
  36. // (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));
  37. //------------------------------------------------------------------
  38. assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
  39. wq2_rptr[ADDRSIZE-2:0]});
  40. always @(posedge wclk or negedge wrst_n)
  41. if (!wrst_n)
  42. wfull <= 1'b0;
  43. else
  44. wfull <= wfull_val;
  45. endmodule

复制代码

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

转载:http://www.openhw.org/module/forum/thread-596561-1-1.html

异步FIFO的FPGA实现相关推荐

  1. FPGA逻辑设计回顾(6)多比特信号的CDC处理方式之异步FIFO

    文章目录 前言 异步FIFO的概念 异步FIFO为什么可以解决CDC问题? 异步FIFO的RTL实现 参考资料 前言 异步FIFO是处理多比特信号跨时钟域的最常用方法,简单来说,异步FIFO是双口RA ...

  2. (87)FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计

    1.1 FPGA面试题-同步FIFO与异步FIFO区别?异步FIFO代码设计 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题-同步FIFO与异步FIFO区 ...

  3. 基于FPGA的目标颜色识别追踪三——FIFO(同/异步FIFO)、DDR3

    FIFO在数据处理过程中是十分重要的. 同步FIFO比较简单,面试过程中手撕代码可能会用到. module sFIFO #(parameter DATA_WIDTH = 8,ADDR_WIDTH = ...

  4. FPGA数字IC刷题58道Verilog题解代码及视频讲解【FPGA探索者】【同步/异步FIFO】【跨时钟】

    牛客 Verilog 刷题入门篇1~24 + 进阶篇1~34 题解代码,所有代码均能通过测试,配合视频讲解效果更佳.为避免内容冗余,本文只给出代码,部分题目给出必要说明. 很多题目本身出题有些问题,着 ...

  5. 【FPGA学习记录1】异步FIFO的介绍

    文章目录 写在前面 1.FIFO的定义以及为什么要用FIFO.FIFO的分类 1.1什么是FIFO 1.2为什么要用FIFO 1.3FIFO的分类 2.异步FIFO的工作原理 2.1FIFO的结构 2 ...

  6. Xilinx 异步FIFO核实现FPGA与DSP通过UPP(通用并口)进行数据传输

    Xilinx 异步FIFO核实现FPGA与DSP通过UPP(通用并口)进行数据传输 一.Xilinx FIFO IP核介绍 二.UPP 仿真效果 总结 一.Xilinx FIFO IP核介绍 1. 因 ...

  7. FPGA基础知识极简教程(4)从FIFO设计讲起之异步FIFO篇

    博文目录 写在前面 正文 同步FIFO回顾 $clog2()系统函数使用 综合属性控制资源使用 异步FIFO设计 FIFO用途回顾 异步FIFO原理回顾 异步FIFO设计 异步FIFO仿真 参考资料 ...

  8. 异步fifo的设计(FPGA)

    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据) fifo位宽(每个 ...

  9. 基于FPGA的异步FIFO设计

    今天要介绍的异步FIFO,可以有不同的读写时钟,即不同的时钟域.由于异步FIFO没有外部地址端口,因此内部采用读写指针并顺序读写,即先写进FIFO的数据先读取(简称先进先出).这里的读写指针是异步的, ...

最新文章

  1. Cisco 3560 丢失 IOS 解决过程
  2. HDU 2181 哈密顿绕行世界问题【DFS】
  3. 原创不易!做网络推广怎么才能更好地保护网站的原创文章?
  4. 蛮力法求最大字段和时间复杂度_硬笔字应该选择的工具,你了解吗?
  5. 某互联网公司校园招聘的小组面试题
  6. SCF: 简单配置门面
  7. 提取Java集合的元素-Java 8方法
  8. 信安教程第二版-第15章网络安全主动防御技术与应用
  9. era5数据内容说明_mysql数据库自带主从配置
  10. 耳机使用说明书 jbl ua_JBL UA联名款,全新一代真无线运动耳机“UA小黑盒”今日天猫首发...
  11. 【推荐】网络安全学习路线和资料分享
  12. ansys linux卸载干净,怎么把ansys删除干净
  13. python把英语句子成分字母_如何标注英语句子成分?
  14. c语言.jpg图片转成数组_JPG图片怎么转换成PDF?可以试试这些转换方法!
  15. DEV gridview数据筛选
  16. python爬虫实操|爬取nba中国官网球员数据
  17. Python金融数据挖掘 第八章 第1节 Apriori算法原理(2)
  18. 艾宾浩斯遗忘曲线复习计划表
  19. 实现strStr()
  20. 剖析Solidity合约创建EVM bytecode

热门文章

  1. 粒子滤波实现物体跟踪
  2. Optimize Slow VBA Code
  3. QT Media Error: DirectShowPlayerService::doRender: Unresolved error code 0x80040266
  4. THANATOS数据库(自噬调节相关蛋白及其翻译后修饰信息数据库)使用指南
  5. 医学工作者如何进行医学科研设计?
  6. linux 镜像自动安装,制作能自动安装的CentOS镜像文件
  7. 【转】程序debug正常release错误
  8. SQLite相关知识
  9. mysql数据每日更新_[每日更新-MySQL]4.记录操作(数据操作)
  10. html 超链接打开Excel,计算机打开Excel超链接时提示的解决方案