Verilog异步FIFO实现

 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,
                  output                          full
    );


reg    [addr_width:0]    wr_addr_ptr;//地址指针,比地址多一位,MSB用于检测在同一圈
reg    [addr_width:0]    rd_addr_ptr;
wire   [addr_width-1:0]  wr_addr;//RAM 地址
wire   [addr_width-1:0]  rd_addr;

wire   [addr_width:0]    wr_addr_gray;//地址指针对应的格雷码
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;//fifo复位后输出总线上是0,并非ram中真的复位。可无
				 end//write_ram_init
       else if(wr_en && (~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 && (~empty))
         begin
            dout <= fifo_ram[rd_addr];
            valid <= 1'b1;
         end
      else
         begin
            dout <=  dout;//fifo复位后输出总线上是0,并非ram中真的复位,只是让总线为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];
//=============================================================格雷码同步化
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 && (~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 && (~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]}) ;//高两位不同
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anndy.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值