d643189658 发表于 2017-8-11 18:00:07

Verilog基本电路设计之三: 异步FIFO

Verilog基本电路设计之三: 异步FIFO

FIFO用于为匹配读写速度而设置的数据缓冲buffer,当读写时钟异步时,就是异步FIFO。多bit的数据信号,并不是直接从写时钟域同步到读时钟域的,而是读写时钟域分别派遣了一个信使,去通知对方时钟域,当前本方所处的读写情况,来判断还能不能写以及可不可以读,这两个信使就是读写指针。

在《Verilog基本电路设计之一》里已讨论过,即使单bit的异步信号,通过两个相同的同步电路,达到clkb域时都可能“长”的不是一个模样,更加不用说多bit的异步信号同时传递到clkb域会变成什么五花八门的模样了。这里读写指针不是单bit信号,它们如何向对方时钟域去同步呢?格雷码!它的特点是每次只有一个bit发生变化,这样就把多bit信号同步转变为了单bit信号同步,这也是为什么多bit的格雷码信号,可以类似于单bit信号那样,直接使用两级DFF去同步的根本原因。

下面给出异步FIFO的主体部分,同样,省略了信号声明定义。

module asyn_fifo (
      // input
      af_wclk , // async-FIFO clear in write clock
      af_rclk , // async-FIFO clear in read clock
      rst_n,    // system reset
      af_wr_en, // async-FIFO write enable
      af_rd_en, // async-FIFO read enable
      af_dati,// async-FIFO data in
      //output            
      af_full , // Async-FIFO full flag
      af_empty, // Async-FIFO empty flag
      af_dato   // Async-FIFO data out
      );

//------------------------- data input --------------------------
assign nxt_wptr_wclk = (af_wr_en && !af_full) ? (wptr_wclk + 1'b1) : wptr_wclk ;
assign nxt_wptr_gray = (nxt_wptr_wclk >> 1) ^ nxt_wptr_wclk ;

always @ (posedge af_wclk or negedge rst_n)
begin
    if (rst_n == 1'b0) begin
      wptr_wclk <= # DLY 5'b0 ;
      wptr_gray <= # DLY 5'b0 ;
    end
    else begin
      wptr_wclk <= # DLY nxt_wptr_wclk ;
      wptr_gray <= # DLY nxt_wptr_gray ;
    end
end

reg   ram   ; //
always @ (posedge af_wclk)
begin
    if (af_wr_en == 1'b1)
      ram] <= # DLY af_dati ;
    else ;
end

//------------------------ data output ---------------------------
assign nxt_rptr_rclk = (af_rd_en && !af_empty) ? (rptr_rclk + 1'b1) : rptr_rclk ;
assign nxt_rptr_gray = (nxt_rptr_rclk >> 1) ^ nxt_rptr_rclk ;

always @ (posedge af_rclk or negedge rst_n)
begin
    if (rst_n == 1'b0) begin
      rptr_rclk <= # DLY 5'b0 ;
      rptr_gray <= # DLY 5'b0 ;
    end
    else begin
      rptr_rclk <= # DLY nxt_rptr_rclk ;
      rptr_gray <= # DLY nxt_rptr_gray ;
    end
end

assign af_dato = ram] ;

// sync read pointer
always @ (posedge af_wclk or negedge rst_n)
begin
    if (rst_n == 1'b0) begin
      rptr_sp1<= # DLY 5'b0 ;
      rptr_sp2<= # DLY 5'b0 ;
    end
    else begin
      rptr_sp1<= # DLY rptr_gray ;
      rptr_sp2<= # DLY rptr_sp1;
    end
end

// sync write pointer
always @ (posedge af_rclk or negedge rst_n)
begin
    if (rst_n == 1'b0) begin
      wptr_sp1<= # DLY 5'b0 ;
      wptr_sp2<= # DLY 5'b0 ;
    end
    else begin
      wptr_sp1<= # DLY wptr_gray ;
      wptr_sp2<= # DLY wptr_sp1;
    end
end

assign af_full= (wptr_gray == {~rptr_sp2,~rptr_sp2,rptr_sp2}) ;
assign af_empty = (rptr_gray == wptr_sp2) ;

assign wptr_bin=   wptr_sp2 ;
assign wptr_bin=   (^wptr_sp2) ;
assign wptr_bin=   (^wptr_sp2) ;
assign wptr_bin=   (^wptr_sp2) ;
assign wptr_bin=   (^wptr_sp2) ;

assign rptr_bin=   rptr_sp2 ;
assign rptr_bin=   (^rptr_sp2) ;
assign rptr_bin=   (^rptr_sp2) ;
assign rptr_bin=   (^rptr_sp2) ;
assign rptr_bin=   (^rptr_sp2) ;

assign af_wlevel = wptr_wclk - rptr_bin ;
assign af_rlevel = wptr_bin - rptr_rclk ;
assign af_half_full = (af_rlevel >= 5'h7) ;

endmodule

上面给出的是深度16,宽度32的示例,大家可以使用parameter参数化定义深度和宽度,方便不同需求下的调用。除了空满信号标志,也可以根据需要做出半空半满之类信号。上面需要注意的一点就是,格雷码必须在本时钟域下DFF输出,再往另一个时钟域同步。同步FIFO呢,就不用有格雷码转换,设计更加简单,就不专门开贴述了。
页: [1]
查看完整版本: Verilog基本电路设计之三: 异步FIFO