来讲一讲面试必问的异步FIFO设计!

异步FIFO设计可以说是数字IC设计工程师面试时必问的一个问题了,也是我们经常使用但是又往往被忽略的一个东西,今天就展开详细说一说不同深度(2^N或者非2^N)异步FIFO的设计思想;

一:2^N深度异步FIFO设计

1:应用场景

异步FIFO用来在两个异步时钟域之间传输数据,如下图中是两个系统,分别为“system A”和“systemY”,从X向Y传输数据,两个系统工作在不同的时钟域。

来讲一讲面试必问的异步FIFO设计!_第1张图片

 system X使用 xclk将数据写入FIFO,systemY使用yclk将数据读出,fifo full和fifo_empty分别负责监控上溢(overflow)和下溢(underflow)情况

fifo_full 指示上溢出情况,拉高时数据不应再写入FIFO,否则会将FIFO内的数据覆盖掉;

fifo_empty指示下溢出情况,拉高时不应该再读取FIFO,否则会读出垃圾数据

与握手信号不同,异步FIFO用于对性能要求较高的设计中,尤其是时钟延时比系统资源更为重要的环境中。

2:异步FIFO的结构

与同步FIFO设计原理相似,异步FIFO的结构也可以分为三个部分,如图中所示,fifo write control。fifo read control和fifo memory,其中fifo write control和fifo read control分别工作在wr_clk和rd_clk时钟域,fifo memory工作在wr_clk和rd_clk时钟域。

fifo write control:写指针产生,满信号产生,读指针同步器

fifo read control:读指针产生,空信号产生和写指针同步器

fifo memory:缓存数据

来讲一讲面试必问的异步FIFO设计!_第2张图片

3:关键设计

3.1:异步fifo和同步fifo的差异,异步fifo特点:

      1:fifo memory工作在两个时钟域

      2:wr_ctrl和rd_ctrl工作在不同的时钟域

      3:读/写指针经过两级同步器后会存在延迟

       异步FIFO不能再使用“计数器”产生fifo空,满信号,原因在于计数器不能被两个时钟同时驱动,此时只能通过比较读,写指针来产生空,满信号,将读指针同步到写时钟域,与写指针比较产生满信号,将写指针同步到读时钟域,与读指针比较产生空信号。

        读写指针的同步都需要跨时钟域传输,若使用握手信号的方式同步指针,效率很低;加入指针仍然使用“二进制编码”,使用两级同步器同步指针,可能会出现采样的亚稳态导致用于比较的指针出现错误。

         如指针从“111-000”经过两级同步器,可能的结果有8种可能,每一个比特都可能存在采样错误,错误的指针会导致fifo空,满信号异常拉起。

# 读指针同步错误,FIFO满信号未正常拉高,继续写FIFO会覆盖原数据,导致数据传输错误;

写指针同步错误,FIFO空信号为正常拉高,继续读FIFO会读出垃圾数据,导致数据传输错误;

因此异步fifo的指针在跨时钟做同步时应该避免使用二进制编码!

3.2:格雷码

实现异步fifo指针同步的一种方式是使用gray code,格雷码是“单位间距码”,即相邻之间只有1bit不同,格雷码与二进制码的对应关系如下图中所示,格雷码有一下几个特点:

# 单位间距码:相邻值之间只有1bit不同;

# 中心对称:除MSB外,其余bit中心对称;

来讲一讲面试必问的异步FIFO设计!_第3张图片

 指针采用格雷码经过两级同步器同步很少会出现亚稳态,此外取样后的值最多只有1bit出现错误,但该错误不会影响空,满信号的产生,考虑格雷码读写指针采样错误的情况:

# 读指针若采样错误:相邻gray code只有1bit不同,若采样出现亚稳态会出现两种情况,一是采样值保持不变,同步的读指针小于当前的真实值,fifo full会提前拉高,此时不会出现overflow;二是采样值采样成功,同步的读指针等于当前的真实值,两种结果都不会影响fifo_full的正确产生;

# 写指针采样错误:写指针同理,一是同步的写指针小于当前的真实值,fifo empty会提前拉高,虽然还有数据未读出,但是这种情况也不会造成underflow;二是同步的写指针党羽当前的真实值,两种结果都不会影响fifo empty的正确产生。

3.3:同步器对指针进行同步操作带来的影响:

比较读写指针的目的是产生FIFO的空满信号,相比同步FIFO,异步FIFO在两个时钟域分别比较读写指针,读/写指针都需要经过两级同步器;

3.3.1:FIFO的 “假满”

由于读指针同步到写时钟域存在2*T_wclk的延迟,此时写时钟域采样到的rd_ptr_sync必然小于或者等于读时钟域的rd_ptr,rd_ptr_sync小于rd_ptr的情况如图所示,wr_ptr追赶上rd_ptr_sync (wr_ptr和rd_ptr_sync都转换为gray code) ,最高位不同其余未相同,此时fifo full信号拉高,但是实际上FIFO内还有两个地址空间可以写入数据,这种情况就是FIFO的假满,fifo的假满虽然阻止了数据的写入,但是对数据的准确性是没有影响的,只有在FIFO实际已经满了但是没有阻止数据写入才会出现overflow(原本有效的数据被覆盖)

来讲一讲面试必问的异步FIFO设计!_第4张图片

3.3.2 :FIFO的 “假空” 

由于写指针同步到读时钟域存在2*T_rclk的延迟,此时读时钟域采样到的wr_ptr_sync小于等于读时钟域的wr_ptr。wr_ptr_sync小于等于wr_ptr的情况如图所示,wr_ptr_sync等于rd_ptr(指针均以转换为gray code),所有比特位均相等,此时会拉高fifo_empty信号。FIFO的“假空”虽然在实际有数据的情况下拉高了FIFO的空信号,阻止了数据的读出,但是对数据的准确性是没有影响的,直到读时钟域看到的写指针变化才会拉低FIFO的空信号。只有在FIFO为空的时候没有阻止读操作才会产生问题,即underflow问题;

来讲一讲面试必问的异步FIFO设计!_第5张图片

3.4 格雷码和二进制码的转换

读、写指针有二进制码转为格雷码后经两级同步器同步到写、读时钟域进行指针的比较以产生FIFO的空、满信号,下面将给出格雷码与二进制码相互转换的方法。

二进制转换gray code:

来讲一讲面试必问的异步FIFO设计!_第6张图片

gray code转换二进制:

来讲一讲面试必问的异步FIFO设计!_第7张图片

3.5 读,写指针的产生

读写指针的产生有两种方法,一种是直接使用gray code计数器产生读写指针,一种是使用二进制码计数器产生读写指针,再将读写指针转换为格雷码用于跨时钟域的同步,下面将分析这两种设计方法:

3.5.1格雷码计数器

格雷码计数器的结构如图所示,由格雷码转换二进制,二进制加法器和二进制码转格雷码组成;

来讲一讲面试必问的异步FIFO设计!_第8张图片

从面积和工作频率两个方面分析该设计。

# 面积:格雷码转二进制、二进制转格雷码的组合逻辑,格雷码指针寄存器;

# 工作频率:寄存器输入信号的组合逻辑较为复杂,该电路工作在较高频率可能存在时序违例的情况。

3.5.2 二进制码计数器

二进制码计数器的结构如图所示,由二进制转格雷码,二进制码寄存器和格雷码寄存器组成;

来讲一讲面试必问的异步FIFO设计!_第9张图片

 从面积和工作频率两个方面分析该设计;

面积:二进制转格雷码,两个寄存器;

工作频率:寄存器输入只有加法器或者二进制转格雷码,组合逻辑延迟较小,该电路可以工作在较高的频率;

3.6:空,满信号的产生

FIFO的空,满信号通过比较读,写指针可以得到,下文直接给出最常用的设计下FIFO的空,满信号如何产生。
采用二进制计数,将二进制转换为gray code进行指针比较产生full或者empty信号;

2^N deep 的fifo 指针计数信号和转换后的gray code信号位宽需要N+1位。

按照格雷码的特性:

FIFO EMPTY:读写指针完全相同时;

FIFO FULL:对比二进制码的满判定原则,最高位不同,其余位相同即认为满,但是如下图所示的情况如果格雷码也采用这种方式判断就会出现问题;

来讲一讲面试必问的异步FIFO设计!_第10张图片

当rd_ptr和wr_ptr相等时FIFO为空,wr_ptr加一后,wr_ptr和rd_ptr的最高位不同,其余位相同,按照二进制的判满原则此时FIFO为满,但此时FIFO并没有满,所以二进制码的FIFO判断满的方法不适用于格雷码。

那格雷码该怎么判满呢?很简单,双格雷码计数器可以很好的解决这个问题,计数器的最高位用于区分写指针比读指针多回绕一次,次高位用于确定写指针的真实位置,FIFO满需要满足三个条件:

1:同步后的读指针rd_ptr_sync的MSB应该与下一个写指针的格雷码wr_gtemp的MSB应该不同;

2:写时钟域两位MSB异或应与读指针一样; 

3:剩下的LSB都应该一样;

总结上述三点其实就是:读写指针的最高位和次高位相反,其余位均相同;

4:大容量异步FIFO设计

大容量同步FIFO是使用SRAM作为FIFO memory实现的,大容量异步FIFO仍可以使用该方法,不过更为常用的方法是使用“大容量同步FIFO和小容量异步FIFO”级联来实现的。原因在于异步FIFO的功能只是跨时钟域传输数据,同步FIFO更适合缓存数据,结合这两种FIFO的特点将其级联,得到大容量异步FIFO;

5:代码实现

5.1:async_fifo_top

//=================================================================================
// module      : async_fifo_gray_cnt.v
// description : asynchronous fifo , pointer generate by gray counter
//=================================================================================

module async_fifo(
    wclk,
    wrst_n,
    wr_en,
    wr_data,
    rclk,
    rrst_n,
    rd_en,
    rd_data,
    fifo_full,
    fifo_empty    
);

//=================================================================================
// parameter & localparam
//=================================================================================

//=================================================================================
// parameter
parameter FIFO_DATA_WIDTH       = 16;
parameter FIFO_DEPTH            = 16;

//=================================================================================
// localparam
localparam FIFO_ADDR_WIDTH      = clog2(FIFO_DEPTH);
localparam FIFO_PTR_WIDTH       = FIFO_ADDR_WIDTH + 1;

//=================================================================================
// I/O
//=================================================================================
input                               wclk;
input                               wrst_n;
input                               wr_en;
input   [FIFO_DATA_WIDTH-1:0]       wdata;

input                               rclk;
input                               rrst_n;
input                               rd_en;
output  [FIFO_DATA_WIDTH-1:0]       rdata;

output                              fifo_full;
output                              fifo_empty;

//=================================================================================
// signal
//=================================================================================

// ---- fifo mem ----
wire    [FIFO_ADDR_WIDTH-1:0]       waddr;
wire    [FIFO_ADDR_WIDTH-1:0]       raddr;

// ---- wr_sync_cell ----
wire    [FIFO_PTR_WIDTH-1:0]        rptr_g;
wire    [FIFO_PTR_WIDTH-1:0]        rptr_g_sync;

// ---- rd_sync_cell ----
wire    [FIFO_PTR_WIDTH-1:0]        wptr_g;
wire    [FIFO_PTR_WIDTH-1:0]        wptr_g_sync;

// ---- wr_ptr_full ----



//=================================================================================
// main body
//=================================================================================

//=================================================================================
// 1. fifo_mem
// 2. wr_sync_cell
// 3. rd_sync_cell
// 4. wptr_full
// 5. rptr_empty

//=================================================================================
// fifo_mem

fifo_mem #(
    .DSIZE          (FIFO_DATA_WIDTH        ),
    .FIFO_DEPTH     (FIFO_DEPTH             )
)
u_fifo_mem(
    .raddr          (raddr                  ),
    .rdata          (rdata                  ),
    .wclk           (wclk                   ),
    .wr_en          (wr_en                  ),
    .waddr          (waddr                  ),
    .wdata          (wdata                  ),
    .fifo_full      (fifo_full              )
);

//=================================================================================
// wr_sync_cell

sync_cell #(
    .DSIZE          (FIFO_DATA_WIDTH)
)
u_rd_ptr_sync(
    .dat_i          (rptr_g         ),
    .clk_o          (wclk           ),
    .rst_n_o        (wrst_n         ),
    .dat_o          (rptr_g_sync    )
);

//=================================================================================
// rd_sync_cell

sync_cell #(
    .DSIZE          (FIFO_DATA_WIDTH)
)
u_wr_ptr_sync(
    .dat_i          (wptr_g         ),
    .clk_o          (rclk           ),
    .rst_n_o        (rrst_n         ),
    .dat_o          (wptr_g_sync    )
);

//=================================================================================
// wptr_full

wptr_full #(
    .FIFO_DEPTH     (FIFO_DEPTH     )
)
u_wptr_full (
    .wclk           (wclk           ),
    .wrst_n         (wrst_n         ),
    .wr_en          (wr_en          ),
    .waddr          (waddr          ),
    .wptr_g         (wptr_g         ),
    .rptr_g_sync    (rptr_g_sync    ),
    .fifo_full      (fifo_full      )
);

//=================================================================================
// rptr_empty

rptr_empty #(
    .FIFO_DEPTH     (FIFO_DEPTH     )
)
u_rptr_empty(
    .rclk           (rclk           ),
    .rrst_n         (rrst_n         ),
    .rd_en          (rd_en          ),
    .raddr          (raddr          ),
    .wptr_g_sync    (wptr_g_sync    ),
    .rptr_g         (rptr_g         ),
    .fifo_empty     (fifo_empty     )
);

endmodule;

5.2 async_fifo_mem

//=================================================================================
// module      : fifo_mem.v
// description : register dpram
//=================================================================================

module fifo_mem (
    raddr,
    rdata,
    wclk,
    wr_en,
    fifo_full,
    waddr,
    wdata
);

// ==========================================================================
// parameter
// ==========================================================================
parameter DSIZE             = 8;
parameter FIFO_DEPTH        = 16;

// ==========================================================================
// localpara
// ==========================================================================
localparam FIFO_ADDR_WIDTH  = $clog2(FIFO_DEPTH);

// ==========================================================================
// I/O
// ==========================================================================
input   [FIFO_ADDR_WIDTH-1:0]       raddr;
output  [DSIZE-1:0]                 rdata;

input                               wclk;
input                               wr_en;
input   [FIFO_ADDR_WIDTH-1:0]       waddr;
input   [DSIZE-1:0]                 wdata;

// ==========================================================================
// signal define
// ==========================================================================
reg     [DSIZE-1:0]                 fifo_mem[FIFO_DEPTH-1:0];

// ==========================================================================
// main body
// ==========================================================================

// ==========================================================================
// write fifo_mem
always@(posedge wclk) begin
    if((wr_en == 1'b1) && (fifo_full != 1'b1)) begin
        fifo_mem[waddr] <= wdata;
    end
end

// ==========================================================================
// read fifo_mem
assign rdata = fifo_mem[raddr];


endmodule

5.3  sync_cell

//=================================================================================
// module      : sync_cell.v
// description : 2-stage synchronizer
//=================================================================================

module sync_cell(
    dat_i,
    clk_o,
    rst_n_o,
    dat_o
);

//=================================================================================
// parameter & localparam
//=================================================================================
parameter DSIZE                 = 16;

//=================================================================================
// I/O
//=================================================================================
input   [DSIZE-1:0]                 dat_i;

input                               clk_o;
input                               rst_n_o;
output  [DSIZE-1:0]                 dat_o;

//=================================================================================
// signal
//=================================================================================

// ---- temp flip-flop ----
reg     [DSIZE-1:0]                 dat_i_ff1;
reg     [DSIZE-1:0]                 dat_i_ff2;

//=================================================================================
// main body
//=================================================================================

always@(posedge clk_o or negedge rst_n_o) begin
    if(rst_n_o == 1'b0) begin
        dat_i_ff1 <= {DSIZE{1'b0}};
        dat_i_ff2 <= {DSIZE{1'b0}};
    end
    else begin
        dat_i_ff1 <= dat_i;
        dat_i_ff2 <= dat_i_ff1;
    end
end

assign dat_o = dat_i_ff2;

endmodule

5.4 wptr_full

//=================================================================================
// module      : wptr_full.v
// description : async_fifo write ctrl
//=================================================================================

module wptr_full(
    wclk,
    wrst_n,
    wr_en,
    waddr,
    wptr_g,
    rptr_g_sync,
    fifo_full
);

//=================================================================================
// parameter & localparam
//=================================================================================
parameter FIFO_DEPTH        = 16;

localparam FIFO_ADDR_WIDTH  = clog2(FIFO_DEPTH) ;
localparam FIFO_PTR_WIDTH   = FIFO_ADDR_WIDTH + 1;

//=================================================================================
// I/O
//=================================================================================
input                               wclk;
input                               wrst_n;
input                               wr_en;
output  [FIFO_ADDR_WIDTH-1:0]       waddr;

output  [FIFO_PTR_WIDTH-1:0]        wptr_g;
input   [FIFO_PTR_WIDTH-1:0]        rptr_g_sync;

output                              fifo_full;

// ==========================================================================
// signal define
// ==========================================================================
reg     [FIFO_PTR_WIDTH-1:0]        wptr_b;
reg     [FIFO_PTR_WIDTH-1:0]        wptr_g;
reg                                 fifo_full;

wire    [FIFO_PTR_WIDTH-1:0]        wptr_bnext;
wire    [FIFO_PTR_WIDTH-1:0]        wptr_gnext;

//=================================================================================
// main body
//=================================================================================

// ---- binary count ----

assign wptr_bnext   = wptr_b + (wr_en & (~fifo_full));
assign waddr        = wptr_b[FIFO_ADDR_WIDTH-1:0];

always @(posedge wclk or negedge wrst_n) begin
    if(wrst_n == 1'b0)begin
        wptr_b <= {FIFO_PTR_WIDTH{1'b0}};
    end
    else begin
        wptr_b <= wptr_bnext;
    end
end

// ---- binary to gray ----
assign wptr_gnext = (wptr_bnext >> 1) ^ (wptr_bnext);

always @(posedge wclk or negedge wrst_n) begin
    if(wrst_n == 1'b0) begin
        wptr_g <= {FIFO_PTR_WIDTH{1'b0}};
    end
    else begin
        wptr_g <= wptr_gnext;
    end

end

// ---- fifo full -----
// three necessary condition
assign fifo_full_val = (wptr_gnext == {~rptr_g_sync[FIFO_PTR_WIDTH-1:FIFO_PTR_WIDTH-2],
                                       rptr_g_sync[FIFO_PTR_WIDTH-3:0]});

always@(posedge wclk or negedge wrst_n) begin
    if(wrst_n == 1'b0) begin
        fifo_full <= 1'b0;
    end
    else begin
        fifo_full <= fifo_full_val;
    end
end



endmodule

5.5 rptr_empty

//=================================================================================
// module      : rptr_empty.v
// description : async_fifo read ctrl
// data        : 2022/3/2
// author      : souther meditating
//=================================================================================

module rptr_empty(
    rclk,
    rrst_n,
    rd_en,
    raddr,
    wptr_g_sync,
    rptr_g,
    fifo_empty
);

//=================================================================================
// parameter & localparam
//=================================================================================
parameter FIFO_DEPTH        = 16;

localparam FIFO_ADDR_WIDTH  = clog2(FIFO_DEPTH) ;
localparam FIFO_PTR_WIDTH   = FIFO_ADDR_WIDTH + 1;

//=================================================================================
// I/O
//=================================================================================
input                               rclk;
input                               rrst_n;

input                               rd_en;
output  [FIFO_ADDR_WIDTH-1:0]       raddr;

input   [FIFO_PTR_WIDTH-1:0]        wptr_g_sync;
output  [FIFO_PTR_WIDTH-1:0]        rptr_g;

output                              fifo_empty;

// ==========================================================================
// signal define
// ==========================================================================
reg     [FIFO_PTR_WIDTH-1:0]        rptr_b;
reg     [FIFO_PTR_WIDTH-1:0]        rptr_g;

wire    [FIFO_PTR_WIDTH-1:0]        rptr_bnext;
wire    [FIFO_PTR_WIDTH-1:0]        rptr_gnext;

wire                                fifo_empty_val;
reg                                 fifo_empty;

//=================================================================================
// main body
//=================================================================================

// ---- binary count ----

assign rptr_bnext       = rptr_b + (rd_en & (~fifo_empty));
assign raddr            = rptr_b[FIFO_ADDR_WIDTH-1:0];

always @(posedge rclk or negedge rrst_n) begin
    if(rrst_n == 1'b0) begin
        rptr_b <= {FIFO_PTR_WIDTH{1'b0}};
    end
    else begin
        rptr_b <= rptr_bnext; 
    end
end

// ---- binary to gray ----

assign rptr_gnext = (rptr_bnext >> 1) ^ rptr_bnext;

always@(posedge rclk or negedge rrst_n) begin
    if(rrst_n == 1'b0) begin
        rptr_g <= {FIFO_PTR_WIDTH{1'b0}};
    end
    else begin
        rptr_g <= rptr_gnext;
    end
end

// ---- fifo empty ----
assign fifo_empty_val = (rptr_gnext == wptr_g_sync);

always @(posedge rclk or posedge rrst_n) begin
    if(rrst_n == 1'b0) begin
        fifo_empty <= 1'b1;
    end
    else begin
        fifo_empty <= fifo_empty_val;
    end
end

endmodule

二:非2^N深度异步FIFO设计

如果设计的深度不是2的幂次,那么格雷码其实也不是连续的,也即在最大值跳变到最小值时不止一个bit发生了变化,当然你也可以设计一个深度略大的,并且是2的幂次的FIFO,但是这样其实是很浪费资源的。

例如我们需要一个深度为5的FIFO,此时我们可以设计一个深度为8的FIFO,但是这样会浪费资源,那么我们应该怎么办呢?

其实可以换个思路,通过观察下表不难发现,格雷码具有对称性,0-15这16个格雷码,如果前后去掉相同个数的格雷码,那么剩余的格雷码仍然符合相邻两个数只有1bit的跳变,也包括最后一个到第一个的跳变。

来讲一讲面试必问的异步FIFO设计!_第11张图片

例如:如果要设计深度为5的FIFO,由于需要5*2 = 10个格雷码【这里为什么需要10个格雷码,假如设计深度为8的fifo,那么地址指针的宽度根据上文描述就要为4,就是说深度为8的fifo,写读两轮,4位的gray code地址指针遍历一轮,理解这一点非常关键】,所以可以前后各去掉3个数,也就是保留3-12这十个格雷码【可以实现对称的情况为3-7,8-12】,可以看到,最大的数12和最小的数3也是只有1bit的跳变。

所以我们可以这样编码:

addr == 0【bin】的时候,gray_code不从4‘b0000开始,而是从4’b0010开始;gray_code最大为4‘b1010,不过此时需要注意的是:

# 我们在由addr【bin】得到格雷码时不能直接转换,而是要使用addr + 3进行转换;同理由格雷码转换回【bin】addr时,也不要忘记减去3;

使用这种编码时,FIFO的满判断也不是简单的高两位相反,低位相同了,比如depth=5,rd_ptr= 4’b0010,wr_ptr= 4‘b1100,表示5个entry已经满了,但是此时并不能通过之前的条件去判断,具体如何判断,需要根据depth的大小寻找规律。

//设置起始、结束位置代码
parameter DEPTH = 6;
parameter ADDR = 3; //$clog2(6)= 3
reg [ADDR:0] start_count,end_count;//设置初始、结束指针地址
//对于任意深度都可以通过下面的代码设置初始值,通过改变
//DEPTH和ADDR控制
always@(posedge clk or rst_n)begin
	if(!rst_n)begin
			start_count <= {1'b1,{ADDR{1'B0}}} - DEPTH;//深度6:8-6=2//深度5:8-5=3
			end_count <= {1‘b0,{ADDR{1'B1}}	+ DEPTH;//深度6:7+6=13//深度5:7+5=12																																																																		
	end
	else begin
			start_count <= start_count;
			end_count <=end_count;
	end
end

设置好起始,结束指针后,接下来就需要考虑的是指针的变化情况了,与2的幂次FIFO不同的是需要加入起始,结束指针。

input winc,rinc;//读写使能信号
reg [ADDR:0]    write_ptr;
reg [ADDR:0]    read_ptr;

// 设计读写指针跳转
//generate write pointer
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        write_ptr <= start_count;    
    end
    else if(winc && !wfull)begin
    	if(write_ptr == end_count)//到了结束指针,跳回起始指针
    		write_ptr <= start_count;
    	else 
        write_ptr <= write_ptr + 1;//不然正常计数
    end
    else 
    	 write_ptr <= write_ptr;//FIFO满或者没有读信号保持不变
end 
 
//generate read pointer
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        read_ptr <= start_count;
    else if(rinc && !rempty)begin
    	if(write_ptr == end_count)//到了结束指针,跳回起始指针
    		read_ptr <= start_count;
    	else 
        	read_ptr <= read_ptr + 1;
    end
    else 
    	read_ptr <= read_ptr;//FIFO满或者没有读信号保持不变
 end

上述代码和普通情况只添加了到结束地址跳回来初始地址的操作。

最后就是控制格雷码的操作了,通过对指针跳转的观察情况,做了以下的分析,首先,因为是非2的幂次的数据,设置了起始和终止位置,所以它返回一次的位置并不是正常的和2次幂深度一样的,依旧拿深度为5举例,它是从12跳回到3的,所以它并不能完全套用异步FIFO的二进制码转换格雷码的情况,首先需要先让二进制码变成正确的情况再进行转换。

//在这里还是利用了格雷码的对称性,因为从最高位1,跳回最高位0的情况,会改变start_count个数。而从最高位0,跳到最高位1的情况,则不会数据的改变。因此对所有最高位为0的情况,减去start_count。
wire [ADDR:0]    real_write_ptr,real_read_ptr;//先改变二进制码
wire [ADDR:0]    gray_wr_ptr,gray_rd_ptr;//二进制转格雷码
wire full_o,emtpy ;
//改变二进制码start_count
//eg gray_code write_ptr = 1010  // 二进制12
//             read_ptr = 0100   //二进制7
assign real_write_ptr = write_ptr[ADDR]?write_ptr:(write_ptr -start_count);
assign real_read_ptr = read_ptr[ADDR]?read_ptr:(read_ptr -start_count);

//二进制转格雷码start_count
assign gray_rd_ptr = real_read_ptr ^ (real_read_ptr >> 1'b1);
assign gray_wr_ptr = real_write_ptr ^ (real_write_ptr >> 1'b1);

//格雷码的比较,满信号为读写指针最高位和次高位不同,其余位相同,空信号为读写指针相同
//write_ptr gray_code 1010 //二进制12
//read_ptr-start_count 0100-3 = 0110 //7-3
assign full_o  = (gray_wr_ptr == {~gray_rd_ptr[ADDR:ADDR-1],gray_rd_ptr[PTR_WIDTH-2:0]})? 1'b1 : 1'b0;
assign empty_o = (gray_rd_ptr == gray_wr_ptr)? 1'b1 : 1'b0;

//读写数据
parameter WIDTH = 8;//数据位宽
reg [WIDTH-1:0] ram_mem [0:DEPTH-1];
always_ff@(posedge wclk)begin
    if(wenc)
        ram_mem[real_write_ptr[ADDR-1:0]] <= wdata;
end
 
always_ff@(posedge rclk)begin
    if(renc)
        rdata <= ram_mem[real_read_ptr[ADDR-1:0]];
end

以上就是非2幂次同步FIFO的设计思路,非2幂次异步FIFO原理与同步FIFO同理。

三:参考文章

硬件架构的艺术:异步FIFO设计_fifo级联_南风在冥想的博客-CSDN博客

数字IC前端设计进阶 - 知乎 (zhihu.com)

【《硬件架构的艺术》读书笔记】03 处理多个时钟(3) - Magnolia666 - 博客园 (cnblogs.com)

任意深度同步FIFO设计总结(非2次幂)_非2次幂深度fifo_离离离谱的博客-CSDN博客 

你可能感兴趣的:(SOC设计)