异步FIFO的基本概念很简单 。对于写操作,是在写信号下产生累加的写地址Write_Pointer,用这个地址往存储器写数。在读信号下产生累加的读地址Read_Pointer,用这个地址从存储器取数据,原则就是first in first out。
FIFO的核心难点是:满和空的产生。对于写操作,得判断FIFO是不是满状态,满了就不能继续往里面写数据,不然就会覆盖还没取走的数据。对于读操作,得判断FIFO是不是空状态,空了就不能接着取数据,不然旧的数据会被取多次。满和空的产生,是拿读和写的Pointer做比较得到的。问题在于:写操作下的Write_Pointer和读操作下的Read_Pointer属于异步时钟的信号。两个异步时钟的信号进行处理,需要同步到同一个时钟域。一般把读指针同步到写时钟域,写时钟域下的写指针和同步读指针作比较,以生成“满”FULL信号;把写指针同步到读时钟域,读时钟域下的读指针则和同步写指针作比较,以生成“空”EMPTY信号。多比特的读写指针一般采用格雷码编码后进行同步处理后,通过简单的双触发器进行同步操作,可以极大减少传输出现亚稳态而导致错误的概率。
将WR_Pointer通过CDC同步之后,送到读时钟域,得到WR_Pointer_syn,然后再将其和RD_Pointer作比较,就可以得到“空”信号,下面的例子是FIFO深度为16,Pointer拓展1bit ,总计5bit,且通过Gray码跨时钟域后需要转换成Binary码后才能比较。
assign Empty = (WR_Pointer_syn[4:0]== RD_Pointer[4:0]);
将RD_Pointer通过CDC同步之后,送到写时钟域,得到RD_Pointer_syn,然后将其和WR_Pointer作比较,就可以得到满信号:
assign Full = (WR_Ponter[4] != RD_Pointer_syn[4]) && (WR_Ponter[3:0] == RD_Pointer_syn[3:0]);
上面的空满判断逻辑得到的满FULL和空EMPTY其实是假满空或者叫做虚满空,并非真正的满空。原因在于读写指针的CDC同步本身也是需要开销的,一般简单的两级同步器需要目标时钟域两个CYCLE。当我们判断满FULL信号的时候,我们用的是WR_Pointer和同步过来的RD_Pointer_syn做的比较。RD_Pointer_syn要比真正的RD_Pointer要滞后,导致判满的逻辑并不完全准确。当FIFO接近满的时候,Full信号就会为1,从而阻止对FIFO继续写入。同理,Empty信号也不准确。当FIFO接近空,但是实际可能还没空的时候,Empty信号就会为1,从而阻止对FIFO数据的读取。
这种假满空并不会导致FIFO的行为出错,只会导致FIFO的效率略微有下降,相当于FIFO的层数少了那么一两层。本质上对FIFO起到了过保护的作用。
假如Gary码直接进行比较,其空满判断的逻辑脚本如下:
wire [FIFO_DEPTH :0] wr_addr_glay_wire;
assign wr_addr_glay_wire = (wr_addr_bin_wire>>1)^wr_addr_bin_wire;
always @(posedge wr_clk) begin
if(!wr_rst_n)
{rd_addr_glay_r2,rd_addr_glay_r1} <= 0;
else
{rd_addr_glay_r2,rd_addr_glay_r1} <= {rd_addr_glay_r1,rd_addr_glay};
end
wire wr_full_w;
wire [FIFO_DEPTH :0] wr_addr_glay_wire1;
assign wr_addr_glay_wire1={~wr_addr_glay_wire[FIFO_DEPTH:FIFO_DEPTH-1],wr_addr_glay_wire[FIFO_DEPTH-2:0]};//MSB与次高位取反,
assign wr_full_w = (wr_addr_glay_wire1==rd_addr_glay_r2);
always @(posedge wr_clk) begin
if(!wr_rst_n)
wr_full <= 0;
else
wr_full <= wr_full_w;
end
//空判断EMPTY方法:Gray码与Binary码一致,全部bit相同
assign rd_empty_wire = (wr_addr_glay_r2== rd_addr_glay_wire);
最后补充一下FIFO的深度计算:
FIFO写入时钟速率为800M,读出的时钟为250MHz
(1)若100个时钟写入80个数据,一个时钟读取一个数据,求FIFO最小深度(110)
(2)若100个时钟写入80个数据,三个时钟读两个数据,求FIFO的最小深度(127)
FIFO深度计算公式:
当读时钟有效占比为1时,计算公式如下:
FIFODEPTH>(WRITE_FS-READ_FS)*Burst_LenWRITE_FS
当读时钟有效占比为xy时,例子中为2/3,计算公式如下:
FIFODEPTH>Burst_Len-Burst_Len*xy*READ_FSWRITE_FS