一、简介
异步FIFO主要是由双端口存储器、写指针产生逻辑、读指针产生逻辑及空满标志产生逻辑4部分组成。读写操作是由两个完全不同时钟域的时钟所控制。在写时钟域部分,由写指针所产生逻辑生成写端口所需要的写地址和写控制信号;在读时钟域部分,由读指针产生逻辑生成读断口所需要的读地址和读控制信号;在空满标志产生部分,通常是把写指针与读指针相互比较产生空满标志。
读写指针属于不同的时钟域,如何同步异步信号,使触发器不产生亚稳态及如何正确地设计空、满信号的控制电路,使FIFO不会溢出,造成数据丢失是异步FIFO设计的两个难点。
二、读/写指针产生逻辑
使用格雷码方式实现指针
格雷码相对于二进制的优势在于:格雷码从一个数变为下一个数时只有一位发生变化。
在格雷码实现FIFO指针时需要注意,对于写逻辑部分,产生写满信号需要对读指针(格雷码)进行同步;对于读逻辑部分,产生读空信号需要对写指针(格雷码)进行同步。使用两级同步器对指针同步时,不可避免会产生两个时钟的延迟。
指针实现方法
-
格雷码计数器
步骤1:根据条件递增二进制值;
步骤2:将二进制值保存到寄存器中,同时将二进制值转换为格雷码;
步骤3:将计数器的最终格雷码值保存到寄存器中。 -
二进制到格雷码转换的Verilog代码如下:
可以通过逐位异或,或者将二进制码右移后与自身异或的操作,计算出对应的格雷码。
module bin_to_gray(bin,gray);
parameter SIZE=4;
input [SIZE-1:0] bin;
output [SIZE-1:0] gray;
assign gray = (bin>>1) ^ bin;
endmodule
- 格雷码计数器实现
根据格雷码和二进制码的特点可以发现,格雷码和二进制码的最高位是相同的,所以只需对低三位进行格雷码的转换。以写指针为例,具体实现方式如下:
parameter SIZE=4;
reg [SIZE:0] wbin,wbnext;
reg [SIZE:0] wptr,wgnext;
always @(posedge wclk ornegedge rst_n) begin
if(!rst_n) begin
wbin <= 'h0;
wptr[SIZE-1:0] <= 'h0;
end
elsebegin
wbin <= wbnext;
wptr[SIZE-1:0] <= wgnext[SIZE-1:0];
end
end
always @(*) wptr[SIZE] = wbin[SIZE];
assign wbnext = !wfull ? wbin+winc : wbin;
assign wgnext[SIZE-1:0] = (wbnext[SIZE-1:0]>>1) ^ wbnext[SIZE-1:0];
将二进制值的结果保存在一组额外的寄存器中,虽然增加了电路的面积(这点面积基本没有影响),但避免了复杂的组合逻辑,可以提高系统工作频率。
三、空满信号产生逻辑
空满标志的产生是异步FIFO设计的核心。如何正确设计此部分的逻辑,会直接影响到FIFO的性能。空满标志的产生原则是:写满不溢出,读空不多读。
以16x16异步FIFO为例,写指针wptr[4:0],读指针rptr[4:0],其中低三位为真正的读写地址,最高位用来区分空满。
若读写指针完全相等,表示读地址追上了写地址,此时FIFO为空;若最高位相反但其余位相同,表示写地址领先读地址一个周期,此时FIFO为满。
读写指针比较产生空满标志rempty和wfull,将写指针同步到读时钟域,产生读空信号rempty控制异步FIFO的读操作;将读指针同步到写时钟域,产生写满信号wfull控制异步FIFO的写操作。
- 空满信号的Verilog实现如下:
wire wfull;
wire rempty;
reg [SIZE:0] wptr_reg1;
reg [SIZE:0] wptr_reg2;
reg [SIZE:0] rptr_reg1;
reg [SIZE:0] rptr_reg2;
// 写指针在读时钟域的两级同步
always @(posedge rclk ornegedge rst_n) begin
if(!rst_n) begin
wptr_reg1 <= 'h0;
wptr_reg2 <= 'h0;
end
elsebegin
wptr_reg1 <= wptr;
wptr_reg2 <= wptr_reg1;
end
end
// 读指针在写时钟域的两级同步
always @(posedge wclk ornegedge rst_n) begin
if(!rst_n) begin
rptr_reg1 <= 'h0;
rptr_reg2 <= 'h0;
end
elsebegin
rptr_reg1 <= rptr;
rptr_reg2 <= rptr_reg1;
end
end
//空满标志产生
assign wfull = ({!wptr[SIZE],wptr[SIZE-1:0]}==rptr_reg2)? 1'b1 : 1'b0;
assign rempty = (wptr_reg2==rptr)? 1'b1:1'b0;