- 源 自
- 不同时钟域的数据
- 端口数
- 两个端口
- 外文名
- Asynchronous FIFO
- 介 绍
- 是一块数据内存
- 中文名
- 异步FIFO
-
异步FIFO
2020-08-04 21:25:06异步FIFO常用情况FIFO的空满标志FIFO指针格雷码格雷码下的空满信号重点最小深度FIFO的计算代码 最近两天晚上在看异步FIFO,之前也使用过异步FIFO,但是一直没有仔细搞懂原理。这次准备写篇博客记录一下。 常用情况 ...
最近两天晚上在看异步FIFO,之前也使用过异步FIFO,但是一直没有仔细搞懂原理。这次准备写篇博客记录一下。常用情况
以下两种情况,经常会使用到异步FIFO
1.跨时钟域问题
2.位宽不一致时(例如 输入8bit 输出16bit)FIFO的空满标志
一个深度为(2^n)的FIFO,其地址位宽需要n+1位
- MSB作为指针折回标志,当指针完成一轮读或写,MSB就翻转一次。所以当wptr和rptr相遇时,如果MSB=1,为满;MSB=0,为空。
FIFO指针格雷码
由于二进制在跨时钟域上可能有所有位同时翻转的可能,而格雷码每次只有一位变化,可以大大降低亚稳态出现的可能。
格雷码下的空满信号
因为地址是格雷码,所以不能单纯的比较n位是否相同来判断空满,而要考虑格雷码的对称性。
- 空:仍为wptr和rptr完全相等。
- 满:wptr和rptr的最高位和次高位都不同,而其余位都相同。
重点
因为在地址的同步逻辑中常使用打两拍来消除亚稳态,所以同步时常有延迟的现象,那么这会导致什么问题吗?
写快读慢的情况下- fifo_empty的产生:这时候从读端看进去,要将wptr同步到rptr,所以wptr可能慢于实际wptr,不满足读空的条件时没问题;当条件满足时,因为延迟的关系,实际可能wptr>=rptr,fifo实际有写入数据,但这写不影响使用,这种情况叫虚空。
- fifo_full的产生:这时候从写端看进去,要将rptr同步到wptr,所以rptr可能慢于实际wptr,满足写满的条件时,没有问题;当条件不满足时,由于延迟的关系,实际可能rptr>=wptr-fifo_depth,这时候信号可能显示为full,实际已经多读了,这种情况叫虚满
写慢读快的情况分析类似
最小深度FIFO的计算
- 如果是同步FIFO问题:
例如
考虑背靠背情况,最坏在160cycle中写入了160个数据,而这16个cycle只能读出16*8=128个数据,FIFO中还有32个数据,所以FIFO的最小深度为32.- 如果是异步FIFO,将上述情况改为
那么背靠背情况,80个cycle里写入了80个数据,而读侧在这些时间内只能读出
8*(80cylce/10cycle)*(100MHz/200MHz)=32
所以FIFO里至少还有48个数据
深度需要2^6 = 64
代码
后续补充
-
Verilog实现FIFO专题5-异步FIFO设计(异步FIFO工作方式、异步FIFO介绍、异步FIFO介绍)
2020-03-25 09:57:31FIFO根据输入输出时钟是否一致,分为同步FIFO与...本文以异步FIFO与同步FIFO的异同入手,在比较过程中逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,最终实现异步FIFO,并进行了仿真、调试、以及验证FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO。同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的不同步可能会导致亚稳态,导致FIFO工作异常,设计较为复杂;在之前的记录中,我们对同步FIFO的设计进行了分析:
此处我们不再对同步FIFO进行介绍而直接以异步FIFO与同步FIFO的异同为线索,逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,并进一步实现异步FIFO。
目录
一、异步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) begin if(!rst_n) dout <= {DW{1'bz}}; else if(!empty & re) dout <= ram[rp[AW-1:0]]; else dout <= dout; end
读指针逻辑:
clk_r来临,re有效,并且FIFO非空(empty=0)时,进行一次读操作,rp加1(即顺序读出),并进行格雷码转换,生成对应rp_g;
// 读指针 always@(posedge clk_r or negedge rst_n) begin if(!rst_n) rp <= {AW{1'b0}}; else if(!empty & re) rp <= rp+1'b1; else rp <= rp; end
写逻辑:
clk_w来临,we有效,并且FIFO不满(full=0)时,将din写入写指针wp对应地址处;
//写操作 always@(posedge clk_w) begin if(!full & we) ram[wp[AW-1:0]] <= din; else ram[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) begin if(!rst_n) wp <= {AW{1'b0}}; else if(!full & we) wp <= wp+1'b1; else wp <= wp; end
格雷码指针生成逻辑:
利用二进制与格雷码的转换关系,将二进制指针转换为格雷码指针,用于另一个时钟域的同步接收;
// 二进制指针转换为格雷指针 assign wp_g = (wp>>1) ^ wp; assign rp_g = (rp>>1) ^ rp;
格雷码指针同步逻辑:
读时钟域,同步写地址,用于空逻辑判断;写时钟域,同步读地址,用于满逻辑判断;
// 读时钟域,写地址同步 always@(posedge clk_r or negedge rst_n) begin if(!rst_n) begin wp_m <= {AW{1'b0}}; wp_s <= {AW{1'b0}}; end else begin wp_m <= wp_g; wp_s <= wp_m; end end // 写时钟域,读地址同步 always@(posedge clk_w or negedge rst_n) begin if(!rst_n) begin rp_m <= {AW{1'b0}}; rp_s <= {AW{1'b0}}; end else begin rp_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) begin if(!rst_n) rp <= {AW{1'b0}}; else if(!empty & re) rp <= rp+1'b1; else rp <= rp; end //写指针 always@(posedge clk_w or negedge rst_n) begin if(!rst_n) wp <= {AW{1'b0}}; else if(!full & we) wp <= wp+1'b1; else wp <= wp; end // 读操作 always@(posedge clk_r or negedge rst_n) begin if(!rst_n) dout <= {DW{1'bz}}; else if(!empty & re) dout <= ram[rp[AW-1:0]]; else dout <= dout; end //写操作 always@(posedge clk_w) begin if(!full & we) ram[wp[AW-1:0]] <= din; else ram[wp[AW-1:0]] <= ram[wp[AW-1:0]]; end // 读时钟域,写地址同步 always@(posedge clk_r or negedge rst_n) begin if(!rst_n) begin wp_m <= {AW{1'b0}}; wp_s <= {AW{1'b0}}; end else begin wp_m <= wp_g; wp_s <= wp_m; end end // 写时钟域,读地址同步 always@(posedge clk_w or negedge rst_n) begin if(!rst_n) begin rp_m <= {AW{1'b0}}; rp_s <= {AW{1'b0}}; end else begin rp_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 begin clk_r = 1'b0; forever #25 clk_r = ~clk_r; end initial begin clk_w = 1'b0; forever #10 clk_w = ~clk_w; end // initial begin rst_n = 1'b1; din = 1'b0; re = 1'b0; we = 1'b0; #50 rst_n = 1'b0; #50 rst_n = 1'b1; // only write we = 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中always@()语句双边沿触发(语法与综合的差异)
-
异步fifo
2020-06-27 11:25:34异步fifo 具体要求 异步fifo:输入输出速率不一样 用100mhz写入0-1023,并用10mhz读出 设计架构 Fifo是一种先进先出的缓存区只能顺序写入数据,顺序 读出数据,其数据地址由内部读写指针自动加 1 完成,不能像...项目名称
异步fifo
具体要求
异步fifo:输入输出速率不一样
用100mhz写入0-1023,并用10mhz读出
设计架构
Fifo是一种先进先出的缓存区只能顺序写入数据,顺序 读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可 以由地址线决定读取或写入某个指定的地址,读请求打开时,数据会自动被读出。
需要注意锁相环为高电平复位.
rdfull:读满信号,fifo满未读出时为真
rdempty:读空信号,fifo全读出时为真
wrfll:写满信号,fifo写满是为真
wrempty:写空信号,fifo没写任何数据时为真
fifo与ram和rom不同,数据读出之后fifo中就不存在数据,但是ram和rom中的数据被读出之后数据依然存在
代码设计
verilog代码设计
创建pll,生成100mhz和10mhz的时钟
创建fifo ipcore
顶层模块设计
module fifo_top( input clk, input rst_n, output [9:0] q ); //产生100m写时钟和10m的读时钟 wire clk_100m; wire clk_10m; wire locked; my_pll my_pll( .areset(!rst_n), .inclk0(clk), .c0(clk_100m), .c1(clk_10m), .locked(locked) ); //写fifo控制模块 wire wrfull; wire wrempty; wire wrrerq; wire [9:0] wrdata; wire rdfull; wire rdempty; wire rdreq; wr_fifo wr_fifo( .wrclk(clk_100m), .rst_n(locked), .wrfull(wrfull), .wrempty(wrempty), .wrrerq(wrrerq), .wrdata(wrdata) ); //fifo模块 my_fifo my_fifo( .data(wrdata), .rdclk(clk_10m), .rdreq(rdreq), .wrclk(clk_100m), .wrreq(wrrerq), .q(q), .rdempty(rdempty), .rdfull(rdfull), .wrempty(wrempty), .wrfull(wrfull) ); //读fifo控制模块 rd_fifo rd_fifo( .rdclk(clk_10m), .rst_n(locked), .rdfull(rdfull), .rdempty(rdempty), .rdreq(rdreq) ); endmodule
写fifo控制模块
module wr_fifo( input wrclk, input rst_n, input wrfull, input wrempty, output reg wrrerq, output reg[9:0] wrdata ); reg state; always@(posedge wrclk or negedge rst_n) if(!rst_n) begin state<=0; wrrerq<=0; wrdata<=10'd0; end else begin case(state) 0: begin if(wrempty) //fifo中没有写任何数据 begin wrrerq<=1; state<=1; end else begin wrrerq<=0; state<=0; wrdata<=10'd0; end end 1: begin if(wrfull) begin wrrerq<=0; wrdata<=10'd0; state<=0;//跳回原来状态,等待下一次写入 end else begin wrrerq<=1; wrdata<=wrdata+1'b1; end end default:; endcase end endmodule
读fifo控制模块
module rd_fifo( input rdclk, input rst_n, input rdfull, input rdempty, output reg rdreq ); reg state; always@(posedge rdclk or negedge rst_n) if(!rst_n) begin state<=0; rdreq<=0; end else begin case(state) 0: begin if(rdfull) begin state<=1; rdreq<=1; end else begin state<=0; rdreq<=0; end end 1: begin if(rdempty) begin state<=0; rdreq<=0; end else begin state<=1; rdreq<=1; end end default:; endcase end endmodule
仿真代码
`timescale 1ns/1ns module fifo_top_tb; reg clk; reg rst_n; wire [9:0] q; fifo_top fifo_top( .clk(clk), .rst_n(rst_n), . q(q) ); initial clk=0; always #10 clk=~clk; initial begin rst_n=0; #200; rst_n=1; end endmodule
仿真结果
出现写空信号时,打开写请求数据在下一个时钟上升沿开始写入,当写入数据之后,fifo中写空信号为低电平
fifo中写满之后,发出写满信号,写请求关闭
fifi中写满之后发出读满信号,读请求打开开始读数据
fifo数据读完之后,发出读空信号,读请求关闭。
-
异步fifo_异步FIFO
2020-11-21 15:35:49本文使用 Zhihu On VSCode 创作并发布概述异步FIFO用来在两个异步时钟域间传输数据。如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将...本文使用 Zhihu On VSCode 创作并发布
概述
异步FIFO用来在两个异步时钟域间传输数据。
如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将数据从FIFO中读出。“fifo_full” 和 “fifo_empty” 信号负责对上溢(overflow)和下溢(underflow)的控制,如果在“fifo_full”为高时,不应该再写入数据,否则会将之前写入的数据覆盖掉。当“fifo_empty” 信号为高时,不应该读取数据,否则会读出无效的数据。FIFO数据“先进先出”,先写入的数据先读取。
结构框图
异步FIFO结构如上图所示
- 第1部分是双口RAM,用于数据的存储。
- 第2部分是数据写入控制器
- 第3部分是数据读取控制器
- 读指针同步器
使用写时钟的两级触发器采集读指针,输出到数据写入控制器。 - 写指针同步器
使用读时钟的两级触发器采集写指针,输出到数据读取控制器。
数据读写控制器
读写指针
- 在同步FIFO中,通常使用二进制计数器实现读写指针。
例如深度为16的同步FIFO,将读写指针位宽设置为5,低四位用作RAM的地址,最高位用于判断FIFO满。
当读写指针相等时,表示写入的数据都被读取出来,此时FIFO为空。
当读写指针第四位相等,而最高位相反时,表示写指针比读指针多循环了一次,此时FIFO为满。
对于异步FIFO,需要将写指针从写时钟域同步到读时钟域,产生空信号,需要将读指针从读时钟域同步到写时钟域,产生满信号。上述结构框图中4,5部分使用两级同步器来减小跨时钟域产生亚稳态的概率。
由于需要同步多位宽的指针信号,如果指针继续使用二进制计数器,当指针从 11111 跳变到 00000 时,每以位数据都需要同步,产生亚稳态的概率会很高。5bit数据每一位都有可能采集出错,可能会将五位数据任何可能的组合采样并同步到新的时钟域中。
可以使用格雷码代替二进制数进行跨时钟域的传输。
格雷码的特点:- 由于具有相邻的两个数据只有一位数据不同的特性,最高位和最低位也只有一位数据不同,适合用于跨时钟域的传输,降低错误发生的概率。
- 只有使用n位格雷码表示 2^n 个数据,格雷码才具有上述特性。
格雷码和二进制数的转换:
二进制数转格雷码:wire [4:0] gray; wire [4:0] bin; assign gray = {bin[4], bin[4:1] ^ bin[3:0]}
格雷码转二进制数:
wire [4:0] gray; reg [4:0] bin; integer i; always @ (gray) begin bin[4]=Gry[4]; for(i=3;i>=0;i=i-1) bin[i]=bin[i+1]^gray[i]; end
我们使用二进制数器作为RAM地址信号,将其转换为格雷码进行跨时钟域传输。
空满信号产生
如上图所示,使用4位格雷码作为深度为8的FIFO的读写指针。
将格雷码转换成四位二进制数,使用二进制数低三位作为访问RAM的地址。- 与同步FIFO类似,当读写指针相等时,得出FIFO为空
- 当写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满。
同步器
使用移位寄存器实现同步器。
双口RAM设计与仿真
设计一个深度位宽可调的双口RAM,默认参数深度为16,位宽为8。
设计和测试文件见文章末尾。仿真波形:
- 连续写入16个数据:
- 连续读取16个数据:
- 同时写入、读取16个数据:
异步FIFO的设计与仿真
- 连续写入16个数据:
- 连续读取16个数据:
工程源码
链接:https://pan.baidu.com/s/1d7HPLnj1pjXMPgWE5epdvQ
提取码:pxgc -
异步fifo_异步FIFO设计
2020-12-18 22:46:20由于知乎编辑器对markdown支持性问题,本文为直接上传markdown文档,格式混乱且图像不全,格式完整版请移步个人博客异步FIFO设计qiankun214.github.io1.设计目标设计一个参数可配置的异步FIFO,要求:FIFO深度从4... -
异步fifo_备战秋招-异步FIFO
2020-12-08 00:32:16异步FIFO本文是对Clifford E. Cummings文章的粗浅翻译,如有错误,欢迎指出。1.0 摘要异步FIFO是一种FIFO设计,数据从一个时钟域进入到FIFO,在另一个时钟域读取数据,并且两个时钟域是异步。异步FIFO的用于将数据从... -
异步fifo_Gray Code异步FIFO的多种约束方式
2020-12-18 22:46:20大家都知道,在处理异步路径时,异步FIFO的使用,是最常见的处理方式。但是,针对于异步FIFO的约束问题,还是非常值得深究的。是否异步FIFO就是完全可以异步处理呢?接下来就跟大家一起分析一下。02 分类我们在实现... -
异步FIFO设计
2020-10-23 02:38:39异步FIFO设计_电子技术交流网本文介绍如何应用美国QUICKLOGIC公司的QUICKRAM器件设计高速、高可靠异步FIFO(Asynchronous FIFO)。 -
异步fifo_FIFO系列(三):fifo与格雷码以及异步fifo设计
2020-12-18 22:46:20本系列分为以下部分:1、FIFO深度计算2、同步fifo设计3、fifo与格雷码以及异步fifo设计计划分三次更新完毕,本次为本系列终结篇!本次增加异步FIFO设计,异常有趣!格雷码的相关知识关于同步fifo的设计疑惑了半天,... -
异步FIFO结构
2015-08-08 21:46:34异步FIFO -
异步fifo_关于异步fifo的快转慢的问题
2020-12-18 22:46:19最近一位童鞋跟我聊到一个异步fifo的问题,还是很有水准的。我贴在这里给大家看一下:异步fifo需要将读写的pointer作比较产生满和空信号,但是假如fifo的两个时钟域的clk相差特别大,pointer在做跨时钟域转换的时候... -
异步fifo深度 设计地址格雷码_异步FIFO
2020-12-31 09:35:30本文使用 Zhihu On VSCode 创作并发布概述异步FIFO用来在两个异步时钟域间传输数据。如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将... -
异步fifo深度 设计地址格雷码_异步FIFO设计
2021-01-16 22:54:03但是在大规模的数字电路设计中,多时钟域往往是不可避免的,这样就会产生了跨时钟域传输等问题,为了解决跨时钟域问题,我们常常采取的方法是握手协议和异步FIFO做数据缓冲区,异步FIFO既可以使相异时钟域数据传输的... -
异步fifo_跨时钟域同步(异步FIFO)
2020-12-13 11:10:09本文使用 Zhihu On VSCode 创作并发布跨时钟域同步(异步FIFO)之前学习了跨时钟域下的单bit信号同步的方法,这些单bit信号多是作为控制信号或者标志信号来使用,再实际的项目中,处理多bit数据也是十分常见的,即... -
异步fifo_IC 面试基础题 - 异步FIFO的设计
2020-12-18 22:46:19前言 | 鱼叔在面试的时候,经常会被问到异步FIFO的问题,之前准备的时候一知半解,现在有时间了于是便想好好整理这个设计,希望这篇文章能给予你启发。1. 相关资料Kevin Zhang:异步FIFO设计(1)zhuanlan.zhihu....