集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 933|回复: 0

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

[复制链接]
d643189658 发表于 2017-8-11 18:00:07 | 显示全部楼层 |阅读模式
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  [31:0]     ram[15:0]     ; //
always @ (posedge af_wclk)
begin
    if (af_wr_en == 1'b1)
        ram[wptr_wclk[3:0]] <= # 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[rptr_rclk[3:0]] ;

// 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[4],~rptr_sp2[3],rptr_sp2[2:0]}) ;
assign af_empty = (rptr_gray == wptr_sp2) ;

assign wptr_bin[4]  =   wptr_sp2[4] ;
assign wptr_bin[3]  =   (^wptr_sp2[4:3]) ;
assign wptr_bin[2]  =   (^wptr_sp2[4:2]) ;
assign wptr_bin[1]  =   (^wptr_sp2[4:1]) ;
assign wptr_bin[0]  =   (^wptr_sp2[4:0]) ;

assign rptr_bin[4]  =   rptr_sp2[4] ;
assign rptr_bin[3]  =   (^rptr_sp2[4:3]) ;
assign rptr_bin[2]  =   (^rptr_sp2[4:2]) ;
assign rptr_bin[1]  =   (^rptr_sp2[4:1]) ;
assign rptr_bin[0]  =   (^rptr_sp2[4:0]) ;

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 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2025-4-20 12:40 , Processed in 0.063121 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表