FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO。同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的不同步可能会导致亚稳态,导致FIFO工作异常,设计较为复杂;在之前的记录中,我们对同步FIFO的设计进行了分析:

Verilog实现FIFO专题(3-同步FIFO设计)

此处我们不再对同步FIFO进行介绍而直接以异步FIFO与同步FIFO的异同为线索,逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,并进一步实现异步FIFO。

目录

一、异步FIFO与同步FIFO工作流程比较

1、同步FIFO

2、异步FIFO

二、异步FIFO的空满检测

1、同步FIFO的空满检测

2、异步FIFO的空满检测

计数检测空满:

指针比较检测空满:

扩展指针比较检测空满:

格雷码指针比较检测空满:

三、异步FIFO的同步处理

1、同步方式

2、延迟对FIFO设计的影响

结论:

FIFO满检测:

FIFO空检测:

四、异步FIFO设计

1、端口设计

外部端口

内部信号

2、功能描述

3、实现代码

4、仿真验证

五、参考文献


一、异步FIFO与同步FIFO工作流程比较

1、同步FIFO

同步FIFO的读写控制信号以及数据均处于同一时钟域,即:FIFO在同一时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

2、异步FIFO

异步FIFO的工作内容与同步FIFO类似:FIFO在时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

但是异步FIFO的控制并不像同步FIFO那么简单,因为异步FIFO工作在不同的时钟域,这就带来了一些问题:

(1)如何进行空满检测?还能像同步FIFO中通过计数值来判断吗?

(2)需要同步电路

二、异步FIFO的空满检测

1、同步FIFO的空满检测

同步FIFO的空满检测可以通过计数很简单的实现:

读写逻辑是同一个时钟,因此可以在每次时钟来临时进行判断,如果不执行读写操作/同时读写,则计数值不变;只执行读操作,计数值减1;只执行写操作,计数值加1;

如果计数值为0,则说明FIFO空,只能写不能读(直到写入一次数据,计数值加1,FIFO不再为空才能执行读操作);

如果计数值为FIFO深度,则说明FIFO满,只能读不能写(直到读出一次数据,计数值减1,FIFO不再为满才能执行写操作);

2、异步FIFO的空满检测

计数检测空满:

异步FIFO不能采用同步FIFO这种计数方式来实现空满检测,因为用两个时钟去控制同一个计数器的加剪很明显是不可取的。

如果不明白为什么不能用两个时钟控制同一个计数器,可以查阅:Verilog中always@()语句双边沿触发(语法与综合的差异)

指针比较检测空满:

读写指针指向读写操作面向的FIFO地址空间,因此空满检测的另一个思路是比较读写指针。每次写操作执行,写指针加1;而每次读操作执行,读指针加1,因此:

FIFO空发生在:读指针追上写指针时;

FIFO满发生在:写指针追上读指针时;

但是这种处理仍存在一个问题,就是读写指针相等时,难以区分FIFO是空还是满。

扩展指针比较检测空满:

如上分析,直接比较读写指针时存在一个特例:读写指针相等时,难以区分FIFO是空还是满。

因此,有人提出,将指针进行高位扩展。即指针加宽一位,当写指针超出FIFO深度时,这额外的一位就会改变。FIFO的深度决定了指针扩展前(即除了最高位的其余位)的宽度,而这扩展的一位与FIFO深度无关,是为了标志指针多转了一圈,因此:

当读写指针完全相同时,FIFO空;

当读写指针高位不同,其余位完全相同时,FIFO满;

经过指针扩展,可以明确的进行空满检测。但是这种处理仍然不够,因为异步FIFO读写时钟相互独立,分属不同时钟域,相邻二进制地址位变化时,不止一个地址位都要变化,这样指针在进行同步过程中很容易出错,比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大。

格雷码指针比较检测空满:

如上分析,直接比较扩展读写指针时可能因多位改变导致错误。因此,进一步采用gray码形式的指针,利用格雷码每次只变化一位的特性,降低同步发生时错误的概率。如图,为一个深度为8FIFO的格雷码指针(绿色框中):

0-7为真实的FIFO地址,而8-15是指针多转一圈以后的地址(8-0,9-1...)。应注意,此时指针按照格雷码方式进行编码,不能再用二级制指针的比较方式来判断空满。比如:位置6(0101)和位置9(1101),除最高位外其余位均相等。但是很明显,位置9实际对应位置1处的存储空间。

因此,格雷码指针下的空满检测条件为:

当最高位和次高位均相同,其余位相同:FIFO空

当最高位和次高位均相反,其余位相同:FIFO满

因此,最终的空满检测方式为:将二进制指针转换为格雷码,用于另一时钟域接收,随后按照检测条件进行检测。

二进制指针转换为格雷码的详情见:Verilog实现二进制码与格雷码转换  此处不再展开。

三、异步FIFO的同步处理

1、同步方式

判断FIFO空满状态时,需要在读FIFO时获取写时钟域的写指针,与读指针比较来判断FIFO是否为空;需要在写FIFO时获取读时钟域的读指针,与写指针比较来判断FIFO是否为满;

也就是说,判断空满状态时牵扯到跨时钟域问题,需要进行同步;

采用两级寄存器打两拍的方式进行同步,具体实现见:亚稳态专题

2、延迟对FIFO设计的影响

异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,将读写指针同步后再进行比较,判断FIFO空满状态。但是因为在同步指针时需要时间(如延迟两拍同步),而在这个同步的时间内有可能还会写入/读出新的数据,因此同步后的指针一定是小于或者等于当前实际的读/写指针,那么此时判断FIFO满空状态时是否会出错?是否会导致错误?

结论:

先说结论:异步逻辑进行同步时,不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。

FIFO满检测:

FIFO满检测发生在写时钟域,将读指针同步到写时钟域后再和写指针比较,进行FIFO满状态判断。因为同步时间的存在,同步后的读指针一定是小于或者等于当前真正的读指针(同步时间内可能出现了读操作,导致读指针增加),所以此时判断FIFO为满不一定是真满,这样更保守(即:写指针=同步读指针<=真实读指针),这样可以保证FIFO的特性:FIFO空之后不能继续读取。

FIFO空检测:

FIFO空检测发生在读时钟域,将写指针同步到读时钟域后再和读指针比较,进行FIFO空状态判断。因为同步时间的存在,同步后的写指针一定是小于或者等于当前真正的写指针(同步时间内可能出现了写操作,导致写指针增加),所以此时判断FIFO为空不一定是真空,这样更保守(即:读指针=同步写指针<=真实写指针),这样可以保证FIFO的特性:FIFO满之后不能继续写入。

四、异步FIFO设计

1、端口设计

外部端口

1、读时钟信号clk_r,作为异步FIFO的读驱动信号

2、写时钟信号clk_w,作为异步FIFO的写驱动信号

3、异步复位信号rst_n

// 写FIFO相关

4、数据输入信号din[DW-1:0],作为FIFO数据写入端,DW数据为位宽

5、写使能we

// 读FIFO相关

6、数据输出信号dout[DW-1:0],作为FIFO数据输出端

7、读使能re

// 标志相关

8、FIFO满标志full,FIFO满时不能再写入数据

9、FIFO空标志empty,FIFO空时不能再读出数据

内部信号

1、读指针wp,作为读地址(FIFO读写只能顺序进行,不能外部设置,因此为内部信号)

2、格雷码读指针wp_g,供写时钟域同步后判断FIFO满使用

3、写指针rp,作为写地址

4、格雷码写指针wp_g,供读时钟域同步后判断FIFO空使用

5、[DW-1:0]ram[0:Depth-1],数据存储空间,Depth为FIFO深度

2、功能描述

读逻辑:

clk_r来临,re有效,并且FIFO非空(empty=0)时,将读指针rp对应地址处的数据读出至dout;

// 读操作
always@(posedge clk_r or negedge rst_n)
beginif(!rst_n)dout <= {DW{1'bz}};else if(!empty & re)dout <= ram[rp[AW-1:0]];elsedout <= dout;
end

读指针逻辑

clk_r来临,re有效,并且FIFO非空(empty=0)时,进行一次读操作,rp加1(即顺序读出),并进行格雷码转换,生成对应rp_g;

// 读指针
always@(posedge clk_r or negedge rst_n)
beginif(!rst_n)rp <= {AW{1'b0}};else if(!empty & re)rp <= rp+1'b1;elserp <= rp;
end

写逻辑:

clk_w来临,we有效,并且FIFO不满(full=0)时,将din写入写指针wp对应地址处;

//写操作
always@(posedge clk_w)
beginif(!full & we)ram[wp[AW-1:0]] <= din;elseram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
end

写指针逻辑

clk_w来临,we有效,并且FIFO不满(full=0)时,进行一次写操作,wp加1(即顺序存储),并进行格雷码转换,生成对应wp_g;

//写指针
always@(posedge clk_w or negedge rst_n)
beginif(!rst_n)wp <= {AW{1'b0}};else if(!full & we)wp <= wp+1'b1;elsewp <= wp;
end

格雷码指针生成逻辑:

利用二进制与格雷码的转换关系,将二进制指针转换为格雷码指针,用于另一个时钟域的同步接收;

// 二进制指针转换为格雷指针
assign wp_g = (wp>>1) ^ wp;
assign rp_g = (rp>>1) ^ rp;

格雷码指针同步逻辑:

读时钟域,同步写地址,用于空逻辑判断;写时钟域,同步读地址,用于满逻辑判断;

// 读时钟域,写地址同步
always@(posedge clk_r or negedge rst_n)
beginif(!rst_n)beginwp_m <= {AW{1'b0}};wp_s <= {AW{1'b0}};       endelsebeginwp_m <= wp_g;wp_s <= wp_m;    end
end
// 写时钟域,读地址同步
always@(posedge clk_w or negedge rst_n)
beginif(!rst_n)beginrp_m <= {AW{1'b0}};rp_s <= {AW{1'b0}};       endelsebeginrp_m <= rp_g;rp_s <= rp_m;    end
end

空满检测逻辑:

根据格雷码指针判断逻辑,进行空满检测,应注意:

读时钟域,同步写地址。读格雷码指针与同步后写格雷码指针比较,用于空逻辑判断;

写时钟域,同步读地址,写格雷码指针与同步后读格雷码指针比较,用于满逻辑判断;

// 空满检测,使用同步后的格雷指针?
assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针

3、实现代码

`timescale 1ns / 1ps
//
// Company:
// Engineer: guoliang CLL
//
// Create Date: 2020/03/24 20:51:06
// Design Name:
// Module Name: afifo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module afifo
#(parameter DW = 8,AW = 4)//默认数据宽度8,FIFO深度16
(input clk_r,input clk_w,input rst_n,input we,input re,input [DW-1:0]din,output reg [DW-1:0]dout,output empty,output full);
// internal signal
parameter Depth = 1 << AW;//depth of FIFO
reg [DW-1:0]ram[0:Depth-1];
reg [AW:0]wp;  //point
reg [AW:0]rp;
wire [AW:0]wp_g;//Gray point
wire [AW:0]rp_g;
reg [AW:0]wp_m;//mid_point for syn
reg [AW:0]rp_m;
reg [AW:0]wp_s;//point after syn
reg [AW:0]rp_s;
// FIFO declaration
// 二进制指针转换为格雷指针
assign wp_g = (wp>>1) ^ wp;
assign rp_g = (rp>>1) ^ rp;
// 空满检测,使用同步后的格雷指针?
assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
// 读指针
always@(posedge clk_r or negedge rst_n)
beginif(!rst_n)rp <= {AW{1'b0}};else if(!empty & re)rp <= rp+1'b1;elserp <= rp;
end
//写指针
always@(posedge clk_w or negedge rst_n)
beginif(!rst_n)wp <= {AW{1'b0}};else if(!full & we)wp <= wp+1'b1;elsewp <= wp;
end
// 读操作
always@(posedge clk_r or negedge rst_n)
beginif(!rst_n)dout <= {DW{1'bz}};else if(!empty & re)dout <= ram[rp[AW-1:0]];elsedout <= dout;
end
//写操作
always@(posedge clk_w)
beginif(!full & we)ram[wp[AW-1:0]] <= din;elseram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
end
// 读时钟域,写地址同步
always@(posedge clk_r or negedge rst_n)
beginif(!rst_n)beginwp_m <= {AW{1'b0}};wp_s <= {AW{1'b0}};       endelsebeginwp_m <= wp_g;wp_s <= wp_m;    end
end
// 写时钟域,读地址同步
always@(posedge clk_w or negedge rst_n)
beginif(!rst_n)beginrp_m <= {AW{1'b0}};rp_s <= {AW{1'b0}};       endelsebeginrp_m <= rp_g;rp_s <= rp_m;    end
end
endmodule

4、仿真验证

测试文件如下:

`timescale 1ns / 1ps
//
// Company:
// Engineer: CLL guoliang
//
// Create Date: 2020/03/24 21:22:45
// Design Name:
// Module Name: afifo_tsb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module afifo_tsb();
// port declaration
reg clk_r;
reg clk_w;
reg rst_n;
reg we;
reg re;
reg [7:0]din;
wire  [7:0]dout;
wire empty;
wire full;
//clk
initial
beginclk_r = 1'b0;forever #25 clk_r = ~clk_r;
end
initial
beginclk_w = 1'b0;forever #10 clk_w = ~clk_w;
end
//
initial
beginrst_n = 1'b1;din = 1'b0;re = 1'b0;we = 1'b0;#50 rst_n = 1'b0;#50 rst_n = 1'b1;// only writewe = 1'b1;repeat(20) #20 din = din+1'b1;// only read we = 1'b0;re = 1'b1;repeat(20) #50; // read and write
//    we = 1'b1;
//    re = 1'b1;
//    din = 1'b0;
//    repeat(20) #20 din = din+1'b1;
end
// inst
afifo inst2(.clk_r(clk_r),.clk_w(clk_w),.rst_n(rst_n),.we(we),.re(re),.din(din),.dout(dout),.empty(empty),.full(full)
);
endmodule

仿真结果如下:

篇幅有限,仿真结果不再展开分析。提醒自己,应注意仿真测试是很必要的,通过功能仿真能暴露出设计上的不足、缺陷、以及实现过程中因粗心等导致的其余问题;

因此,如何设计测试文件也具有重要意义。测试文件容易编写,但是如何使得测试文件能全面的对设计进行检测,高效准确的对设计进行测试,无疑是一门学问;

我只简单记录一下,我调试时关注的部分

1、写逻辑

数据能否在写时钟驱动下,顺序写入FIFO中对应地址;FIFO满时,是否停止写入;

2、读逻辑

能否在读时钟驱动下,顺序读出FIFO中对应数据;FIFO空时,是否停止读出;

3、满判断

设计能否在写时钟驱动下,同步读指针,并且在适当位置产生满标志;

3、空判断

设计能否在读时钟驱动下,同步写指针,并且在适当位置产生空标志;

RTL电路如下:

五、参考文献

Verilog实现FIFO专题(3-同步FIFO设计)

异步FIFO的设计

Verilog中always@()语句双边沿触发(语法与综合的差异)

Verilog实现二进制码与格雷码转换

亚稳态专题

Verilog实现FIFO专题5-异步FIFO设计(异步FIFO工作方式、异步FIFO介绍、异步FIFO介绍)相关推荐

  1. 影视概念设计的日常工作方式?

    影视概念设计的日常工作方式?有很多人的理想就是做影视概念设计师,但是理想很丰满,现实很骨感.想必对于影视概念设计有所了解的人知道这个行业的要求是非常高的,如果你没有真才实学,那么是很难立足于这个行业中 ...

  2. 异步复位设计中的亚稳态问题及其解决方案

    异步复位设计中的亚稳态问题及其解决方案田志明,杨军,罗岚(东南大学国家专用集成电路系统工程技术研究中心,南京 210096) 摘 要:尽管异步复位是一种安全可靠复位电路的方法,但如果处理不当的话,异步 ...

  3. 实验五、任意N进制异步计数器设计

    实验五  任意N进制异步计数器设计 实验目的 掌握任意N进制异步计数器设计的方法. 实验要求 一人一组,独立上机. 在电脑上利用Multisim软件完成实验内容. 实验内容 说明任意N进制异步计数器的 ...

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

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

  5. FPGA设计实用分享02之XILINX的可参数化FIFO

    一.背景 FIFO是FPGA项目中使用最多的IP核,一个项目使用几个,甚至是几十个FIFO都是很正常的.通常情况下,每个FIFO的参数,特别是位宽和深度,是不同的. 明德扬(MDY)在2021年承担了 ...

  6. FIFO专题之单口RAM实现FIFO(同步)

    使用单口RAM实现FIFO,其实很简单,其中的重点就是区分出读写, 读写如果同时启动,你肯定会思考单口RAM肯定会出问题,毕竟单口RAM只有一个口,肯定不能实现同时读写,那么怎么解决这个问题呢. 有两 ...

  7. 05【Verilog实战】AMBA 3 APB接口设计(附源码RTL/TB)

    官方手册:点击下载 脚  本:makefile 工  具:vcs & verdi 写在前面 这个专栏的内容记录的是个人学习过程,博文中贴出来的代码是调试前的代码,方便bug重现. 调试后的程序 ...

  8. fifo介绍及fifo IP核使用(工程文件获取请参考文末)

    一.fifo简介 1.fifo first in first out的缩写,先进先出数据缓存器. 与普通存储器的区别:对外接口没有地址线. 由此所带来的优点是:不用处理地址信号,时序较简单. 缺点是: ...

  9. FPGA异步时钟设计中的同步策略

    1 引言    基于FPGA的数字系统设计中大都推荐采用同步时序的设计,也就是单时钟系统.但是实际的工程中,纯粹单时钟系统设计的情况很少,特别是设计模块与外围芯片的通信中,跨时钟域的情况经常不可避免. ...

最新文章

  1. php的匿名函数和闭包函数
  2. python测试用例怎么写_Python单元测试unittest的具体使用示例
  3. CCNP中单区域OSPF详解(第一部分)
  4. SAP CRM WebClient UI根据扩展字段搜索出结果的实现原理
  5. puppet变量、数据类型及类(03)
  6. ModuleNotFoundError: No module named ‘keras.api‘
  7. php举例--array,php中session的应用举例-php中array_key_exists与isset的区别...-php取整数的几种方式(实例)_169IT.COM...
  8. BZOJ1023 SHOI2008 仙人掌图 仙人掌、单调队列
  9. 【Python实践】Python部分实际案例解答1
  10. matlab画图函数基本使用(适合新手)
  11. Vivado使用记录(二)下载与固化
  12. ajax 点击下一页,ajax调用不会进入下一页
  13. h标签,b标签,em,i ,u,s标签
  14. 【观察】“专精特新”数智化国产替代,为何首选用友U9cloud?
  15. 跟计算机相关的商标类别,一体电脑商标注册属于第几类?
  16. 笔记本计算机的连接无线网络连接,笔记本电脑连接wifi的方法步骤
  17. Edge浏览器兼容性设置,实用技巧,分享给你(2023新版)
  18. 《MySQL数据库用户模块需求分析》
  19. jquery 图片转为base64
  20. cpu负载过高产生的原因及排查

热门文章

  1. linux下如何计算cpu利用率,Linux下的CPU利用率计算原理详解
  2. windows启动管理器修复计算机,电脑开机显示Windows启动管理器无法进入怎么处理...
  3. 华为云发布CodeArts Req需求管理工具,让需求管理化繁为简
  4. 阿里的德鲁伊怎么玩?
  5. Linux下ARM汇编教程
  6. QQMusic与Android studio 冲突导致Adb not reposponding
  7. 解决IIS中无法运行aspx文件,提示:服务器应用程序不可用
  8. 【Verilog】ROM RAM
  9. 关于Staltstack
  10. Python数据分析案例28——西雅图交通事故预测(不平衡样本处理)