FIFO(first in first out)定义
先入先出的数据缓存器,没有外部读写地址线,可同时读写。
FIFO的工作流程
写操作
在写时钟和状态信号的控制下,写使能信号允许向FIFO中写入数据;写到一定程度后,FIFO无法存入新的数据(或以丢失旧数据为代价),此时无法写入新的数据。
复位后,FIFO(双口RAM)中的数据被清空,只能进行写操作。
当写使能信号有效时,数据在写时钟下被写入FIFO中的RAM存储单元(存储单元的地址由写指针寄存器的写地址确定,复位时为0)。在完成写操作或允许写入数据后,写指针寄存器会加1,指向下一个存储单元。
当写指针寄存器到达一定数值时,由于先进的数据还没被读出,再写入新的数据会覆盖原有数据,此时为写满,产生状态信号FULL,不允许继续写数据。
读操作
在读时钟和状态信号的控制下,读使能信号允许从FIFO中读取数据;读到一定程度后,无法从FIFO中读取新的数据,此时无法读出新的数据。
复位后,FIFO数据被清空,不能进行读操作,否则会读出错误数据。
当FIFO中有数据时,读使能信号有效,会根据读指针寄存器的读地址(复位时为0)读取FIFO中RAM存储单元的数据,在完成读操作或允许读出数据后,读指针寄存器会加1,指向下一个存储单元。
当读指针寄存器到达一定数值时,由于FIFO中没有数据,此时无法读取数据否则会出错,此时为读空,产生状态信号empty,不允许继续读数据。
FIFO的组成
存储数据,且支持读写操作:双端口RAM;
读写地址产生模块:读写地址存储器;
控制读写操作:读/写控制逻辑和状态信号产生逻辑;
(异步FIFO)对读写地址进行跨时钟域比较:同步器。
1、同步fifo
fifo状态表示 fifo_empty,fifo_full
一、
二、
2、异步fifo
需要引入同步时钟信号,
fifo_full时需要,允许fifo实际未满,但是fifo_full为1,即
格雷码实现FIFO指针
完美的实现FIFO空或FIFO满条件需要正确取样wr_ptr和rd_ptr的值,在wr_clk和rd_clk时钟域之间传递指针的最好方式是使用格雷码实现指针。
使用格雷码作为异步fifo指针的原因:解决汇聚问题,格雷码每次跳转只会有1位发生变化,所以如果出现不确定状态只有正确变化了和不变两种状况。因此在读写时钟不一样的情况下,纵使读写地址每bit同步过程中出现延时不一致,也不会使得FIFO在实际空或者满之后,FIFO却没有正确的产生出空满信号。只有可能是实际没有空或者满,但产生了空满信号,但这对于FIFO的功能不会有影响,只会使得FIFO的读或者写操作暂停。如果同步时钟信号在计数值转换期间到来,这种编码能消除绝大部分的错误。
格雷码计数器设计设计流程
设计步骤:
- 将格雷值转换为二进制值;
- 根据条件递增二进制值;
- 将二进制值转化为格雷码;
- 将计数器的最终格雷值保存到寄存器中。
1、格雷码到二进制转换器
转换关系表
对于n位计数器来说,格雷值与二进制值的MSB相同:
二进制值非最高位值可以通过二进制值的高一位值与对应位置的格雷码值做异或获得,可以替代做链式推导:
以四位转换器为例,转换公式为:
//转换器verilog代码
module gray_to_bin #(parameter size=4)(
input [size-1:0] gray,
output reg [size-1:0] bin
);
integer i;
always @(gray)
for(i=0; i<=size; i=i+1)
bin[i] = ^(gray >> i); //移位预算符产生的0在异或中不影响最终结果
endmodule
2、二进制码到格雷码转换器
对于n位计数器来说,格雷值与二进制值的MSB相同:
// 二进制码转换为格雷码
module bin_to_gray #(parameter size=4)(
input [size-1:0] bin,
output reg [size-1:0] gray
);
assign gray = bin ^ (bin>>1);
endmodule
格雷码计数器verilog代码
module gray_counter#(parameter size=4)(
input clk, inr, rst_n,
output reg [size-1:0] gray
);
reg [size-1:0] gray_temp, bin_temp, bin;
integer i;
always @(posedge clk or negedge rst_n)
begin:gray_registered
if(!rst_n)
gray <= {size{1'b0}};
else
gray <= gray_temp;
end
always @(gray or inr)
begin:gray_bin_gray
for(i=0; i<=size; i=i+1)
bin[i] = ^(gray>>i);
bin_temp = bin + inr;
gray_temp = bin_temp ^ (bin_temp>>1)
end
endmodule
逻辑原理图:
异步FIFO满与空的产生
方法一:将格雷码转换为二进制码后进行对比
指针表示方法:
N位指针可以覆盖2^N个地址,如果使用与FIFO大小相同的N表示地址会导致,wr_ptr和rd_ptr相同时,FIFO可能处于fifo_empty或者fifo_full的状态,因此可以增加一位作为地址的MSB,即指针宽度比寻址FIFO存储器所需宽度多一位。
当wr_ptr和rd_ptr完全相同时,表示fifo当前状态为fifo_empty;
当wr_ptr和rd_ptr仅有MSB不相同时,表示fifo当前状态为fifo_full;
FIFO状态模块原理图,(fifo满产生中MSB比较器'=='应该为‘!=’):由于存在XOR门链路,最大操作频率取决于格雷码计数器的速度。
特点:该方法指针以格雷码形式保存,比较和递增操作以二进制形式进行,需要4个格雷码到二进制转化器,需要比较多的逻辑;功能区分明显,逻辑实现比较简单,纠错也比较简单。
方法二:直接比较读指针与写指针的格雷码
问题:由于格雷码除最高位以外的值不同于二进制码的对称方式,而是镜像对称,因此需要进行一定的转换,从而实现格雷码之间的对比。
四位格雷码转换三位格雷码转换方法及转换表:
// 产生fifo_full的信号
wire rd_2nd_msb = rd_ptr_sync[size] ^ rd_ptr_sync[size-1];
wire wr_2nd_msb = wr_gtemp[size] ^ wr_gtemp[size-1];
always @(posedge wclk or negedge rst_n)
begin:
if(!rst_n)
fifo_full <= 1'b0;
else
fifo_full <= ((wr_gtemp[size] != rd_ptr_sync[size])) && (rd_2nd_msb == wr_2nd_msb) && (wr_gtemp[size-2:0] && rd_ptr_sync[size-2]));
end
fifo_empty应该由rd_ptr与经rd_clk同步后的wr_ptr_sync计算获得,因为fifo_empty仅对读操作有影响,wr_ptr_sync相对于wr_ptr信号存在延迟,即使出现在同步过程中wr_ptr发生变化,也仅会导致实际fifo不为空,但fifo_empty有效,导致无法读取数据;但是如果由rd_ptr_sync与wr_ptr计算获得则可能导致读空FIFO的操作,从而出错。同理fifo_full也应该由rd_ptr_sync与wr_ptr计算获得。
fifo_empty仅对读操作有影响,对写操作没有影响,因此fifo_empty应当由rd_ptr和wr_ptr_sync产生,需要‘立刻’了解到rd_ptr的状态,而wr_ptr可以允许由延迟。
fifo_full仅对写操作有影响,对读操作没有影响,因此fifo_full应当由wr_ptr和rd_ptr_sync产生,需要‘立刻’了解到wr_ptr的状态,而rd_ptr可以允许由延迟。
异步FIFO设计注意事项
异步FIFO的设计与同步FIFO的设计具有很大差距;设计的时候需要考虑跨时钟域处理带来的问题;
- FIFO的设计必须解决empty和full控制问题;
- 异步FIFO可以考虑把写Addr在写时钟域转换成gray码,然后通过读时钟来寄存器,转换到读时钟域中,解决empty标记信号;
- 异步FIFO可以考虑把读Addr在读时钟域转换成gray码,然后通过写时钟来寄存器,转换到写时钟域中,解决full标记信号;
- 使用gray码的原因在gray码可以把Addr值连续变化的规例(但是Addr的会有多个bit跳变)转换成gray码中只有1个bit跳变,这样在跨时钟域传输中不会出现异步采取出现很大差异(异步时钟采样会出现亚稳态现象),最多是原始Addr值加1或者减1,这样不会使得FIFO状态出现错误;
当fifo的尺寸很大时候。用gray code 变得不太合算。因为要从binary变成gray,再变回来。
这个时候,要用异步握手来把地址转到另一个时钟域里面。简单的说用request和ack。