异步FIFO设计文档格式.docx
《异步FIFO设计文档格式.docx》由会员分享,可在线阅读,更多相关《异步FIFO设计文档格式.docx(16页珍藏版)》请在冰豆网上搜索。
rami1(.wdata(wdata),//读写存储模块
.rdata(rdata),
.waddr(wptr),//地址与指针同步
.raddr(rptr),//
.wrep(wrep),
.wclk(wclk));
async_cmpi2(.aempty_n(aempty_n),//异步比较读写指针产生异步空满标志
.afull_n(afull_n),
.wptr(wptr),
.rptr(rptr),
.wrst_n(wrst_n));
rptr_empty2i3(.rempty(rempty),//根据rclk产生读指针rptr和空标志rempty
.aempty_n(aempty_n),
.rrep(rrep),
.rclk(rclk),
.rrst_n(rrst_n));
wptr_full2i4(.wfull(wfull),//根据wclk产生写指针wptr和满标志wfull
.wclk(wclk),
endmodule
顶层模块图:
其中
2.1读写地址产生逻辑(本设计中读写地址与读写指针同步)
读写地址线一般有多位,如果在不同的时钟域内直接同步二进制码的地址指针,则有可能产生亚稳态。
例如,读指针从011变化到100时,所有位都要变化,读指针的每一位在读时钟的作用下,跳变不一致,即产生毛刺。
如果写时钟恰好在读指针的变化时刻采样,得到的采样信号可能是000~111中的任何一个,从而导致空/满信号判断错误。
由实践可知,同步多个异步输入信号出现亚稳态的概率远远大于同步一个异步信号的概率[3]。
解决这一问题的有效方法是采用格雷码。
格雷码的主要特点是相邻的两个编码之间只有一位变化。
图2是格雷码产生的逻辑框图。
在读使能或写使能信号有效、并且空/满标志无效的情况下,读写指针开始累加,进行FIFO读或写操作。
二进制码与格雷码的转换是一个“异或”运算:
gnext=(bnext>
>
1)^bnext。
格雷码gnext经寄存器输出格雷码指针ptr。
这种方法采用了两组寄存器,虽然面积较大,但是有助于提高系统的工作频率。
图2
always@(posedgerclkornegedgerrst_n)
begin
if(!
rrst_n)
begin
rbin<
=0;
rptr<
end
else
=rbnext;
=rgnext;
end
assignrbnext=!
rempty?
rbin+rrep:
rbin;
assignrgnext=(rbnext>
1)^rbnext;
//二进制码转换成格雷码
always@(posedgewclkornegedgewrst_n)
wrst_n)
wbin<
wptr<
=wbnext;
=wgnext;
assignwbnext=!
wfull?
wbin+wrep:
wbin;
assignwgnext=(wbnext>
1)^wbnext;
2.2空/满标志产生逻辑
正确地产生空/满标志是设计任何类型FIFO的关键点。
空/满标志产生的原则是:
写满而不溢出,能读空而不多读。
传统的异步FIFO把读写地址信号同步后再进行同步比较以产生空满标志,由于读写地址的每一位都需要两级同步电路,大量使用寄存器必然要占用很大的面积。
这种方法不适合设计大容量的FIFO。
当读、写指针相等也就是指向同一个内存位置时,FIFO可能处于满或空两种状态,必须区分FIFO是处于空状态还是满状态。
传统的做法是把读、写地址寄存器扩展一位,最高位设为状态位,其余低位作为地址位。
当读写指针的地址位和状态位全部吻合时,FIFO处于空状态;
当读写指针的地址位相同而状态位相反时,FIFO处于满状态。
传统的异步FIFO工作频率低、面积大。
下面将介绍一种产生空/满标志的新方法。
采用象限比较法,先确定当前读写状态是快接近满了,还是快要接近空了。
取读指针rptr和写指针wptr的最高两位,按其次序分为4个象限,显然当写指针滞后一个象限,并且当写使能,即复位信号wrst_n为1时,FIFO快要满了,在程序中用close_full_n(dirset)表示,当写指针超前一个象限时,FIFO快要空了,在程序中用close_empty_n(dirrst)表示。
如图3所示:
图3
图4
用一个寄存器direction来寄存当前是否接近满空的状态,快接近满了则令direction=1;
快接近空了则令direction=0;
由于空/满标志产生的原则为:
写满而不溢出,能读空而不多读,所以在其它情况下令direction=1,这样保证满标志不会出错,不会在快满时误以为未满而继续向FIFO写数据造成有效数据被覆盖。
最后再由读写指针是否相同和direction一起来判断当前状态是满afull_n还是空aempty_n。
整个满空标志位的判断过程都使用组合逻辑,这样虽然读写时钟不一样,但是在读写指针发生变化时可以马上判断当前满空状态,之后再在读写时钟下清除写满wfull和读空rempty标志位,写满wfull要同步到写时钟域,而读空rempty要同步到读时钟域。
写满wfull在afull_n变低时有效为1,在afull_n变高后的下一个时钟清除为0。
读空rempty在aempty_n变低时有效为1,在afull_n变高后的下一个时钟清除为0。
根据图4可得:
wireclose_full_n=~((wptr[n]^rptr[n-1])&
~(wptr[n-1]^rptr[n]));
wireclose_empty_n=~((~(wptr[n]^rptr[n-1])&
(wptr[n-1]^rptr[n]))|~wrst);
always@(posedgehighornegedgedirsetornegedgedirrst)
if(!
close_empty_n)direction<
=1′b0;
elseif(!
close_full_n)direction<
=1′b1;
elsedirection<
=high;
assignaempty=~((wptr==rptr)&
&
!
direction);
assignafull=~((wptr==rptr)&
direction);
always@(posedgerclkornegedgeaempty)
aempty){rempty,rempty2}<
=2′b11;
else{rempty,rempty2}<
={rempty2,~aempty};
always@(posedgewclkornegedgeafull)
afull){wfull,wfull2}<
else{wfull,wfull2}<
={wfull2,~afull};
2.3保守的空/满标志
设计中FIFO空/满标志的设置是保守的,即FIFO空/满标志的置位是立即有效的,而其失效则是在一段时间之后。
例如一旦读指针追上写指针,就会立即声明一个低电平有效的异步空信号aempty。
此信号会立即把图6所示的set触发器置位,使触发器输出为1,即向外部输出同步的空信号rempty,并且保证了FIFO一旦为空,读指针就不增加,避免了FIFO的读溢出。
当写地址增加时,表明FIFO已经非空,空标志aempty由低变高,此时可以进行安全的读操作。
aempty信号的失效与写时钟同步。
空信号rempty是在读时钟域中同步aempty信号得到的。
由于同步器使用了两个触发器,因此空信号rempty的失效要经过至少两个时钟周期的延迟。
所以,空信号的声明是及时的,而空信号的失效是保守的。
也就是说,虽然FIFO已经非空了,但是空信号rempty要经过几个周期的延迟才能变为无效。
满信号也有类似的情况。
虽然空/满标志的设置是保守的,但这并不影响FIFO功能的正确性,经验证保守的空/满标志能够满足FIFO的设计要求。
具体的各个模块程序如下:
读写存储模块RAM:
//异步FIFO缓存16*8数据,即数据宽度为8,深度为16//
moduleram(wclk,wrep,wdata,waddr,raddr,rdata);
inputwclk,wrep;
//读时钟,读使能
input[7:
//写数据8位
input[3:
//读写地址
reg[7:
0]fifomem[0:
3];
//du
assign
rdata=fifomem[raddr];
//xie
always@(posedgewclk)
if(wrep)
fifomem[waddr]<
=wdata;
ram模块图如下:
异步比较读写指针产生异步空满标志async_cmp模块
moduleasync_cmp(aempty_n,afull_n,wptr,rptr,wrst_n);
//异步比较产生空满信号
//parameteraddrsize=4;
//parameterN=addrsize-1;
//地址位宽
inputwrst_n;
//写复位
input[3:
//读写指针
outputaempty_n,afull_n;
//空满标志
regdirection;
//用一个寄存器direction来寄存当前是否接近满空的状态
wireclose_full_direction_n;
//快接近满了,低电平有效
wireclose_empty_direction_n;
//快接近空了,低电平有效
assignclose_full_direction_n=~((wptr[3]^rptr[3-1])&
~(wptr[3-1]^rptr[3]));
assignclose_empty_direction_n=~((~(wptr[3]^rptr[3-1])&
(wptr[3-1]^rptr[3]))|~wrst_n);
always@(negedgeclose_full_direction_nornegedgeclose_empty_direction_n)//低电平有效,用来判断direction
close_full_direction_n)
direction<
=1'
b1;
//快接近满了则令direction=1;
elseif(!
close_empty_direction_n)
b0;
//快接近空了则令direction=0;
////所以在其它情况下令direction=1
assignaempty_n=~((wptr==rptr)&
!
//几乎空信号产生
assignafull_n=~((wptr==rptr)&
//几乎满信号产生
async_cmp模块图如下:
根据rclk产生读指针rptr和空标志rempty
,rptr_empty2模块
modulerptr_empty2(rempty,rptr,aempty_n,rrep,rclk,rrst_n);
inputrrep,rclk,rrst_n;
//读时钟,读使能,读复位
inputaempty_n;
//空标志
output[3:
0]rptr;
//读指针
outputrempty;
//读空标志
reg[3:
0]rptr,rbin;
//读指针,读地址二进制
regrempty,rempty2;
wire[3:
0]rgnext,rbnext;
//读格雷码地址,读二进制地址
always@(posedgerclkornegedgerrst_n)
always@(posedgerclkornegedgeaempty_n)//读空标志的产生。
用格雷码表示
aempty_n)
{rempty,rempty2}<
=2'
b11;
={rempty2,~aempty_n};
rptr_empty2模块图:
根据wclk产生写指针wptr和满标志wfull,wptr_full2模块
modulewptr_full2(wfull,wptr,afull_n,wrep,wclk,wrst_n);
inputwrep,wclk,wrst_n;
//写时钟,写使能,写复位
inputafull_n;
//满标志
0]wptr;
//写指针
outputwfull;
//写满标志
0]wptr,wbin;
//写指针,写地址二进制
regwfull,wfull2;
0]wgnext,wbnext;
//写格雷码地址,写二进制地址
always@(posedgewclkornegedgeafull_n)//写满标志的产生。
afull_n)
{wfull,wfull2}<
={wfull2,~afull_n};
wptr_full2模块图如下:
测试程序:
moduleyibufifo_tb;
//parameterDSIZE=8;
//parameterASIZE=4;
0]rdata;
wirewfull;
wirerempty;
reg[7:
0]wdata;
regwrep,wclk,wrst_n;
regrrep,rclk,rrst_n;
initialbegin
wclk=0;
rclk=0;
wrst_n=0;
rrst_n=0;
wrep=1;
rrep=0;
wdata=0;
#30;
wrst_n=1;
rrst_n=1;
#300;
wrep=0;
rrep=1;
always#5wclk=!
wclk;
//10ns
always#10rclk=!
rclk;
//20ns
always@(negedgewclk)
wdata<
=wdata+1;
yibufifoi1(
.rdata
(rdata
),
.wfull
(wfull
.rempty
(rempty
.wdata
(wdata
.wrep
(wrep
.wclk
(wclk
.wrst_n
(wrst_n
.rrep
(rrep
.rclk
(rclk
.rrst_n
(rrst_n
)
);
在modelsim中的仿真结果如下: