先进先出存储器 FIFO.docx

上传人:b****8 文档编号:10049338 上传时间:2023-02-08 格式:DOCX 页数:15 大小:21.99KB
下载 相关 举报
先进先出存储器 FIFO.docx_第1页
第1页 / 共15页
先进先出存储器 FIFO.docx_第2页
第2页 / 共15页
先进先出存储器 FIFO.docx_第3页
第3页 / 共15页
先进先出存储器 FIFO.docx_第4页
第4页 / 共15页
先进先出存储器 FIFO.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

先进先出存储器 FIFO.docx

《先进先出存储器 FIFO.docx》由会员分享,可在线阅读,更多相关《先进先出存储器 FIFO.docx(15页珍藏版)》请在冰豆网上搜索。

先进先出存储器 FIFO.docx

先进先出存储器FIFO

1.什么是FIFO?

   FIFO是英文FirstInFirstOut的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

2.什么情况下用FIFO?

   FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端时AD数据采集,另一端时计算机的PCI总线,假设其AD采集的速率为16位100KSPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。

另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

3.FIFO的一些重要参数

   FIFO的宽度:

也就是英文资料里常看到的THEWIDTH,它只的是FIFO一次读写操作的数据位,就像MCU有8位和16位,ARM32位等等,FIFO的宽度在单片成品IC中是固定的,也有可选择的,如果用FPGA自己实现一个FIFO,其数据位,也就是宽度是可以自己定义的。

   FIFO的深度:

THEDEEPTH,它指的是FIFO可以存储多少个N位的数据(如果宽度为N)。

如一个8位的FIFO,若深度为8,它可以存储8个8位的数据,深度为12,就可以存储12个8位的数据,FIFO的深度可大可小,个人认为FIFO深度的计算并无一个固定的公式。

在FIFO实际工作中,其数据的满/空标志可以控制数据的继续写入或读出。

在一个具体的应用中也不可能由一些参数算数精确的所需FIFO深度为多少,这在写速度大于读速度的理想状态下是可行的,但在实际中用到的FIFO深度往往要大于计算值。

一般来说根据电路的具体情况,在兼顾系统性能和FIFO成本的情况下估算一个大概的宽度和深度就可以了。

而对于写速度慢于读速度的应用,FIFO的深度要根据读出的数据结构和读出数据的由那些具体的要求来确定。

   满标志:

FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。

   空标志:

FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。

   读时钟:

读操作所遵循的时钟,在每个时钟沿来临时读数据。

   写时钟:

写操作所遵循的时钟,在每个时钟沿来临时写数据。

   读指针:

指向下一个读出地址。

读完后自动加1。

   写指针:

指向下一个要写入的地址的,写完自动加1。

   读写指针其实就是读写的地址,只不过这个地址不能任意选择,而是连续的。

4.FIFO的分类

   根均FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。

同步FIFO是指读时钟和写时钟为同一个时钟。

在时钟沿来临时同时发生读写操作。

异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

5.FIFO设计的难点

   FIFO设计的难点在于怎样判断FIFO的空/满状态。

为了保证数据正确的写入或读出,而不发生益处或读空的状态出现,必须保证FIFO在满的情况下,不能进行写操作。

在空的状态下不能进行读操作。

怎样判断FIFO的满/空就成了FIFO设计的核心问题。

由于同步FIFO几乎很少用到,这里只描述异步FIFO的空/满标志产生问题。

在用到触发器的设计中,不可避免的会遇到亚稳态的问题(关于亚稳态这里不作介绍,可查看相关资料)。

在涉及到触发器的电路中,亚稳态无法彻底消除,只能想办法将其发生的概率将到最低。

其中的一个方法就是使用格雷码。

格雷码在相邻的两个码元之间只由一位变换(二进制码在很多情况下是很多码元在同时变化)。

这就会避免计数器与时钟同步的时候发生亚稳态现象。

但是格雷码有个缺点就是只能定义2^n的深度,而不能像二进制码那样随意的定义FIFO的深度,因为格雷码必须循环一个2^n,否则就不能保证两个相邻码元之间相差一位的条件,因此也就不是真正的各雷码了。

第二就是使用冗余的触发器,假设一个触发器发生亚稳态的概率为P,那么两个及联的触发器发生亚稳态的概率就为P的平方。

但这回导致延时的增加。

亚稳态的发生会使得FIFO出现错误,读/写时钟采样的地址指针会与真实的值之间不同,这就导致写入或读出的地址错误。

由于考虑延时的作用,空/满标志的产生并不一定出现在FIFO真的空/满时才出现。

可能FIFO还未空/满时就出现了空/满标志。

这并没有什么不好,只要保证FIFO不出现overfloworunderflow就OK了。

   很多关于FIFO的文章其实讨论的都是空/满标志的不同算法问题。

   在VijayA.Nebhrajani的《异步FIFO结构》一文中,作者提出了两个关于FIFO空/满标志的算法。

   第一个算法:

构造一个指针宽度为N+1,深度为2^N字节的FIFO(为便方比较将格雷码指针转换为二进制指针)。

当指针的二进制码中最高位不一致而其它N位都相等时,FIFO为满(在CliffordE.Cummings的文章中以格雷码表示是前两位均不相同,而后两位LSB相同为满,这与换成二进制表示的MSB不同其他相同为满是一样的)。

当指针完全相等时,FIFO为空。

这也许不容易看出,举个例子说明一下:

一个深度为8字节的FIFO怎样工作(使用已转换为二进制的指针)。

FIFO_WIDTH=8,FIFO_DEPTH=2^N=8,N=3,指针宽度为N+1=4。

起初rd_ptr_bin和wr_ptr_bin均为“0000”。

此时FIFO中写入8个字节的数据。

wr_ptr_bin=“1000”,rd_ptr_bin=“0000”。

当然,这就是满条件。

现在,假设执行了8次的读操作,使得rd_ptr_bin=“1000”,这就是空条件。

另外的8次写操作将使wr_ptr_bin等于“0000”,但rd_ptr_bin仍然等于“1000”,因此FIFO为满条件。

   显然起始指针无需为“0000”。

假设它为“0100”,并且FIFO为空,那么8个字节会使wr_ptr_bin=“1100”,,rd_ptr_bin仍然为“0100”。

这又说明FIFO为满。

   在VijayA.Nebhrajani的这篇《异步FIFO结构》文章中说明了怎样运用格雷码来设置空满的条件,但没有说清为什么深度为8的FIFO其读写指针要用3+1位的格雷码来实现,而3+1位的格雷码可以表示16位的深度,而真实的FIFO只有8位,这是怎么回事?

而这个问题在CliffordE.Cummings的文章中得以解释。

三位格雷码可表示8位的深度,若在加一位最为MSB,则这一位加其他三位组成的格雷码并不代表新的地址,也就是说格雷码的0100表示表示7,而1100仍然表示7,只不过格雷码在经过一个以0位MSB的循环后进入一个以1为MSB的循环,然后又进入一个以0位MSB的循环,其他的三位码仍然是格雷码,但这就带来一个问题,在0100的循环完成后,进入1000,他们之间有两位发生了变换,而不是1位,所以增加一位MSB的做法使得该码在两处:

0100~1000,1100~0000有两位码元发生变化,故该码以不是真正的格雷码。

增加的MSB是为了实现空满标志的计算。

VijayA.Nebhrajani的文章用格雷码转二进制,再转格雷码的情况下提出空满条件,仅过两次转换,而CliffordE.Cummings的文章中直接在格雷码条件下得出空满条件。

其实二者是一样的,只是实现方式不同罢了。

   第二种算法:

CliffordE.Cummings的文章中提到的STYLE#2。

它将FIFO地址分成了4部分,每部分分别用高两位的MSB00、01、11、10决定FIFO是否为goingfull或goingempty(即将满或空)。

如果写指针的高两位MSB小于读指针的高两位MSB则FIFO为“几乎满”,

   若写指针的高两位MSB大于读指针的高两位MSB则FIFO为“几乎空”。

   在VijayA.Nebhrajani的《异步FIFO结构》第三部分的文章中也提到了一种方法,那就是方向标志与门限。

设定了FIFO容量的75%作为上限,设定FIFO容量的25%为下限。

当方向标志超过门限便输出满/空标志,这与CliffordE.Cummings的文章中提到的STYLE#2可谓是异曲同工。

他们都属于保守的空满判断。

其实这时输出空满标志FIFO并不一定真的空/满。

   说到此,我们已经清楚地看到,FIFO设计最关键的就是产生空/满标志的算法的不同产生了不同的FIFO。

但无论是精确的空满还是保守的空满都是为了保证FIFO工作的可靠。

6.关于FIFO的一点的思考

   关于FIFO丢数据的问题,其实各位对同一个问题的理解有偏差,才造成了相互误解。

如果在理想状况下(时钟同步不回出现错码),FIFO由读写指针控制是不会丢数的,(这不是废话吗,现实中哪来的理想状况!

)且慢,我的意思是说丢数据并不是读写谁快谁慢造成的,在正确的设置空满标志算法的情况下,数据overflow和underflow是不会发生的。

而往往现实中因为亚稳态的存在,才出现了丢数的情况,也就是说是只要读写时钟不同步,在采样的过程中采样出错,使得本该是0100的变成了1101等等,就会出现读写的错误,我们称其为丢数,其原因就是在时钟同步指针的时候出现亚稳态,由于二进制码加1的时候很多位同时变化,所以很容易出现亚稳态。

因此才用格雷码将此问题发生的概率比降到最小,其次用多余的触发器使其概率进一步降低,也就是说错误难免,但我们可以将其发生的概率降到最低,并且在出现错误时也不会错的态离谱(详见VijayA.Nebhrajani的《异步FIFO结构》第二篇)。

   二进制码指针并非不好用,在前面也提到了它有自身的优势,由于通过设置握手信号,指针可以有多位同时变化,二进制指针每次移动可以跳跃过任意的长度,这样给FIFO的某些功能的实现带来了方便(例如,硬件直接控制FIFO从缓存的数据流中丢弃一个出错的包);而格雷码指针一般只能做递增或递减的移动。

设置握手信号虽然可以保证指针不出错,但这样你来我往的经过三四个回合才能开始传数据,所以对于高速的场合就不适用了。

 

异步FIFO的Verilog代码之一

这个是基于RAM的异步FIFO代码,个人认为代码结构简单易懂,非常适合于考试中填写。

与之前的用RAM实现的同步FIFO的程序相比,异步更为复杂。

增加了读写控制信号的跨时钟域的同步。

此外,判空与判满的也稍有不同。

modulefifo1(rdata,wfull,rempty,wdata,winc,wclk,wrst_n,rinc,rclk,rrst_n);

parameterDSIZE=8;

parameterASIZE=4;

output[DSIZE-1:

0]rdata;

outputwfull;

outputrempty;

input[DSIZE-1:

0]wdata;

inputwinc,wclk,wrst_n;

inputrinc,rclk,rrst_n;

regwfull,rempty;

reg[ASIZE:

0]wptr,rptr,wq2_rptr,rq2_wptr,wq1_rptr,rq1_wptr;

reg[ASIZE:

0]rbin,wbin;

reg[DSIZE-1:

0]mem[0:

(1<

wire[ASIZE-1:

0]waddr,raddr;

wire[ASIZE:

0]rgraynext,rbinnext,wgraynext,wbinnext;

wirerempty_val,wfull_val;

//-----------------双口RAM存储器--------------------

assignrdata=mem[raddr];

always@(posedgewclk)

if(winc&&!

wfull)mem[waddr]<=wdata;

//-------------同步rptr指针-------------------------

always@(posedgewclkornegedgewrst_n)

if(!

wrst_n){wq2_rptr,wq1_rptr}<=0;

else{wq2_rptr,wq1_rptr}<={wq1_rptr,rptr};

//-------------同步wptr指针---------------------------

always@(posedgerclkornegedgerrst_n)

if(!

rrst_n){rq2_wptr,rq1_wptr}<=0;

else{rq2_wptr,rq1_wptr}<={rq1_wptr,wptr};

//-------------rempty产生与raddr产生-------------------

always@(posedgerclkornegedgerrst_n)//GRAYSTYLE2pointer

begin

if(!

rrst_n){rbin,rptr}<=0;

else{rbin,rptr}<={rbinnext,rgraynext};

end

//Memoryread-addresspointer(okaytousebinarytoaddressmemory)

assignraddr=rbin[ASIZE-1:

0];

assignrbinnext=rbin+(rinc&~rempty);

assignrgraynext=(rbinnext>>1)^rbinnext;

//FIFOemptywhenthenextrptr==synchronizedwptroronreset 

assignrempty_val=(rgraynext==rq2_wptr);

always@(posedgerclkornegedgerrst_n)

begin

if(!

rrst_n)rempty<=1'b1;

elserempty<=rempty_val;

end

//---------------wfull产生与waddr产生------------------------------

always@(posedgewclkornegedgewrst_n)//GRAYSTYLE2pointer

if(!

wrst_n){wbin,wptr}<=0;

else{wbin,wptr}<={wbinnext,wgraynext};

//Memorywrite-addresspointer(okaytousebinarytoaddressmemory)

assignwaddr=wbin[ASIZE-1:

0];

assignwbinnext=wbin+(winc&~wfull);

assignwgraynext=(wbinnext>>1)^wbinnext;

assignwfull_val=(wgraynext=={~wq2_rptr[ASIZE:

ASIZE-1],wq2_rptr[ASIZE-2:

0]});//:

ASIZE-1]

always@(posedgewclkornegedgewrst_n)

if(!

wrst_n)wfull<=1'b0;

elsewfull<=wfull_val;

endmodule

对于FIFO,读写指针都指向一个内存的初始位置,每进行一次读写操作,相应的指针就递增一次,指向下一个内存位置。

当指针移动到了内存的最后一个位置时,它又重新跳回初始位置。

在FIFO非满或非空的情况下,这个过程将随着读写控制信号的变化一直进行下去。

如果FIFO处于空的状态,下一个读动作将会导致向下溢(underflow),一个无效的数据被读人;同样,对于一个满了的FIFO,进行写动作将会导致向上溢出(overflow),一个有用的数据被新写入的数据覆盖。

这两种情况都属于误动作,因此需要设置满和空两个信号,对满信号置位表示FIFO处于满状态,对满信号复位表示FIFO非满,还有空间可以写入数据;对空信号置位表示FIFO处于空状态,对空信号复位表示FIFO非空,还有有效的数据可以读出。

FIFO存储器的设计

modulefifo(clk,rstp,din,writep,readp,dout,emptyp,fullp);

inputclk;

inputrstp;//复位信号

input[15:

0]din;

inputreadp;//读信号

inputwritep;//写信号

output[15:

0]dout;

outputemptyp;//空标志

outputfullp;//满标志

parameterDEPTH=2,

MAX_COUNT=2‘b11;//定义地址最大值

regemptyp;

regfullp;

reg[15:

0]dout;

reg[(DEPTH-1):

0]tail;//定义读指针

reg[(DEPTH-1):

0]head;//定义写指针

//定义计数器

reg[(DEPTH-1):

0]count;

reg[15:

0]fifomem[0:

MAX_COUNT];//定义fifomem存储器有4个16位的存储器

//dout被赋给tail指向的值

 

always@(posedgeclk)begin

if(rstp==1)begin

dout<=16‘h0000;//复位信号有效置0

end

elsebegin

dout<=fifomem[tail];//将fifomem中第tail个单元赋给dout

end

end

always@(posedgeclk)begin

if(rstp==1'b0&&writep==1'b1&&fullp==1'b0)begin

fifomem[head]<=din;//写入

end

end

always@(posedgeclk)begin

if(rstp==1'b1)begin

head<=2‘b00;//复位

end

elsebegin

if(writep==1'b1&&fullp==1'b0)begin

head<=head+1;

end

end

end

always@(posedgeclk)begin

if(rstp==1'b1)begin

tail<=2‘b00;//复位

end

elsebegin

if(readp==1'b1&&emptyp==1'b0)begin

tail<=tail+1;

end

end

end

always@(posedgeclk)begin

if(rstp==1'b1)begin

count<=2'b00;

end

elsebegin

case({readp,writep})

2'b00:

count<=count;

2'b01:

if(count!

=MAX_COUNT)

count<=count+1;//为写状态时计数器进行加法计数

2'b10:

if(count!

=2'b00)

count<=count-1;//为读状态计数器进行减法计数

2'b11:

count<=count;

endcase

end

end

always@(count)begin

if(count==2'b00)

emptyp<=1‘b1;//count为0时emptyp赋为1

else

emptyp<=1'b0;

end

 

always@(count)begin

if(count==MAX_COUNT)

fullp<=1‘b1;//计数到最大时fullp赋为1

else

fullp<=1'b0;

end

endmodule

测试程序:

moduletest_fifo;

regclk;

regrstp;

reg[15:

0]din;

regreadp;

regwritep;

wire[15:

0]dout;

wireemptyp;

wirefullp;

reg[15:

0]value;

fifoU1(.clk(clk),.rstp(rstp),.din(din),.readp(readp),.writep(writep),.dout(dout),

.emptyp(emptyp),.fullp(fullp));

 

taskread_word;

begin

@(negedgeclk);

readp=1;

@(posedgeclk)#5;

readp=0;

end

endtask

taskwrite_word;

input[15:

0]value;

begin

@(negedgeclk);

din=value;

writep=1;

@(posedgeclk);

#5;

din=16'hzzzz;

writep=0;

end

endtask

initialbegin

clk=0;

foreverbegin

#10clk=1;

#10clk=0;

end

end

initialbegin

//test1;

test2;//调用测试模块2

end

tasktest1;

begin

din=16'hzzzz;

writep=0;

readp=0;

rstp=1;

#50rstp=0;

#50;

write_word(16'h1111);

write_word(16'h2222);

write_word(16‘h3333);//写入3个数据

read_word;

read_word;//读两个

write_word(16‘h4444);//

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 家庭教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1