Verilog asynchronous FIFO implementation

 module fifo_async#(
                 parameter data_width = 32,
                 parameter data_depth = 8,
                 parameter addr_width = 4
)
(
                  input rst_n,
                  input wr_clk,
                  input wr_en,
                  input [data_width-1:0] din,
                  input rd_clk,
                  input rd_en,
                  output reg valid,
                  output reg [data_width-1:0] dout,
                  output empty,
                  full output
    );


reg [addr_width:0] wr_addr_ptr;//Address pointer, one more bit than the address, MSB is used to detect in the same circle
reg [addr_width:0] rd_addr_ptr;
wire [addr_width-1:0] wr_addr;//RAM address
wire [addr_width-1:0] rd_addr;

wire [addr_width:0] wr_addr_gray;//Gray code corresponding to the address pointer
reg [addr_width:0] wr_addr_gray_d1;
reg [addr_width:0] wr_addr_gray_d2;
wire [addr_width:0] rd_addr_gray;
reg [addr_width:0] rd_addr_gray_d1;
reg [addr_width:0] rd_addr_gray_d2;


reg [data_width-1:0] fifo_ram [data_depth-1:0];

//================================================ =========write fifo


always@(posedge wr_clk or negedge rst_n)
    begin:write_ram
integer i;
       if(!rst_n)
for( i = 0; i < data_depth; i = i + 1 ) begin:write_ram_init
          fifo_ram[i] <= 'h0;//After fifo is reset, the output bus is 0, which is not a real reset in ram. Dispensable
end//write_ram_init
       else if(wr_en & amp; & amp; (~full))
          fifo_ram[wr_addr] <= din;
       else
          fifo_ram[wr_addr] <= fifo_ram[wr_addr];
    end//write_ram

//================================================ ========read_fifo
always@(posedge rd_clk or negedge rst_n)
   begin
      if(!rst_n)
         begin
            dout <= 'h0;
            valid <= 1'b0;
         end
      else if(rd_en & amp; & amp; (~empty))
         begin
            dout <= fifo_ram[rd_addr];
            valid <= 1'b1;
         end
      else
         begin
            dout <= dout;//After fifo is reset, the output bus is 0. It is not really reset in ram, it just makes the bus 0;
            valid <= 1'b0;
         end
   end
assign wr_addr = wr_addr_ptr[addr_width-1-:addr_width];
assign rd_addr = rd_addr_ptr[addr_width-1-:addr_width];
//================================================ =============Gray code synchronization
always@(posedge wr_clk)
   begin
      rd_addr_gray_d1 <= rd_addr_gray;
      rd_addr_gray_d2 <= rd_addr_gray_d1;
   end
always@(posedge wr_clk or negedge rst_n)
   begin
      if(!rst_n)
         wr_addr_ptr <= 'h0;
      else if(wr_en & amp; & amp; (~full))
         wr_addr_ptr <= wr_addr_ptr + 1;
      else
         wr_addr_ptr <= wr_addr_ptr;
   end
//================================================ =========rd_clk
always@(posedge rd_clk)
      begin
         wr_addr_gray_d1 <= wr_addr_gray;
         wr_addr_gray_d2 <= wr_addr_gray_d1;
      end
always@(posedge rd_clk or negedge rst_n)
   begin
      if(!rst_n)
         rd_addr_ptr <= 'h0;
      else if(rd_en & amp; & amp; (~empty))
         rd_addr_ptr <= rd_addr_ptr + 1;
      else
         rd_addr_ptr <= rd_addr_ptr;
   end

//================================================ ========== translation gary code
assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;

assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//The upper two digits are different
assign empty = ( rd_addr_gray == wr_addr_gray_d2 );

endmodule

module fifo_async_tb();
                        
  reg tb_rst_n;
  reg tb_wr_clk;
  reg tb_wr_en;
  reg [31:0] tb_din;
  reg tb_rd_clk;
  reg tb_rd_en;
  wire tb_valid;
  wire [31:0] tb_dout;
  wire tb_empty;
  wire tb_full;

  task delay;
    input [31:0] num;
    begin
      repeat(num) @(posedge tb_wr_clk);
      #1;
    end
  endtask

  task delay1;
    input [31:0] num;
    begin
      repeat(num) @(posedge tb_rd_clk);
      #1;
    end
  endtask

  initial begin
    tb_wr_clk = 0;
  end
  always #40 tb_wr_clk = ~tb_wr_clk;

  initial begin
    tb_rd_clk = 0;
  end
  always #10 tb_rd_clk = ~tb_rd_clk;

  initial begin
    tb_rst_n = 1;
    delay(1);
    tb_rst_n = 0;
    delay(1);
    tb_rst_n = 1;
  end

  initial begin
    $dumpfile(" async_fifo_tb.vcd ");
    $dumpvars();
  end

  initial begin
    tb_wr_en = 0;
    tb_din = 0;
    tb_rd_en = 0;
    delay(3);
    // write data
    tb_wr_en = 1;
    tb_din = 32'haaaa;
    delay(1);
    tb_wr_en = 0;
    delay(5);
    // read data, empty
    tb_rd_en = 1;
    delay1(1);
    tb_rd_en = 0;
    delay1(5);
    // write data1
    tb_wr_en = 1;
    tb_din = 32'hbbbb;
    delay(1);
    tb_wr_en = 0;
    delay(5);
    // write data2
    tb_wr_en = 1;
    tb_din = 32'hcccc;
    delay(1);
    tb_wr_en = 0;
    delay(5);
    // write data3
    tb_wr_en = 1;
    tb_din = 32'hdddd;
    delay(1);
    tb_wr_en = 0;
    delay(5);
    // write data4, full
    tb_wr_en = 1;
    tb_din = 32'heeee;
    delay(1);
    tb_wr_en = 0;
    delay(5);
    // read data1
    tb_rd_en = 1;
    delay1(1);
    tb_rd_en = 0;
    delay1(5);
    // read data2
    tb_rd_en = 1;
    delay1(1);
    tb_rd_en = 0;
    delay1(5);
    // read data3
    tb_rd_en = 1;
    delay1(1);
    tb_rd_en = 0;
    delay1(5);
    // read data4, empty
    tb_rd_en = 1;
    delay1(1);
    tb_rd_en = 0;
    delay1(5);
    delay(5);
    $finish;
  end
fifo_async#(
                 .data_width(32) ,
                 .data_depth(4) ,
                 .addr_width(2)
) dut1_fifo_async
(
  .rst_n (tb_rst_n),
  .wr_clk(tb_wr_clk),
  .wr_en ( tb_wr_en ) ,
  .din ( tb_din ) ,
  .rd_clk(tb_rd_clk),
  .rd_en (tb_rd_en),
  .valid(tb_valid),
  .dout (tb_dout),
  .empty (tb_empty),
  .full (tb_full)
    );


endmodule