本文主要内容来自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)。
  • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
  • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

四、FIFO设计的关键

1. 读写指针的工作原理
  • 写指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。
  • 读指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)
  • 循环数组:类似于C语言中用数组编写队列是front = (front+1)%length,在verilog中定义一个位宽为ADDRSIZE 的地址变量会自动溢出,天然就是一个循环数组
2. 产生可靠的empty/full控制信号

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

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

那么读指针和写指针相等的时候究竟是空还是满呢?
为了区分到底是满状态还是空状态,软件中常用的办法如下:

  • 规定front=rear 为队列空
  • 规定front=rear+1 为队列满

但是这样相当于浪费了一个RAM地址,设计硬件本着减少面积的宗旨,则是采用给指针增加一位MSB,当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。

  • 如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
  • 如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;
3. 异步时钟域同步问题

异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,将写地址同步到读时钟域再和读地址比较进行FIFO空状态判断(同步后的写地址一定是小于或者等于当前的写地址,所以此时判断FIFO为空不一定是真空,这样更保守),将读地址同步到写时钟域再和写地址比较进行FIFO满状态判断(同步后的读地址一定是小于或者等于当前的读地址,所以此时判断FIFO为满不一定是真空,这样更保守),这样可以保证FIFO的特性:FIFO空之后不能继续读取,FIFO满之后不能继续写入。

大多数情形下,异步FIFO两端的时钟不是同频的,或者读快写慢,或者读慢写快,这时候进行地址同步的时候,可能会有地址遗漏,以读慢写快为例,进行满标志判断的时候需要将读地址同步到写时钟域,因为读慢写快,所以不会有读地址遗漏,同步后的读地址滞后当前读地址,所以可能满标志会提前产生。进行空标志判断的时候需要将写地址同步到读地址,因为读慢写快,所以当读时钟同步写地址的时候,必然会漏掉一部分写地址(写时钟快,写地址随写时钟翻转,直到满标志出现为止),那到底读时钟会同步到哪个写地址?不必在意是哪一个,我们关注的是漏掉的地址会不会对FIFO的空标志产生影响。比如写地址从0写到10,期间读时钟域只同步到了2,5,7这三个写地址,漏掉了其他地址。同步到7地址时,真实的写地址可能已经写到10地址,相当于“在读时钟域还没来得及觉察的情况下,写时钟域可能偷偷写了数据到FIFO去”,这样在比较读写地址的时候不会产生FIFO“空”读操作。漏掉的地址也没有对FIFO的逻辑操作产生影响。

binary编码的地址总线在跳变时极易产生毛刺,因为binary编码是多位跳变,在实现电路时不可能做到所有的地址总线等长,address bus skew必然存在,而且写地址和读地址分属不同时钟域,读写时钟完全异步,这样地址总线在进行同步过程中出错不可避免,比如写地址在从0111到1000转换时4条地址线同时跳变,这样读时钟在进行写地址同步后得到的写地址可能是0000-1111的某个值,这个完全不能确定,所以用这个同步后的写地址进行FIFO空判断的时候难免出错。

这个时候gray码体现了价值,一次只有一位数据发生变化,这样在进行地址同步的时候,只有两种情况:1.地址同步正确;2.地址同步出错,但是只有1位出错;第一种正确的情况不需要分析,我们关注第二种,假设写地址从000->001,读时钟域同步出错,写地址为000->000,也就是地址没有跳变,但是用这个错误的写地址去做空判断不会出错,最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。所以gray码保证的是同步后的读写地址即使在出错的情形下依然能够保证FIFO功能的正确性,当然同步后的读写地址出错总是存在的(因为时钟异步,采样点不确定)。这里需要注意gray码只是在相邻两次跳变之间才会出现只有1位数据不一致的情形,超过两个周期则不一定,所有地址总线bus skew一定不能超过一个周期,否则可能出现gray码多位数据跳变的情况,这个时候gray码就失去了作用,因为这时候同步后的地址已经不能保证只有1位跳变了。

4. gray码编址情况下的empty/full如何产生

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

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

  • wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。

  • wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
  • 剩下的其余位完全相等。


五、实现

top

module fifo1 #(parameter DSIZE = 8,parameter ASIZE = 4)(output [DSIZE-1:0] rdata,output             wfull,output             rempty,input  [DSIZE-1:0] wdata,input              winc, wclk, wrst,input              rinc, rclk, rrst);wire [ASIZE-1:0] waddr, raddr;
wire [ASIZE:0] wptr, rptr, wrptr2, rwptr2;sync_r2w #(ASIZE) U1(.wrptr2(wrptr2),.rptr(rptr),.wclk(wclk),.wrst(wrst));sync_w2r #(ASIZE) U2(.rwptr2(rwptr2),.wptr(wptr),.rclk(rclk),.rrst(rrst));fifomem #(DSIZE, ASIZE) U3(.rdata(rdata),.wdata(wdata),.waddr(waddr),.raddr(raddr),.wclken(winc),.wclk(wclk));rptr_empty #(ASIZE) U4(.rempty(rempty),.raddr(raddr),.rptr(rptr),.rwptr2(rwptr2),.rinc(rinc),.rclk(rclk),.rrst(rrst));wptr_full #(ASIZE) U5(.wfull(wfull),.waddr(waddr),.wptr(wptr),.wrptr2(wrptr2),.winc(winc),.wclk(wclk),.wrst(wrst));endmodule
empty/full generation

module rptr_empty #(parameter ADDRSIZE = 4)(output     [ADDRSIZE-1:0] raddr,output reg [ADDRSIZE:0]   rptr,output reg                rempty,input      [ADDRSIZE:0]   rwptr2,input                     rinc, rclk, rrst);reg [ADDRSIZE:0] rbin, rgnext, rbnext;// Grey code pointer
always @(posedge rclk or posedge rrst) beginif (rrst) beginrptr <= 0;rbin <= 0;endelse beginrptr <= rgnext;rbin <= rbnext;end
endalways @(*) beginrbnext = (!rempty)? (rbin + rinc) : rbin;rgnext = (rbnext>>1)^rbnext; // binary to gray
end// Memory read-address pointer
assign raddr = rbin[ADDRSIZE-1:0];// FIFO empty on reset or when the next rptr == synchronized wptr
always @(posedge rclk or posedge rrst) beginif (rrst) rempty <= 1'b1;else rempty <= (rgnext == rwptr2);
endendmodule
module wptr_full #(parameter ADDRSIZE = 4)(output     [ADDRSIZE-1:0] waddr,output reg [ADDRSIZE:0]   wptr,output reg                wfull,input      [ADDRSIZE:0]   wrptr2,input                     winc, wclk, wrst);reg [ADDRSIZE:0] wbin, wgnext, wbnext;// Grey code pointer
always @(posedge wclk or posedge wrst) beginif (wrst) beginwptr <= 0;wbin <= 0;endelse beginwptr <= wgnext;wbin <= wbnext;end
endalways @(*) beginwbnext = (!wfull)? (wbin + winc) : wbin;wgnext = (wbnext>>1)^wbnext;
end// Memory read-address pointer
assign waddr = wbin[ADDRSIZE-1:0];// FIFO full generation
always @(posedge wclk or posedge wrst) beginif (wrst) wfull <= 1'b0;else wfull <= ((wgnext[ADDRSIZE]     != wrptr2[ADDRSIZE]  ) &&(wgnext[ADDRSIZE-1]   != wrptr2[ADDRSIZE-1]) &&(wgnext[ADDRSIZE-2:0] == wrptr2[ADDRSIZE-2:0]));
endendmodule
sync
module sync_r2w #(parameter ADDRSIZE = 4)(output reg [ADDRSIZE:0] wrptr2,input      [ADDRSIZE:0] rptr,input                   wclk, wrst);reg [ADDRSIZE:0] wrptr1;always @(posedge wclk or posedge wrst) beginif (wrst) begin{wrptr2, wrptr1} <= 0;endelse begin{wrptr2, wrptr1} <= {wrptr1, rptr};end
endendmodule
module sync_w2r #(parameter ADDRSIZE = 4)(output reg [ADDRSIZE:0] rwptr2,input      [ADDRSIZE:0] wptr,input                   rclk, rrst);reg [ADDRSIZE:0] rwptr1;always @(posedge rclk or posedge rrst) beginif (rrst) begin{rwptr2, rwptr1} <= 0;endelse begin{rwptr2, rwptr1} <= {rwptr1, wptr};end
endendmodule
testbench

首先是一个行为级的FIFO,二进制编码

module beh_fifo #(parameter DSIZE = 8,parameter ASIZE = 4)(output [DSIZE-1:0] rdata,output             wfull,output             rempty,input  [DSIZE-1:0] wdata,input              winc, wclk, wrst,input              rinc, rclk, rrst);localparam MEMDEPTH = 1<<ASIZE;
reg [DSIZE-1:0] ex_mem [0:MEMDEPTH-1];reg [ASIZE:0] wptr, wrptr1, wrptr2, wrptr3;
reg [ASIZE:0] rptr, rwptr1, rwptr2, rwptr3;always @(posedge wclk or posedge wrst) beginif (wrst) beginwptr <= 0;endelse if (winc && !wfull) beginex_mem[wptr[ASIZE-1:0]] <= wdata;wptr <= wptr+1;end
endalways @(posedge wclk or posedge wrst) beginif (wrst) begin{wrptr3, wrptr2, wrptr1} <= 0;endelse begin{wrptr3, wrptr2, wrptr1} <= {wrptr2, wrptr1, rptr};end
endalways @(posedge rclk or posedge rrst) beginif (rrst) beginrptr <= 0;endelse if (rinc && !rempty) beginrptr <= rptr+1;end
endalways @(posedge rclk or posedge rrst) beginif (rrst) begin{rwptr3, rwptr2, rwptr1} <= 0;endelse begin{rwptr3, rwptr2, rwptr1} <= {rwptr2, rwptr1, wptr};end
endassign rdata = ex_mem[rptr[ASIZE-1:0]];
assign rempty = (rptr == rwptr3);
assign wfull = ((wptr[ASIZE-1:0] == wrptr3[ASIZE-1:0]) &&(wptr[ASIZE]     != wrptr3[ASIZE]));endmodule
`timescale 1ns/100psmodule fifo_tb;parameter DSIZE = 8;
parameter ASIZE = 4;wire [DSIZE-1:0] rdata, beh_rdata;
wire             wfull, beh_wfull;
wire             rempty, beh_rempty;
reg  [DSIZE-1:0] wdata;
reg              winc, wclk, wrst;
reg              rinc, rclk, rrst;fifo1 #(DSIZE, ASIZE) U1(.rdata(rdata),.wfull(wfull),.rempty(rempty),.wdata(wdata),.winc(winc),.wclk(wclk),.wrst(wrst),.rinc(rinc),.rclk(rclk),.rrst(rrst));beh_fifo #(DSIZE, ASIZE) U2(.rdata(beh_rdata),.wfull(beh_wfull),.rempty(beh_rempty),.wdata(wdata),.winc(winc),.wclk(wclk),.wrst(wrst),.rinc(rinc),.rclk(rclk),.rrst(rrst));always #30 wclk = ~wclk;
always #20 rclk = ~rclk;
always #30 wdata = {$random}%256;initial beginwrst = 0;rrst = 0;rclk = 0;wclk = 0;winc = 0;rinc = 0;#50 wrst = 1;rrst = 1;#50 wrst = 0;rrst = 0;#10 rinc = 1;#100 rinc = 0;#100 winc = 1;#1000 winc = 0;#100 rinc = 1;#2000 $finish;
endalways @((rdata  != beh_rdata ) && (wfull  != beh_wfull ) &&(rempty != beh_rempty)) begin$display($time, "rdata is %h, beh_rdata is %h", rdata, beh_rdata);
endendmodule

Verilog基础知识(异步FIFO)相关推荐

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

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

  2. Verilog基础知识总结02

    Verilog基础知识总结02 1.简述Verilog如何建模 数字电路有两种基本要素:线(器件管脚之间的物理连线:wire)和器件(模块:module). Verilog建模就是用HDL语言把数字电 ...

  3. Verilog基础知识(数值表示总结,signed,原码,反码,补码)

    以前虽然是用过verilog,但是只使用了其中最常见wire,reg类型数据,并且是无符号的,因为是及处理过程很多数据就是无符号的.但是想进一步拓展无符号数,或者其底层的补码形式存储与运算方式,就需要 ...

  4. 【基础知识】~ FIFO

    本章目录: 1. 了解FIFO 1.1 定义 1.2 FIFO有什么用处? 1.3 FIFO的参数有哪些? 2. 同步FIFO 2.1 原理 2.2 代码 3. 异步FIFO 3.1 原理 3.2 最 ...

  5. Verilog基础知识3(门控时钟及FPGA时钟使能处理)

    需求说明:Verilog设计 内容       :第一部分  门控时钟                   第二部分  门控时钟和时钟使能的理解(附代码) 来自       :时间的诗 第一部分  门 ...

  6. Verilog基础知识

    I/O端口类型: input             wire型 output           wire/reg型 verilog可综合语句 assign,always,其中initial 语句不 ...

  7. Verilog 基础知识

    Verilog 的逻辑值 逻辑 0:表示低电平,也就是对应我们电路的 GND: 逻辑 1:表示高电平,也就是对应我们电路的 VCC: 逻辑 X:表示未知,有可能是高电平,也有可能是低电平: 逻辑 Z: ...

  8. 同步FIFO的设计,介绍一下FIFO的基础知识

    同步FIFO的设计,介绍一下FIFO的基础知识 \\\插播一条: 自己在今年整理一套单片机单片机相关论文800余篇 论文制作思维导图 原理图+源代码+开题报告+正文+外文资料 想要的同学私信找我. 本 ...

  9. 异步FIFO基本原理(基于Verilog的简单实现)

    参考文献: [1]彭莉, 秦建业, 付宇卓. 异步FIFO的设计与验证[J]. 计算机工程与应用, 2005, 41(3):4. [2]魏芳, 刘志军, 马克杰. 基于Verilog HDL的异步FI ...

最新文章

  1. Binary Matrix Transform
  2. Python学习入门基础教程(learning Python)--3.2 if-else分支语句
  3. nodejs与npm版本对应表
  4. Git的remote
  5. XP下Virtualbox虚拟Ubuntu共享文件夹
  6. weblogic启动项目报错找不到类_启动类报错是经常出现的事但是单一的从一个地方找原因会越找越错...
  7. 修改APACHE的默认站点
  8. 关于使用墙外安卓应用
  9. 公司行政的未来在哪里?要不要转行?
  10. QQ超市模拟排配2D版 1.08 (XNA4.0)
  11. 基于Android Studio的游戏开发-横版格斗.part
  12. CAD导入arcgisMap进行shp导出异常现象
  13. 最大子段和三种算法实现
  14. WIN7更改用户名访问共享文件夹
  15. 怎样用html制作歌词字幕,pr歌词字幕制作方法
  16. 明明办了100M宽带,下载速度为何不到10M/s?
  17. 矩阵快速幂求斐波那契数列 poj3070
  18. 世界上最权威的68句创业名言
  19. 5.5 除法的运算过程
  20. Unity Shader-Ambient Occlusion环境光遮蔽(AO贴图,GPU AO贴图烘焙,SSAO,HBAO)

热门文章

  1. Spring+Hibernate配置文件-applicationContext.xml设置
  2. python中的entry_point
  3. Windows ❀ 使用CMD配置或修改IP地址与DNS命令
  4. python的itchat模块_Python itchat模块在微信上的各种小应用
  5. 计算机老师发展的现状,计算机专业教师队伍的现状分析.doc
  6. 数据科学与大数据技术
  7. mac 常用快捷键整理
  8. STM32——I2S简介硬件连接
  9. 鸿蒙os和hms,华为的鸿蒙os和HMS可以改变,当前美国独霸世纪操作系统的格局?...
  10. vue---uedito---135