1.什么是FIFO?
FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

2.什么情况下用FIFO?
FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集,另一端是计算机的PCI总线,假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。
另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

3.FIFO的一些重要参数
FIFO的宽度:也就是英文资料里常看到的THE WIDTH,它只的是FIFO一次读写操作的数据位,就像MCU有8位和16位,ARM 32位等等,FIFO的宽度在单片成品IC中是固定的,也有可选择的,如果用FPGA自己实现一个FIFO,其数据位,也就是宽度是可以自己定义的。

FIFO的深度:THE DEEPTH,它指的是FIFO可以存储多少个N位的数据(如果宽度为N)。如一个8位的FIFO,若深度为8,它可以存储8个8位的数据,深度为12 ,就可以存储12个8位的数据,FIFO的深度可大可小,个人认为FIFO深度的计算并无一个固定的公式。在FIFO实际工作中,其数据的满/空标志可以控制数据的继续写入或读出。在一个具体的应用中也不可能由一些参数算数精确的所需FIFO深度为多少,这在写速度大于读速度的理想状态下是可行的,但在实际中用到的FIFO深度往往要大于计算值。一般来说根据电路的具体情况,在兼顾系统性能和FIFO成本的情况下估算一个大概的宽度和深度就可以了。而对于写速度慢于读速度的应用,FIFO的深度要根据读出的数据结构和读出数据的由那些具体的要求来确定。

满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。

空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。

读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。

写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

读指针:指向下一个读出地址。读完后自动加1。

写指针:指向下一个要写入的地址的,写完自动加1。
读写指针其实就是读写的地址,只不过这个地址不能任意选择,而是连续的。

4.FIFO的分类
根均FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

5.FIFO设计的难点
FIFO设计的难点在于怎样判断FIFO的空/满状态。为了保证数据正确的写入或读出,而不发生益处或读空的状态出现,必须保证FIFO在满的情况下,不能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题。由于同步FIFO几乎很少用到,这里只描述异步FIFO的空/满标志产生问题。在用到触发器的设计中,不可避免的会遇到亚稳态的问题(关于亚稳态这里不作介绍,可查看相关资料)。
在涉及到触发器的电路中,亚稳态无法彻底消除,只能想办法将其发生的概率将到最低。其中的一个方法就是使用格雷码。格雷码在相邻的两个码元之间只由一位变换(二进制码在很多情况下是很多码元在同时变化)。这就会避免计数器与时钟同步的时候发生亚稳态现象。但是格雷码有个缺点就是只能定义2^n的深度,而不能像二进制码那样随意的定义FIFO的深度,因为格雷码必须循环一个2^n,否则就不能保证两个相邻码元之间相差一位的条件,因此也就不是真正的各雷码了。
第二就是使用冗余的触发器,假设一个触发器发生亚稳态的概率为P,那么两个及联的触发器发生亚稳态的概率就为P的平方。但这回导致延时的增加。亚稳态的发生会使得FIFO出现错误,读/写时钟采样的地址指针会与真实的值之间不同,这就导致写入或读出的地址错误。由于考虑延时的作用,空/满标志的产生并不一定出现在FIFO真的空/满时才出现。可能FIFO还未空/满时就出现了空/满标志。这并没有什么不好,只要保证FIFO不出现overflow or underflow 就OK了。

很多关于FIFO的文章其实讨论的都是空/满标志的不同算法问题。

第一个算法:Clifford E. Cummings的文章中提到的STYLE #1,构造一个指针宽度为N+1,深度为2^N字节的FIFO(为便方比较将格雷码指针转换为二进制指针)。当指针的二进制码中最高位不一致而其它N位都相等时,FIFO为满(在Clifford E. Cummings的文章中以格雷码表示是前两位均不相同,而后两位LSB相同为满,这与换成二进制表示的MSB不同其他相同为满是一样的)。当指针完全相等时,FIFO为空。
这种方法思路非常明了,为了比较不同时钟产生的指针,需要把不同时钟域的信号同步到本时钟域中来,而使用Gray码的目的就是使这个异步同步化的过程发生亚稳态的机率最小,而为什么要构造一个N+1的指针,Clifford E. Cummings也阐述的很明白,有兴趣的读者可以看下作者原文是怎么论述的,Clifford E. Cummings的这篇文章有Rev1.1 \ Rev1.2两个版本,两者在比较Gray码指针时的方法略有不同,个Rev1.2版更为精简。
第二种算法:Clifford E. Cummings的文章中提到的STYLE #2。它将FIFO地址分成了4部分,每部分分别用高两位的MSB 00 、01、 11、 10决定FIFO是否为going full 或going empty (即将满或空)。如果写指针的高两位MSB小于读指针的高两位MSB则FIFO为“几乎满”,若写指针的高两位MSB大于读指针的高两位MSB则FIFO为“几乎空”。
它是利用将地址空间分成4个象限(也就是四个等大小的区域),然后观察两个指针的相对位置,如果写指针落后读指针一个象限(25%的距离,呵呵),则证明很可能要写满,反之则很可能要读空,这个时候分别设置两个标志位dirset和dirrst,然后在地址完全相等的情况下,如果dirset有效就是写满,如果dirrst有效就是读空。
这种方法对深度为2^N字节的FIFO只需N位的指针即可,处理的速度也较第一种方法快。

第一种方法代码如下:

module fifo1(rdata, wfull, rempty, wdata, winc, wclk, wrst_n,rinc, rclk, rrst_n);parameter DSIZE = 8;parameter ASIZE = 4;output [DSIZE-1:0] rdata;output wfull;output rempty;input [DSIZE-1:0] wdata;input winc, wclk, wrst_n;input rinc, rclk, rrst_n;reg wfull,rempty;reg [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr, wq1_rptr,rq1_wptr;reg [ASIZE:0] rbin, wbin;reg [DSIZE-1:0] mem[0:(1<<ASIZE)-1];wire [ASIZE-1:0] waddr, raddr;wire [ASIZE:0]  rgraynext, rbinnext,wgraynext,wbinnext;wire  rempty_val,wfull_val;
//-----------------双口RAM存储器--------------------assign rdata=mem[raddr];always@(posedge wclk)if (winc && !wfull) mem[waddr] <= wdata;
//-------------同步rptr 指针-------------------------always @(posedge wclk or negedge wrst_n)if (!wrst_n) {wq2_rptr,wq1_rptr} <= 0;else {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
//-------------同步wptr指针---------------------------always @(posedge rclk or negedge rrst_n)if (!rrst_n) {rq2_wptr,rq1_wptr} <= 0;else {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
//-------------rempty产生与raddr产生-------------------
//-------------------
// GRAYSTYLE2 pointer
//-------------------always @(posedge rclk or negedge rrst_n)beginif (!rrst_n) {rbin, rptr} <= 0;else {rbin, rptr} <= {rbinnext, rgraynext};end
// Memory read-address pointer (okay to use binary to address memory)assign raddr = rbin[ASIZE-1:0];assign rbinnext = rbin + (rinc & ~rempty);assign rgraynext = (rbinnext>>1) ^ rbinnext;
//---------------------------------------------------------------
// FIFO empty when the next rptr == synchronized wptr or on reset
//---------------------------------------------------------------assign rempty_val = (rgraynext == rq2_wptr);always @(posedge rclk or negedge rrst_n)beginif (!rrst_n) rempty <= 1'b1;else rempty <= rempty_val;end
//---------------wfull产生与waddr产生------------------------------
// GRAYSTYLE2 pointeralways @(posedge wclk or negedge wrst_n)if (!wrst_n) {wbin, wptr} <= 0;else {wbin, wptr} <= {wbinnext, wgraynext};
// Memory write-address pointer (okay to use binary to address memory)assign waddr = wbin[ASIZE-1:0];assign wbinnext = wbin + (winc & ~wfull);assign wgraynext = (wbinnext>>1) ^ wbinnext;
//------------------------------------------------------------------
// 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[ASIZE:ASIZE-1],wq2_rptr[ASIZE-2:0]});always @(posedge wclk or negedge wrst_n)if (!wrst_n) wfull <= 1'b0;else wfull <= wfull_val;
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

第二种方法代码如下

module fifo2 (rdata, wfull, rempty, wdata,winc, wclk, wrst_n, rinc, rclk, rrst_n);parameter DSIZE = 8;parameter ASIZE = 4;output [DSIZE-1:0] rdata;output wfull;output rempty;input [DSIZE-1:0] wdata;input winc, wclk, wrst_n;input rinc, rclk, rrst_n;wire [ASIZE-1:0] wptr, rptr;wire [ASIZE-1:0] waddr, raddr;async_cmp #(ASIZE) async_cmp(.aempty_n(aempty_n),.afull_n(afull_n),.wptr(wptr), .rptr(rptr),.wrst_n(wrst_n));fifomem2 #(DSIZE, ASIZE) fifomem2(.rdata(rdata),.wdata(wdata),.waddr(wptr),.raddr(rptr),.wclken(winc),.wclk(wclk));rptr_empty2 #(ASIZE) rptr_empty2(.rempty(rempty),.rptr(rptr),.aempty_n(aempty_n),.rinc(rinc),.rclk(rclk),.rrst_n(rrst_n));wptr_full2 #(ASIZE) wptr_full2(.wfull(wfull),.wptr(wptr),.afull_n(afull_n),.winc(winc),.wclk(wclk),.wrst_n(wrst_n));
endmodulemodule fifomem2 (rdata, wdata, waddr, raddr, wclken, wclk);parameter DATASIZE = 8; // Memory data word widthparameter ADDRSIZE = 4; // Number of memory address bitsparameter DEPTH = 1<<ADDRSIZE; // DEPTH = 2**ADDRSIZEoutput [DATASIZE-1:0] rdata;input [DATASIZE-1:0] wdata;input [ADDRSIZE-1:0] waddr, raddr;input wclken, wclk;
`ifdef VENDORRAM
// instantiation of a vendor's dual-port RAMVENDOR_RAM MEM (.dout(rdata), .din(wdata),.waddr(waddr), .raddr(raddr),.wclken(wclken), .clk(wclk));
`else
reg [DATASIZE-1:0] MEM [0:DEPTH-1];assign rdata = MEM[raddr];always @(posedge wclk)if (wclken) MEM[waddr] <= wdata;
`endif
endmodule
module async_cmp (aempty_n, afull_n, wptr, rptr, wrst_n);parameter ADDRSIZE = 4;parameter N = ADDRSIZE-1;output aempty_n, afull_n;input [N:0] wptr, rptr;input wrst_n;reg direction;wire high = 1'b1;wire dirset_n = ~( (wptr[N]^rptr[N-1]) & ~(wptr[N-1]^rptr[N]));wire dirclr_n = ~((~(wptr[N]^rptr[N-1]) & (wptr[N-1]^rptr[N])) |~wrst_n);always @(posedge high or negedge dirset_n or negedge dirclr_n)if (!dirclr_n) direction <= 1'b0;else if (!dirset_n) direction <= 1'b1;else direction <= high;
//always @(negedge dirset_n or negedge dirclr_n)
//if (!dirclr_n) direction <= 1'b0;
//else direction <= 1'b1;assign aempty_n = ~((wptr == rptr) && !direction);assign afull_n = ~((wptr == rptr) && direction);
endmodule
module rptr_empty2 (rempty, rptr, aempty_n, rinc, rclk, rrst_n);parameter ADDRSIZE = 4;output rempty;output [ADDRSIZE-1:0] rptr;input aempty_n;input rinc, rclk, rrst_n;reg [ADDRSIZE-1:0] rptr, rbin;reg rempty, rempty2;wire [ADDRSIZE-1:0] rgnext, rbnext;
//---------------------------------------------------------------
// GRAYSTYLE2 pointer
//---------------------------------------------------------------always @(posedge rclk or negedge rrst_n)if (!rrst_n) beginrbin <= 0;rptr <= 0;endelse beginrbin <= rbnext;rptr <= rgnext;end
//---------------------------------------------------------------
// increment the binary count if not empty
//---------------------------------------------------------------assign rbnext = !rempty ? rbin + rinc : rbin;assign rgnext = (rbnext>>1) ^ rbnext; // binary-to-gray conversionalways @(posedge rclk or negedge aempty_n)if (!aempty_n) {rempty,rempty2} <= 2'b11;else {rempty,rempty2} <= {rempty2,~aempty_n};
endmodule
module wptr_full2 (wfull, wptr, afull_n, winc, wclk, wrst_n);parameter ADDRSIZE = 4;output wfull;output [ADDRSIZE-1:0] wptr;input afull_n;input winc, wclk, wrst_n;reg [ADDRSIZE-1:0] wptr, wbin;reg wfull, wfull2;wire [ADDRSIZE-1:0] wgnext, wbnext;
//---------------------------------------------------------------
// GRAYSTYLE2 pointer
//---------------------------------------------------------------
always @(posedge wclk or negedge wrst_n)if (!wrst_n) beginwbin <= 0;wptr <= 0;endelse beginwbin <= wbnext;wptr <= wgnext;end
//---------------------------------------------------------------
// increment the binary count if not full
//---------------------------------------------------------------assign wbnext = !wfull ? wbin + winc : wbin;assign wgnext = (wbnext>>1) ^ wbnext; // binary-to-gray conversionalways @(posedge wclk or negedge wrst_n or negedge afull_n)if (!wrst_n ) {wfull,wfull2} <= 2'b00;else if (!afull_n) {wfull,wfull2} <= 2'b11;else {wfull,wfull2} <= {wfull2,~afull_n};
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

异步的多时钟设计应按以下几个原则进行设计:
1,尽可能的将多时钟的逻辑电路(非同步器)分割为多个单时钟的模块,这样有利于静态时序分析工具来进行时序验证。
2,同步器的实现应使得所有输入来自同一个时钟域,而使用另一个时钟域的异步时钟信号采样数据。
3,面向时钟信号的命名方式可以帮助我们确定那些在不同异步时钟域间需要处理的信号。
4,当存在多个跨时钟域的控制信号时,我们必须特别注意这些信号,保证这些控制信号到达新的时钟域仍然能够保持正确的顺序。

本文转自http://blog.sina.com.cn/s/blog_70c1137b01012n8z.html

https://blog.csdn.net/deng_d1/article/details/50179677

异步FIFO设计原理及Verliog源代码相关推荐

  1. 异步fifo设计及验证verilog代码

    论文参考:The Design and Verification of a Synchronous First-In First-Out 博客参考:[原创]异步FIFO设计原理详解 (含RTL代码和T ...

  2. 74ls390设计任意进制计数器_异步FIFO:设计原理及Verliog源码

    1.  异步FIFO的概念 异步FIFO为读取与写入采用不同的时钟,使用异步FIFO用于在不同的时钟域传输数据,主要用于跨时钟域传输多bit数据. 2.  异步FIFO的设计难点 同步异步信号,避免亚 ...

  3. 基于FPGA的异步FIFO设计

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

  4. 异步fifo_【推荐】数字芯片异步FIFO设计经典论文

    之前有一篇文章我已经推荐过了数字芯片跨时钟域设计的经典论文 ([推荐]数字芯片跨时钟域设计经典论文 ),希望看过的读者都有一定的收获.不过有点遗憾的是那片论文中虽然提到了异步FIFO,却没有讲具体的原 ...

  5. 异步FIFO设计:各个模块的作用及Verilog代码详解

    实现原理参考:异步FIFO---Verilog实现_alangaixiaoxiao的博客-CSDN博客_异步fifo 代码参考:IC基础(一):异步FIFO_MaoChuangAn的博客-CSDN博客 ...

  6. 异步FIFO的原理以及可综合的Verilog代码

    异步FIFO的原理以及可综合的Verilog代码 一.FIFO的定义 二.FIFO的应用场景 三.FIFO的分类 FIFO的参数 FIFO的设计难点 一.FIFO的定义 _ First In Firs ...

  7. 异步fifo_异步FIFO设计

    由于知乎编辑器对markdown支持性问题,本文为直接上传markdown文档,格式混乱且图像不全,格式完整版请移步个人博客 异步FIFO设计​qiankun214.github.io 1.设计目标 ...

  8. 基于 FPGA 的高级数字电路设计(7)单口 RAM、同步 FIFO、异步 FIFO 设计

    一.单口 RAM 设计 module BRAM_PORTA( input clka, input ena, input wea, input [3:0] addra, input [15:0] din ...

  9. 02【Verilog实战】异步FIFO设计(附源码RTL/TB)

    脚 本:makefile 工 具:vcs 和 verdi 文 章:1. 同步FIFO的设计和功能验证(附源码)     2. Verilog的亚稳态现象和跨时钟域处理方法 写在前面 这个专栏的内容记录 ...

最新文章

  1. 解决kubernetes中ingress-nginx配置问题
  2. 用C#使用HttpWebRequest Post数据时如何保持Session
  3. Linux 2.6内核中新的锁机制--RCU [转]
  4. 外部系统调用PO的webservice服务,往SAP系统发送数据,用的XISOAPAdapter的方式,出现错误
  5. 头像裁剪上传插件php,PHP+ajaxfileupload+jcrop插件完美实现头像上传剪裁
  6. java 实现压缩单个文件
  7. matlab——FFT傅里叶快速变换
  8. 谈自动化测试与CI中一些常见的谬见
  9. linux用vsc写c语言,vscode写c语言(windows)
  10. TensorFlow 教程 --教程--2.4MNIST 进阶
  11. linux企业版笔记之Samber服务的基本配置
  12. 【学习】把自己的电脑创建成ftp服务器,用Cuteftp软件上传文件和下载文件。
  13. 以《西游记》为例 详解游戏设计归纳演绎法
  14. SAR图像去噪方法研究
  15. Mac和Windows系统中ssh密钥的生成和共享
  16. 数模电路基础知识 —— 2. 常见电路符号说明 (导线、电源、接地、变压器与保险丝)
  17. JQuery-layer web弹窗层
  18. 倒序输出字符串,如输入i love you,则输出you love i
  19. C++学习日记——头文件的编写
  20. ios 设置控制器背景半透明_iOS Modal出半透明控制器

热门文章

  1. 程序员哑巴英语修炼指南
  2. 30_ElasticSearch IK分词器配置文件 以及自定义词库
  3. 【自动化测试】想了解Selenium吗?看这里!
  4. 真彩色图像数据量 计算_计算机基础:图形、图像相关知识笔记
  5. 哔哩哔哩php开发工作怎么样,[上海] [哔哩哔哩]-努力寻求[PHP /C++/Andro/iOS/前端开发工程师]-会是你吗?...
  6. java 图片水印_JAVA实现图片水印
  7. App陷阱防不胜防:欺骗误导用户下载App、欺骗误导用户提供个人信息
  8. day06ViewPager
  9. 新一配:perl循环调用python爬虫批量下载喜马拉雅音频
  10. 2019TFE计算机科学排名,美国留学|2019TFE Times 硕士专业排名