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

news2024/11/15 12:56:17

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

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

1:应用场景

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

 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:缓存数据

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中心对称;

 指针采用格雷码经过两级同步器同步很少会出现亚稳态,此外取样后的值最多只有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(原本有效的数据被覆盖)

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问题;

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

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

二进制转换gray code:

gray code转换二进制:

3.5 读,写指针的产生

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

3.5.1格雷码计数器

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

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

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

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

3.5.2 二进制码计数器

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

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

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

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

3.6:空,满信号的产生

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

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

按照格雷码的特性:

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

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

当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的跳变,也包括最后一个到第一个的跳变。

例如:如果要设计深度为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博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/855139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity开发笔记:截取指定位置含有UI的场景截图并输出

学习记录整理&#xff0c;自用&#xff0c;也希望能帮助到有相同需求的人。 如果直接截全图&#xff1a; string screenshotName "Assets/Textures/UI/20230803/2.png";ScreenCapture.CaptureScreenshot(screenshotName);截取指定位置含有UI的场景截图&#xff1a; …

Therac-25事故:软件缺陷引发的医疗灾难与教训

目录 引言 Therac-25&#xff1a;背景与功能 软件缺陷导致的灾难 Bug原理解析与编程人员的反思 教训与反思 结论 引言 在计算机科技的进步与应用领域&#xff0c;软件的质量和安全性至关重要。然而&#xff0c;历史上曾经发生过一系列令人震惊的事件&#xff0c;突显了软…

培训报名小程序报名功能完善

目录 1 修改数据源2 修改表单3 支付成功时修改状态4 创建报名成功页5 最终的效果总结 目前我们的报名功能已经搭建了一个基础版&#xff0c;后续需要展示用户已经报名的信息&#xff0c;需要添加一个状态来显示用户是否成功付费。 1 修改数据源 打开我们的报名数据源&#xff…

google PGS 下一代id

前言&#xff1a;为了进一步增强用户的隐私及其多平台游戏体验&#xff0c;Play 游戏服务(PGS) 正在推出下一代玩家 ID&#xff0c;用户第一次玩游戏时&#xff0c;他们将始终被分配一个唯一的下一代玩家 ID&#xff0c;无论用户在什么设备或平台上玩游戏&#xff0c;该 ID 都将…

加入[无人驾驶吕同学]Apollo专属课程领礼品啦!

号外号外&#xff0c;无人驾驶吕同学的Apollo领航官专属课程已经上线了。用户完成课程50%即可领取专属福利&#xff08;百度限定鼠标垫、Apollo限量帆布包、Apollo六周年定制钥匙扣、Apollo六周年限定徽章&#xff09;&#xff0c;四种礼品任选其一。 课程链接&#xff1a;htt…

使用 Docker 和 Streamlit 构建和部署 LangChain 支持的聊天应用程序

文章目录 前言聊天应用程序组件和技术LangChain Python框架开放人工智能模型前端 Streamlit UI使用 Docker 进行部署Docker 优化以实现轻量级和快速构建Docker-compose.yaml 文件基础设施Streamlit 公共云谷歌应用引擎使用 Google Cloud Run 部署应用1.启动服务2. 创建角色并将…

水产养殖产量低?教你个万能的方法!

水产养殖业作为重要的食品生产和经济活动之一&#xff0c;为人们提供了丰富的水产品&#xff0c;但如果不加以适当监测和管理&#xff0c;也可能对水质和生态系统造成负面影响。因此&#xff0c;水产养殖用水监测显得尤为重要。 在养殖过程中&#xff0c;水质的优劣直接影响着养…

Linux小型操作系统项目,《操作系统真象还原》第三章——完善MBR

前引 上一章我们完成了MBR的雏形编写&#xff0c;但是只打印了几个字符&#xff0c;这一章我们才要真正地去完成MBR的功能。 在完成MBR的功能之前我们要先了解一些知识&#xff0c;首先介绍什么是实模式。 书上的内容实在繁杂&#xff0c;简单地说&#xff0c;实模式没有虚拟和…

【算法|数组】双指针

算法|数组——双指针 引入 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;…

77. 组合

题目链接&#xff1a;力扣 解题思路&#xff1a; AC代码 class Solution {List<Integer> tem new ArrayList<>();List<List<Integer>> result new ArrayList<>();public List<List<Integer>> combine(int n, int k) {process(n…

重试框架入门:Spring-RetryGuava-Retry

前言 在日常工作中&#xff0c;随着业务日渐庞大&#xff0c;不可避免的涉及到调用远程服务&#xff0c;但是远程服务的健壮性和网络稳定性都是不可控因素&#xff0c;因此&#xff0c;我们需要考虑合适的重试机制去处理这些问题&#xff0c;最基础的方式就是手动重试&#xf…

Vue3:页面A搜索后跳转到页面B,然后从页面B退回页面A重新搜索,但是得到的页面B得刷新一下才会显示正确的数据

问题 Vue3&#xff1a; 从页面A进行搜索后跳转到页面B&#xff0c;然后从页面B退回页面A重新搜索&#xff0c;但是得到的页面B得刷新一下才会显示正确的数据。 读取数据的代码格式大致如下&#xff08;代码做了一些删减&#xff09;&#xff1a; 解决 会出现上述情况&#…

DIY 一个 Docker Base Image

1. 我们先使用C语言写一个hello-world程序 vim hello.c # include <stdio.h>int main() {print("hello docker\n"); } 2. 将hello.c文件编译成二进制文件, 需要安装工具 yum install gcc yum install glibc-static 开始编译 gcc -static hello.c -o hello 编译…

低代码平台“高”效率开发的5个能力

迫于智改数转的压力&#xff0c;促使企业要不停地思考如何从低代码的角度释放一些重复枯燥又高成本的人力投入。历经多次重大重构及大量项目实战验证之后&#xff0c;个人认为一款高效率的低代码开发平台&#xff0c;应有以下几点能力。 一、低代码平台“高”效率开发的5个能力…

ES索引重建reindex详解

目录 一、使用场景 二、reindex介绍 三、使用手册 1、覆盖更新 2、创建丢失的文档并更新旧版本的文档 3、仅创建丢失的文档 4、冲突处理 5、source中添加查询条件 6、source中包含多个源索引 7、限制处理的记录数 8、从远程ES集群中重建索引 9、提取随机子集 10、…

Java实现轻量型Web服务器接收http协议提交的RFID读卡信息

示例使用的读卡器&#xff1a;RFID网络WIFI无线TCP/UDP/HTTP可编程二次开发读卡器POE供电语音-淘宝网 (taobao.com) import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSock…

Java-day07(面向对象-3,4)

继承 将多个类中共有的属性&#xff0c;方法剥离出来单独创建一个共有类&#xff0c;当需要使用共有的属性与方法时&#xff0c;就可以通过继承(extends)来调用共有的属性与方法。通过"class A extends B" 来实现类的继承&#xff08;子类&#xff1a;A 父类&#x…

手把手教你 使用SpringBoot 实现业务数据动态脱敏

文章目录 什么是数据脱敏静态数据脱敏动态数据脱敏 需求实现1. 切面AOP实现脱敏是否脱敏注解定义切入点测试单条记录结果多条记录结果分页记录结果 2. 自定义注解和自定义消息转换器实现数据脱敏自定义DataDesensitization注解定义脱敏类型枚举实现AnnotationFormatterFactory接…

SAP度量单位转换功能

针对今天N2项目提出业务痛点&#xff1a;物料30011110的基本单位是KG&#xff0c;在XXX的BOM里单位是G&#xff0c;由于物料没配单位转换关系&#xff0c;但系统又能正常进行转换&#xff0c;开发需要技术支持。 经专项调查&#xff0c;G和KG的转换是SAP相同量纲转换标准功能&…

econml双机器学习实现连续干预和预测

连续干预 在这个示例中&#xff0c;我们使用LinearDML模型&#xff0c;使用随机森林回归模型来估计因果效应。我们首先模拟数据&#xff0c;然后模型&#xff0c;并使用方法来effect创建不同干预值下的效应&#xff08;Conditional Average Treatment Effect&#xff0c;CATE&…