【【verilog代码异步FIFO的设计解释+源码+tb】】

异步FIFO的设计

使用格雷码 判断 是否是真的满员
用了很巧的办法
使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。
对于“空”的判断依然依据二者完全相等(包括MSB);
而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:
wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
剩下的其余位完全相等。
解释一下 :
我们来看 7是 0_100 要是用原来的判别 我们会发现 下一个8 是 1_100 除了 第一位不同 但是很明显 我们并没有相差一圈
接下来 我们 观察 15-7=8 其实 我们正需要的是这样的8 其实是我们设置的3位宽所能提供的最大的 深度 而我们将比照 格雷码
才得出的首位相同 次高位不同 接下来都相同

源码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                        wempty          ,
    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 && !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 && !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 && !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 && !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
      wempty <= 0 ;
    end
    else if(rd_addr_gray == wr_addr_gray2)
    begin
      wempty <= 1 ;
    end
    else
    begin
      wempty <= 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                              wfull           ;
  wire                              wempty          ;
  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}%30  ;
       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}%30;
        end
    end

end


endmodule  

你可能感兴趣的:(FPGA学习,fpga开发)