[[Verilog code asynchronous FIFO design explanation + source code + tb]]

Design of asynchronous FIFO

Use Gray code to determine whether it is really full
Used a very clever method
Using gray code solves one problem, but it also brings another problem, that is, how to judge empty and full in the Gray code domain.
The judgment of “empty” is still based on the fact that the two are completely equal (including MSB);
As for the judgment of “full”, as shown in the figure below, since the gray code has the characteristics of mirror symmetry except for the MSB, when the read pointer points to 7 and the write pointer points to 8, except for the MSB, the other bits are the same, so it cannot be said to be full. Therefore, we cannot simply detect the highest bit. To judge the gray code as full, the following three conditions must be met at the same time:
The MSBs of wptr and synchronized rptr are not equal because wptr must be folded back one more time than rptr.
The second highest bits of wptr and rptr are not equal. As shown in the figure above, positions 7 and 15 are converted into binary and correspond to 0111 and 1111. Different MSBs indicate one more fold, and the same 111 represents the same position.
The remaining bits are exactly equal.
explain :
Let’s see that 7 is 0_100. If we use the original judgment, we will find that the next 8 is 1_100 except that the first one is different, but it is obvious that we are not different from each other.
Next, we observe that 15-7=8. In fact, what we need is 8. In fact, it is the maximum depth that the 3-bit width we set can provide, and we will compare it with Gray code.
The first one we got is the same, the second highest one is different, and the next ones are the same.

Source code asychronous.v

// asynchronous
module asyn #(
    parameter WIDTH = 8 ,
    parameter DEEPTH = 16 , // I just try 16
    parameter ADDR_WIDTH = clogb2(DEEPTH)
    // just give up PROG_EMPTY
  )(
    input wr_clk,
    input rd_clk,
    input wr_en,
    input rd_en,
    input wr_rst_n ,
    input rd_rst_n ,
    input [WIDTH-1 : 0 ] din ,
    output reg wfull ,
    output reg wetty ,
    output reg [WIDTH-1 : 0 ] dout
  );



  // ================================================ ===================================//
  // define parameter and internal signals //
  //================================================ ====================================//
  reg [WIDTH-1 : 0] ram[DEEPTH-1 : 0] ; // this is ram
  reg [ADDR_WIDTH : 0] wr_addr ; // we must set more bit wode add exact bit + address
  reg [ADDR_WIDTH : 0] rd_addr ; // we must set more bit wode
  wire [ADDR_WIDTH - 1 : 0] wr_addr1 ;
  wire [ADDR_WIDTH - 1 : 0] rd_addr1;
  assign wr_addr1 = wr_addr[ADDR_WIDTH - 1 : 0] ; // we must put address and addr_pointer together
  assign rd_addr1 = rd_addr[ADDR_WIDTH - 1 : 0] ;

  // gray
  wire [ADDR_WIDTH : 0] wr_addr_gray;
  wire [ADDR_WIDTH : 0] rd_addr_gray;
  // B 2 Gray code
  assign wr_addr_gray = wr_addr ^ (wr_addr >> 1) ;
  assign rd_addr_gray = rd_addr ^ (rd_addr >> 1);

  //synchronous
  reg [ADDR_WIDTH : 0] wr_addr_gray1;
  reg [ADDR_WIDTH : 0] wr_addr_gray2;
  //synchronous
  reg [ADDR_WIDTH : 0] rd_addr_gray1;
  reg [ADDR_WIDTH : 0] rd_addr_gray2;





  //================================================ ======================================//
  // next is main code //
  //================================================ ========================================//
  function integer clogb2;
    input [31:0] value;
    begin
      value = value - 1;
      for( clogb2 = 0 ; value > 0 ; clogb2 = clogb2 + 1)
        value = value >>1;
    end
  endfunction

  //------------------------------------------------ ------------------wr_addr
  //--the pointer is another thing
  always@(posedge wr_clk or negedge wr_rst_n)
  begin
    if(wr_rst_n == 0)
    begin
      wr_addr <= 0 ;
    end
    else if( wr_en & amp; & amp; !wfull)
    begin
      wr_addr <= wr_addr + 1'b1;
    end
    else
      wr_addr <= wr_addr;
  end

  //------------------------------------------------ ----------------------- rd_addr
  always@(posedge rd_clk or negedge rd_rst_n)
  begin
    if(rd_rst_n == 0)
    begin
      rd_addr <= 0;
    end
    else if( rd_en & amp; & amp; !wempty)
    begin
      rd_addr <= rd_addr + 1'b1;
    end
    else
      rd_addr <= rd_addr;
  end

  //------------------------------------------------ -------------------------- write to ram
  //wr_addr1
  always@(posedge wr_clk or negedge wr_rst_n)
  begin
    if(wr_rst_n == 0 )
    begin
      ram[wr_addr1] <= 0;
    end
    else if(wr_en & amp; & amp; !wfull)
    begin
      ram[wr_addr1] <= din;
    end
    else
    begin
      ram[wr_addr1] <= ram[wr_addr1];
    end
  end

  //------------------------------------------------ ----------------------------------read to ram
  // rd_addr1 [ADDR_WIDTH-1 : 0] this is used to which need reed
  always@(posedge rd_clk or negedge rd_rst_n)
  begin
    if(rd_rst_n == 0)
    begin
      dout <= 0 ;
    end
    else if( rd_en & amp; & amp; !wempty)
    begin
      dout <= ram[rd_addr1];
    end
    else
    begin
      dout <= dout ;
    end
  end

  //------------------------------------------------ ----------------------------------This is what sysnchronus need
  //------------------------------------------------ -------------------------------------------------- --
  //-- next is change to gary and send to the same time clock //
  //------------------------------------------------ -------------------------------------------------- ----\

  //synchronous
  //reg [ADDR_WIDTH : 0] wr_addr_gray1 ;
  //reg [ADDR_WIDTH : 0] wr_addr_gray2 ;
  //synchronous
  //reg [ADDR_WIDTH : 0] rd_addr_gray1;
  // reg [ADDR_WIDTH : 0] rd_addr_gray2 ;

  // this is pointer point
  // full use reed synchronous write
  // empty use write synchronous read

  //------------------------------------------------ -------------------------------------------------- ----\


  // write pointer synchronous read clk ----- empty
  always@(posedge rd_clk or negedge rd_rst_n)
  begin
    if(rd_rst_n == 0)
    begin
      wr_addr_gray1 <= 0 ;
      wr_addr_gray2 <= 0 ;
    end
    else
    begin
      wr_addr_gray1 <= wr_addr_gray;
      wr_addr_gray2 <= wr_addr_gray1;
    end
  end


  // read pointer sunchronous write clk ----- full
  always@(posedge wr_clk or negedge wr_rst_n)
  begin
    if(wr_rst_n == 0)
    begin
      rd_addr_gray1 <= 0;
      rd_addr_gray2 <= 0;
    end
    else
    begin
      rd_addr_gray1 <= rd_addr_gray;
      rd_addr_gray2 <= rd_addr_gray1;
    end
  end

  //------------------------------------------------ ---empty full determine

  //empty
  always@(*)
  begin
    if( rd_rst_n == 0 )
    begin
      wetty <= 0 ;
    end
    else if(rd_addr_gray == wr_addr_gray2)
    begin
      wetty <= 1 ;
    end
    else
    begin
      wetty <= 0 ;
    end
  end



  // full
  always@(*)
  begin
    if(wr_rst_n == 0)
    begin
      wfull <= 0 ;
    end
    else if( wr_addr_gray == {<!-- -->~rd_addr_gray2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_gray2[ADDR_WIDTH-2 : 0] } )
    begin
      wfull <= 1;
    end
    else
    begin
      wfull <= 0 ;
    end
  end

endmodule

asychro_tb.v

`timescale 1ns/1ps
module asych_tb #(
    parameter WIDTH = 8 ,
    parameter DEEPTH = 8 ,
    parameter ADDR_WIDTH = 3
  );
  reg wr_clk;
  reg rd_clk;
  reg wr_en;
  reg rd_en;
  reg wr_rst_n;
  reg rd_rst_n;
  reg [WIDTH-1 : 0 ] din ;
  wire full;
  wire wetty;
  wire [WIDTH-1 : 0 ] dout ;


  asyn#(
        .WIDTH ( WIDTH ),
        .DEEPTH (DEEPTH),
        .ADDR_WIDTH ( ADDR_WIDTH )
      )u_asyn(
        .wr_clk ( wr_clk ),
        .rd_clk ( rd_clk ),
        .wr_en ( wr_en ),
        .rd_en ( rd_en ),
        .wr_rst_n ( wr_rst_n ),
        .rd_rst_n ( rd_rst_n ),
        .din (din),
        .wfull ( wfull ),
        .wempty ( wempty ),
        .dout (dout)
      );




  always #10 wr_clk = ~wr_clk;
  always #5 rd_clk = ~rd_clk;



  initial
  begin
    wr_clk = 0;
    wr_rst_n = 1;
    wr_en = 0;


    rd_clk = 0;
    rd_rst_n = 1;
    rd_en = 0;

    #10
     wr_rst_n = 0;
    rd_rst_n = 0;

    #10
     wr_rst_n = 1;
    rd_rst_n = 1;


    // next is write
    wr_en = 1;
    rd_en = 0;

    //only write
    wr_en = 1;
    rd_en = 0;
din = 1;
    repeat(10)
    begin
      @(negedge wr_clk)
       begin
         din = {<!-- -->$random}0;
       end
     end

     //only read
     wr_en = 0;
    rd_en = 1;

    repeat(10)
    begin
      @(negedge rd_clk);

    end
    rd_en = 0;


//read and write
    wr_en = 0;
    rd_en = 0;
    #80;
    wr_en = 1;
    rd_en = 1;

    repeat(20) begin
        @(negedge wr_clk) begin
            din = {<!-- -->$random}0;
        end
    end

end


endmodule