参考文献:
[1]彭莉, 秦建业, 付宇卓. 异步FIFO的设计与验证[J]. 计算机工程与应用, 2005, 41(3):4.
[2]魏芳, 刘志军, 马克杰. 基于Verilog HDL的异步FIFO设计与实现[J]. 电子技术应用, 2006, 32(7):4.
FIFO简介:一种跨时钟域传输信号的方法,读写操作由不同的时钟产生,对空或满的判断是跨时钟域的。
背景:
现代的IC芯片包含丰富的触发器,不同电路的时钟驱动源存在频率和相位的差异,因而需要对跨不同时钟域进行异步信号传输。
异步FIFO法是跨越异步时钟边界传输数据的一种常用方法!
首先需要简单复习一下亚稳态,亚稳态问题是异步数据传输的主要问题。
首先需要明确建立时间(Setup Time)和保持时间(Hold Time)的概念:我们知道,针对一个使用上升沿触发的触发器来说,在上升沿到来之前,为了获得稳定的输出,触发器的输入数据需要稳定一段时间,而需要稳定的最小时间就是建立时间;而在上升沿到来之后,需要保持一段时间,需要保持时间的最小值就是保持时间。(针对建立时间和保持时间以及亚稳态的概念,触发器具体变化过程之后有时间再整理一下)
原理如图:
如果无法保证上述条件,触发器将会工作在一个不确定的状态,就成为亚稳态。
传统异步FIFO结构与设计
异步FIFO的FIFO是“First in,first out.”的缩写,即先进先出,常使用在产生数据的接口部分用于存储、缓存数据。异步FIFO是一种不同时钟域之间传递数据的常用方法,而时钟周期和相位完全独立,在异步FIFO设计中的两个最大难题是——避免亚稳态问题和空满信号的产生。
异步FIFO结构:
双端口存储器(Dual Port RAM)、写地址(wptr)产生逻辑、读地址(rptr)产生逻辑及空满标志(wfull、rempty)产生逻辑。
读写时钟是不同的时钟域,而通常空满信号是在写地址和读地址的相互比较下得出的。
设计方案:
如图2就是一种常见的设计方案,而在实际设计中有一些方法可以尽量减少亚稳态。
(1)格雷码
对读写地址采用格雷码。同步多个异步信号出现亚稳态的概率远远大于同步一个异步信号的概率。由于格雷码每次只变化一位,采用格雷码可以有效的减少亚稳态的产生。
(2)两级同步电路
采用触发器来同步异步输入信号,如图4,将a时钟域的数据adata,用两级寄存器同步,a2bdata是属于b时钟域的。这种方法可以将出现亚稳态的概率减低很多。但需要注意输入信号的时延问题。
异步FIFO设计改进
在传统设计中,空满信号是由读写地址先两级同步后再比较产生,每一位都需要两级同步。若设计需要读写地址为4位的寄存器(wptr[3:0],rptr[3:0]),则需要4X2X2=16个寄存器。若先比较读写地址产生空满信号,后同步空满信号的方法。则需要2X2=4个寄存器,极大地减少了设计面积。
空满标志产生逻辑(关键!):
产生原则:写满不溢出,读空不多读。无论在什么时刻都不能出现读写地址同时对一个存储器地址操作。空满标志满足:
空标志:(|写地址-读地址|<=预定值)&&(写地址超前读地址)-- 写
满标志:(|写地址-读地址|<=预定值)&&(读地址超前写地址)-- 读
若采用减法会需要一个很大的组合逻辑,一般只采用相等不相等的比较逻辑,避免使用减法器,再需要一个控制信号来区分当读写地址相等时是空还是满。
首先通过比较得到两个异步空/满标志信号(aempty/afull),送入读写模块进行同步,输出rempty/wfull的空/满信号。(rempty意味着空,不能读操作;wfull,满,不写)
在参考文献【1】中,空满信号标志由首位确定;在参考文献【2】的方法中,空满标志由首位和direction(判断是方向)决定(常用,例题所用方法)。
方法【2】如图:
以16X16异步FIFO为例,写地址wptr[4:0],读地址rptr[4:0],低三位为真正读写位,最高位区分空满。若读写地址完全相等表明读地址追上了写地址,则FIFO为空;若最高位相反但其余位相同,表明写地址领先读地址一个周期,FIFO为满。(两个地址完全相等,为空;首位相反其余相等,为满)
以写地址为例:
parameter n =4;
reg[n:0] wbin,wbnext; //写地址
reg[n:0] wptr,wgnext;
always @(posedge wclk or negedge wrst_n)
if(!wrst_n)begin //异步低电平置零
wbin<=0;
wptr[n-1:0]<=0;
end
else begin
wbin<=wbnext;
wptr[n-1:0]<=wgnext[n-1:0];
end
always @(wbin[n]) //当wbin首位发生改变时,对wptr赋值一次
wptr[n]=wbin[n];
assgin wbnext = !full? wbin+winc:wbin; //写地址格雷码加法器,不为满时加
assign wgnext[n-1:0] = (wbnext[n-1:0]>>1)^wbnext[n-1:0];
将空标志aempty_n同步到读时钟域控制异步FIFO读操作,满标志afull_n同步到读时钟域异步FIFO写操作。代码如下:
wire aempty_n = ~(wptr==rptr);
//按文中的意思,读写地址完全相同时取0,此时为空,所以aempty_n应该时低电平有效,为写状态
wire afull_n = ~((wptr[n]!==rptr[n])&&(rptr[n-1]==wptr[n-1]:0))
// 首位不同且其余位相同时,full_n取0,为满,读状态。
//读操作,按文章意思应该是将空信号到同步读操作
always @(posedge rclk or negedge rrst_n or negedge aempty_n)
if(!rrst_n)
{rempty,rempty2}<=2'b11; //空,不可读,清除数据
else if(!aempty_n)
{rempty,rempty2}<=2'b11;// 空,不可读
else
{rempty,rempty2}<={rempty2,~rempty_n};
//写操作
always @(posedge rclk or negedge wrst_n or negedge afull_n)
if(!wrst_n)
{wfull,wfull2}<=2'b00; //异步置零,清除数据,wfull为0时,不满,可以写
else if(!afull_n) //afull_n有效时,为满,写地址为满,不能再继续写入
{wfull,wfull2}<=2'b11;
else
{wfull,wfull2}<={wfull2,~afull_n}; //不满时。继续写入
//这一段代码还不是太理解,主要是两个always模块不理解,之后再好好看看
改进方案分析与总结
(1)空满信号标志无毛刺:由于格雷码,使得rptr[n-1:0]==wptr[n-1:0]是一个稳定的信号。
(2)同步电路不会造成FIFO溢出:
afull_n是因为写地址追上了读地址并领先了一个周期,此时“满”,不能再写数据,否则会造成写溢出。当读地址增加时,读走了一个数据,afull_n跳变为1,无效。可见,afull_n由1到0是由wclk写时钟同步的,由0到1是读时钟rclk同步的。afull_n只影响FIFO的写入,所以同步到写时钟域。
而当afull_n变高无效时,表明FIFO非满,可以进行写操作,且与读操作同步。但写信号无法及时采到(因为 {wfull,wfull2}<={wfull2,~afull_n};,wfull取到0时才能写,会延后至少两个周期),FIFO的写要至少晚两个周期。
空信号类似,也不会出现读溢出。
//后续再找一道例题看看。
好好学习天天向上!