异步FIFO总结

异步FIFO的基本概念


异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据

FIFO的常见参数


FIFO的宽度:即FIFO一次读写操作的数据位;
FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

异步FIFO的设计难点


如何同步异步信号,使触发器不产生亚稳态
如何正确地设计空、满以及几乎满等信号的控制电路

亚稳态问题的解决


对写地址/读地址采用格雷码
采用触发器来同步异步输入信号

空/满标志的产生


空/满标志产生的原则是:写满不溢出,读空不多读

FIFO的缓冲空间是有限的,在设计中需要考虑缓冲区的溢出和空情况。
缓冲区满,产生FULL信号,以阻塞数据继续向FIFO写入;
缓冲区空,产生EMPTY信号,以阻塞数据继续从FIFO读出。

方法:

1.采用握手协议
2.读时钟采样写指针,写时钟采样读指针,采样的写指针和读指针判断是否为EMPTY,采样的读指针和写指针判断是否为FULL

Gray code counter


异步FIFO总结-风君雪科技博客

FIFO 框图


异步FIFO总结-风君雪科技博客
异步FIFO总结-风君雪科技博客

VCS仿真结果


异步FIFO总结-风君雪科技博客

异步FIFO代码(verilog)


RTL描述


fifoif.v

module fifoif
       (
       fifo_flush,
       data_out,
       full_out,
       empty_out,
       data_in,
       wren_in, wclk, wclr_in,
       rden_in, rclk, rclr_in,
       fifo_waddr, fifo_raddr,
       ram_douta,
       ram_dinb,
       ram_ada,
       ram_adb,
       ram_cena,
       ram_cenb,
       ram_clka,
       ram_clkb
       );

parameter DSIZE = 32;
parameter ASIZE = 7;

input              fifo_flush;
output [DSIZE-1:0] data_out;
output             full_out;
output             empty_out;
input  [DSIZE-1:0] data_in;
input              wren_in, wclk, wclr_in;
input              rden_in, rclk, rclr_in;
output [ASIZE-1:0] fifo_waddr, fifo_raddr;

input  [DSIZE-1:0] ram_douta;
output [DSIZE-1:0] ram_dinb;
output [ASIZE-1:0] ram_ada;
output [ASIZE-1:0] ram_adb;
output             ram_cena;
output             ram_cenb;
output             ram_clka;
output             ram_clkb;

wire   [DSIZE-1:0] ram_douta;
wire   [DSIZE-1:0] ram_dinb;
wire   [ASIZE-1:0] ram_ada;
wire   [ASIZE-1:0] ram_adb;
wire               ram_cena;
wire               ram_cenb;
wire               ram_clka;
wire               ram_clkb;

wire  [DSIZE-1:0] data_out;
wire  [ASIZE-1:0] waddr;
wire  [ASIZE-1:0] raddr;
wire    [ASIZE:0] wptr;
wire    [ASIZE:0] rptr;
wire    [ASIZE:0] wq2_rptr;
wire    [ASIZE:0] rq2_wptr;
wire  [ASIZE-1:0] fifo_waddr = waddr;
wire  [ASIZE-1:0] fifo_raddr = raddr;

sync_r2w #(ASIZE) u_sync_r2w(
    .wq2_rptr     (wq2_rptr),
    .rptr         (rptr),
    .wclk         (wclk),
    .wrst_n       (wclr_in),
    .wflush       (fifo_flush));

sync_w2r #(ASIZE) u_sync_w2r(
    .rq2_wptr     (rq2_wptr),
    .wptr         (wptr),
    .rclk         (rclk),
    .rrst_n       (rclr_in),
    .rflush       (fifo_flush));

fifomem #(DSIZE, ASIZE) u_fifomem(
    .rdata        (data_out),
    .wdata        (data_in),
    .waddr        (waddr),
    .raddr        (raddr),
    .wren         (wren_in),
    .wclk         (wclk),
    .rden         (rden_in),
    .rclk         (rclk),
    .ram_douta    (ram_douta),
    .ram_dinb     (ram_dinb),
    .ram_ada      (ram_ada),
    .ram_adb      (ram_adb),
    .ram_cena     (ram_cena),
    .ram_cenb     (ram_cenb),
    .CLKA         (ram_clka),
    .CLKB         (ram_clkb)
    );

rptr_empty #(ASIZE) u_rptr_empty(
    .rempty       (empty_out),
    .raddr        (raddr),
    .rptr         (rptr),
    .rq2_wptr     (rq2_wptr),
    .rinc         (rden_in),
    .rclk         (rclk),
    .rrst_n       (rclr_in),
    .rflush       (fifo_flush)
    );

wptr_full #(ASIZE) u_wptr_full(
    .wfull        (full_out),
    .waddr        (waddr),
    .wptr         (wptr),
    .wq2_rptr     (wq2_rptr),
    .winc         (wren_in),
    .wclk         (wclk),
    .wrst_n       (wclr_in),
    .wflush       (fifo_flush)
    );

endmodule

fifomem.v

module fifomem(
       rdata, wdata, waddr, raddr, wren, wclk, rden, rclk,

       //memory interface
       ram_douta, ram_dinb, ram_ada, ram_adb, ram_cena, ram_cenb,
       CLKA, CLKB
       );
parameter DATASIZE = 32; // Memory data word width
parameter ADDRSIZE = 7; // Number of mem address bits

output [DATASIZE-1:0] rdata;
input  [DATASIZE-1:0] wdata;
input  [ADDRSIZE-1:0] waddr, raddr;
input                 wren, wclk;
input                 rden, rclk;

input  [DATASIZE-1:0] ram_douta;
output [DATASIZE-1:0] ram_dinb;
output [ADDRSIZE-1:0] ram_ada;
output [ADDRSIZE-1:0] ram_adb;
output                ram_cena;
output                ram_cenb;
output                CLKA;
output                CLKB;

wire                ram_cena,ram_cenb;
wire                CLKA;
wire                CLKB;
wire [DATASIZE-1:0] ram_dinb,ram_douta;
wire [ADDRSIZE-1:0] ram_ada, ram_adb;
wire [DATASIZE-1:0] rdata,wdata;

assign ram_cena = ~rden;
assign ram_ada  = raddr;
assign ram_cenb = ~wren;
assign ram_adb  = waddr;
assign ram_dinb = wdata;
assign CLKA     = rclk;
assign CLKB     = wclk;
assign rdata    = ram_douta;

endmodule

ram_dp.v

module ram_dp #(
                parameter DATA_WIDTH = 32,
                parameter ADDR_WIDTH = 7,
                parameter RAM_DEPTH = 1 << ADDR_WIDTH
                )
                (
                 output  reg    [DATA_WIDTH-1:0]     data_out     ,
                 input          [DATA_WIDTH-1:0]     data_in      ,
                 input          [ADDR_WIDTH-1:0]     addr_a       ,
                 input          [ADDR_WIDTH-1:0]     addr_b       ,
                 input                               web          , //Write Enable/Read Enable,addr_b write enable, low active
                 input                               clka         , // write  Clock Input
                 input                               clkb         , // read   Clock Input
                 input                               cena         , //Chip Select
                 input                               cenb           //Chip Select
                ); 



//--------------Internal variables---------------- 
reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];
reg                  oe_r;

//--------------Code Starts Here------------------ 

// Memory Write Block 
// Write Operation : When web = 0, cenb = 1
always @ (posedge clkb)
begin : MEM_WRITE
   if ((~cenb) && (~web) ) begin
       mem[addr_b] = data_in;
   end
end

// Memory Read Block 
// Read Operation : When web = 1, cena = 1
always @ (posedge clka)
begin : MEM_READ
  if ((~cena) && web) begin
    data_out = mem[addr_a];
  end
end

endmodule // End of Module ram_dp

sync_r2w.v

module sync_r2w 
       #(parameter ADDRSIZE = 4)
       (
        output reg  [ADDRSIZE:0]    wq2_rptr,
        input       [ADDRSIZE:0]    rptr,
        input                       wclk,
        input                       wrst_n,
        input                       wflush
       );

reg    [ADDRSIZE:0] wq1_rptr;

always @(posedge wclk or negedge wrst_n)
  if (!wrst_n) 
      {wq2_rptr,wq1_rptr} <= 0;
  else if(wflush) 
      {wq2_rptr,wq1_rptr} <= 0;
  else 
      {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};

endmodule

sync_w2r.v

module sync_w2r 
       #(parameter ADDRSIZE = 4)
       (
        output    reg    [ADDRSIZE:0]   rq2_wptr,
        input            [ADDRSIZE:0]   wptr,
        input                           rclk,
        input                           rrst_n,
        input                           rflush
       );

reg              [ADDRSIZE:0]   rq1_wptr;

always @(posedge rclk or negedge rrst_n)
  if (!rrst_n) 
      {rq2_wptr,rq1_wptr} <= 0;
  else if(rflush) 
      {rq2_wptr,rq1_wptr} <= 0;
  else 
      {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};

endmodule

rptr_empty.v

module rptr_empty
       #(parameter ADDRSIZE = 4)
       (
        output    reg                    rempty,
        output          [ADDRSIZE-1:0]   raddr,
        output    reg   [ADDRSIZE :0]    rptr,
        input           [ADDRSIZE :0]    rq2_wptr,
        input                            rinc,
        input                            rclk,
        input                            rrst_n,
        input                            rflush
       );

reg      [ADDRSIZE:0] rbin;
wire     [ADDRSIZE:0] rgraynext, rbinnext;

//-------------------
// GRAYSTYLE2 pointer
//-------------------
always @(posedge rclk or negedge rrst_n)
  if (!rrst_n) 
      {rbin, rptr} <= 0;
  else if(rflush) 
      {rbin, rptr} <= 0;
  else 
      {rbin, rptr} <= {rbinnext, rgraynext};

// Memory read-address pointer (okay to use binary to address memory)
assign raddr = rbin[ADDRSIZE-1:0];

assign rbinnext = rbin + (rinc & ~rempty);
assign rgraynext = (rbinnext>>1) ^ rbinnext;

//---------------------------------------------------------------
// FIFO empty when the next rptr == synchronized wptr or on reset
//---------------------------------------------------------------
wire rempty_val = (rgraynext == rq2_wptr);

always @(posedge rclk or negedge rrst_n)
  if (!rrst_n) 
      rempty <= 1'b1;
  else 
      rempty <= rempty_val;

endmodule

wptr_full.v

module wptr_full
       #(parameter ADDRSIZE = 4)
       (
        output   reg                      wfull,
        output          [ADDRSIZE-1:0]    waddr,
        output   reg    [ADDRSIZE :0]     wptr,
        input           [ADDRSIZE :0]     wq2_rptr,
        input                             winc,
        input                             wclk,
        input                             wrst_n,
        input                             wflush
       );

reg             [ADDRSIZE:0]      wbin;
wire            [ADDRSIZE:0]      wgraynext, wbinnext;

// GRAYSTYLE2 pointer
always @(posedge wclk or negedge wrst_n)
  if (!wrst_n) 
      {wbin, wptr} <= 0;
  else if(wflush) 
      {wbin, wptr} <= 0;
  else 
      {wbin, wptr} <= {wbinnext, wgraynext};

// Memory write-address pointer (okay to use binary to address memory)
assign waddr = wbin[ADDRSIZE-1:0];

assign wbinnext = wbin + (winc & ~wfull);
assign wgraynext = (wbinnext>>1) ^ wbinnext;

//------------------------------------------------------------------
// Simplified version of the three necessary full-tests:
// assign wfull_val=((wgnext[ADDRSIZE]    !=wq2_rptr[ADDRSIZE]  ) &&
//                   (wgnext[ADDRSIZE-1]  !=wq2_rptr[ADDRSIZE-1]) &&
//                   (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));
//------------------------------------------------------------------

wire wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
                               wq2_rptr[ADDRSIZE-2:0]});

always @(posedge wclk or negedge wrst_n)
  if (!wrst_n) 
      wfull <= 1'b0;
  else 
      wfull <= wfull_val;

endmodule

testbench


fifo_tb.v

module fifo_tb;

reg                          fifo_flush;
wire       [31:0]            data_out;
wire                         full_out;
wire                         empty_out;
wire       [31:0]            data_in;
wire                         wren_in;
wire                         wclk;
wire                         wr_rst_n;
wire                         rd_rst_n;
wire                         rden_in;
wire                         rclk;
wire       [6:0]             fifo_waddr;
wire       [6:0]             fifo_raddr;

// memory interface signal
wire       [31:0]            if_ram_douta;
wire       [31:0]            if_ram_dinb;
wire       [6:0]             if_ram_ada;
wire       [6:0]             if_ram_adb;
wire                         if_ram_cena;
wire                         if_ram_cenb;
wire                         if_ram_clka;
wire                         if_ram_clkb;

//******************************************************************
//  generate clk
//******************************************************************
clock #(.CLK_FREQ(100.0))
    u_clock_wr (
                .clk    (   wclk  )
               );

clock #(.CLK_FREQ(70.0))
    u_clock_rd (
                .clk    (   rclk  )
               );

assign  if_ram_clka  = rclk;
assign  if_ram_clkb  = wclk;

//******************************************************************
//  generate read and write data
//******************************************************************

fifo_data #(.DATA_WIDTH(8))
             u_fifo_data (
                          .wr_rst_n   (   wr_rst_n    ),
                          .wr_clk     (   wclk        ),
                          .wr_en      (   wren_in     ),
                          .wr_data    (   data_in     ),
                          .wr_full    (   full_out    ),
                          .rd_rst_n   (   rd_rst_n    ),
                          .rd_clk     (   rclk        ),
                          .rd_en      (   rden_in     ),
                          .rd_empty   (   empty_out   )
                         );

//******************************************************************
//  creat FSDB
//******************************************************************
initial begin
	$fsdbDumpfile("tb.fsdb");
	$fsdbDumpvars();
end

//===================================================
// u_fifoif from rtl/fifoif.v                        
//===================================================

fifoif u_fifoif (
    .fifo_flush                 ( 1'b0 ),
    .data_out                   ( data_out ),
    .full_out                   ( full_out ),
    .empty_out                  ( empty_out ),
    .data_in                    ( data_in ),
    .wren_in                    ( wren_in ),
    .wclk                       ( wclk ),
    .wclr_in                    ( wr_rst_n ),
    .rden_in                    ( rden_in ),
    .rclk                       ( rclk ),
    .rclr_in                    ( rd_rst_n ),
    .fifo_waddr                 ( fifo_waddr ),
    .fifo_raddr                 ( fifo_raddr ),

     // Memory interface
    .ram_douta                  ( if_ram_douta ),
    .ram_dinb                   ( if_ram_dinb ),
    .ram_ada                    ( if_ram_ada ),
    .ram_adb                    ( if_ram_adb ),
    .ram_cena                   ( if_ram_cena ),
    .ram_cenb                   ( if_ram_cenb ),
    .ram_clka                   ( if_ram_clka ),
    .ram_clkb                   ( if_ram_clkb )
);

ram_dp u_ram_dp(
                .data_out           (if_ram_douta[31:0]), //A data output, 32 bits
                .data_in            (if_ram_dinb[31:0]),  //B data input , 32 bits
                .addr_a             (if_ram_ada[6:0]),    //A adress, 7 bits
                .addr_b             (if_ram_adb[6:0]),    //B adress, 7 bits
                .web                (if_ram_cenb),        //Write Enable/Read Enable,addr_b write enable, low active
                .clka               (if_ram_clka),        // write  Clock Input
                .clkb               (if_ram_clkb),        // read   Clock Input
                .cena               (if_ram_cena),        //Chip Select ,low active
                .cenb               (if_ram_cenb)         //Chip Select ,low active
                ); 

endmodule

clock.v

module clock #(parameter   CLK_FREQ = 100.0)  //MHz
           (
            output    reg   clk
            );

    localparam  CLK_CYCLE = 1000.0 / CLK_FREQ;
    
    initial
    begin
        clk = 0;
        forever #(CLK_CYCLE/2)
            clk = ~clk;
    end
    
endmodule

fifo_data.v

module fifo_data #(parameter DATA_WIDTH = 8)
                (
                 output reg                      wr_rst_n,
                 input                           wr_clk,
                 output reg                      wr_en,
                 output reg  [DATA_WIDTH-1:0]    wr_data,
                 input                           wr_full,
                 output reg                      rd_rst_n,
                 input                           rd_clk,
                 output reg                      rd_en,
                 input                           rd_empty
                );
    
    
    reg             normal_wr;
    reg             normal_rd;
    
    initial
    begin
        wr_rst_n  = 1'b0;
        rd_rst_n  = 1'b0;
        normal_wr = 1'b0;
        normal_rd = 1'b0;
        #492;
        wr_rst_n  = 1'b1;
        rd_rst_n  = 1'b1;
        #100;

        //only write FIFO
        normal_rd = 1'b0;
        normal_wr = 1'b1;
        repeat(500) @(negedge wr_clk);
        
        //only read FIFO
        normal_wr = 1'b0;
        normal_rd = 1'b1;
        repeat(500) @(negedge rd_clk);
        
        //read and write FIFO
        normal_rd = 1'b0;
        normal_wr = 1'b0;

        normal_wr = 1'b1;
        normal_rd = 1'b1;
        repeat(1000) @(negedge wr_clk);
        
        normal_wr = 1'b0;
        normal_rd = 1'b0;
        repeat(50) @(negedge rd_clk);
        $finish;
    end
    
    //******************************************************************
    //  write FIFO data generate
    //******************************************************************
    always @(negedge wr_clk or negedge wr_rst_n)
    begin
        if(wr_rst_n == 1'b0)
        begin
            wr_en   <= 1'b0;
            wr_data <= {(DATA_WIDTH){1'b0}};
        end
        else if(normal_wr == 1'b1)
        begin
            if(wr_full == 1'b0)
            begin
                wr_en   <= 1'b1;
                wr_data <= {$random%((1 << (DATA_WIDTH-1)))};
                //wr_data <= $random;
            end
            else
            begin
                wr_en   <= 1'b0;
                wr_data <= {(DATA_WIDTH){1'b0}};
            end
        end
        else
        begin
            wr_en   <= 1'b0;
            wr_data <= {(DATA_WIDTH){1'b0}};
        end
    end
    
    //******************************************************************
    //  read FIFO data generate
    //******************************************************************
    always @(negedge wr_clk or negedge wr_rst_n)
    begin
        if(wr_rst_n == 1'b0)
            rd_en   <= 1'b0;
        else if(normal_rd == 1'b1)
        begin
            if(rd_empty == 1'b0)
                rd_en   <= 1'b1;
            else
                rd_en   <= 1'b0;
        end
        else
            rd_en   <= 1'b0;
    end
    
endmodule

参考资料


[0].Simulation and Synthesis Techniques for Asynchronous FIFO Design
[1].Memories
[2].异步FIFO的FPGA实现
[3].异步FIFO的设计
[4].基于FPGA的异步FIFO设计
[5].基于FPGA的异步FIFO验证