在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 46139|回复: 76

[原创] Verilog基本电路设计之三(异步FIFO)

[复制链接]
发表于 2016-6-18 12:33:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 杰克淡定 于 2016-6-18 12:36 编辑

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呢,就不用有格雷码转换,设计更加简单,就不专门开贴描述了。
发表于 2016-6-18 13:58:59 | 显示全部楼层
请教楼主,如果异步FIFO不是2的整数倍次方,该如何处理?
 楼主| 发表于 2016-6-18 16:26:41 | 显示全部楼层
本帖最后由 杰克淡定 于 2016-6-18 16:28 编辑

回复 2# 老阮

你的问题很犀利,嗯。比如说,深度改成6
二进制码跳变:   0000 0001 0010 0011 0100 0101 -- > 1000 1001 1010 1011 1100 1101
对应格雷码选择:0000 0001 0011 0111 0101 0100 -- > 1100 1101 1111 1011 1001 1000


此时格雷码的选择还是必须保证每次只能有一个bit的改变,而且满标志的产生,还能使用高位两个bit取反,低位bit相等来产生,但此时的格雷码似乎没有简单计算公式,只能方便使用查找表来映射得到,即
case (nxt_wptr_wclk)
     4'b0000 : nxt_wptr_gray = 4'b0000 ;
     4'b0001 : nxt_wptr_gray = 4'b0001 ;
     4'b0010 : nxt_wptr_gray = 4'b0011 ;
     4'b0011 : nxt_wptr_gray = 4'b0111 ;
     4'b0100 : nxt_wptr_gray = 4'b0101 ;
     4'b0101 : nxt_wptr_gray = 4'b0100 ;
     ……
     ……
endcase


如果指针位宽更多,格雷码的选择和映射也更麻烦。所以我觉得,宁可浪费几层FIFO使得其深度为2的n次方,也好过费力去设计选择合适的格雷码变换。

发表于 2016-6-18 18:31:40 | 显示全部楼层
回复 3# 杰克淡定


   明白了,多谢您的解答!
发表于 2016-6-19 09:22:48 | 显示全部楼层
其实原理上还是原来的异步处理原则,如果另一个时钟阈的信号要在目标时钟阈被处理(文中是被比较),那么必须同步过去,而是用格雷码的意义文中说了(就是将本来要多比特传递的计数信息以单比特的方法传递),概括不对请指正
发表于 2016-6-20 09:33:26 | 显示全部楼层
您说:上面需要注意的一点就是,格雷码必须在本时钟域下DFF输出,再往另一个时钟域同步。
也就是说代码不能写成下面这样:
always @ (posedge af_wclk or negedge rst_n)
begin
    if (rst_n == 1'b0) begin
        wptr_wclk <= # DLY 5'b0 ;
    end
    else if(af_wr_en && !af_full)begin
        wptr_wclk <= # DLY (wptr_wclk + 1'b1) ;
    end
end

assign wptr_gray = (wptr_wclk >> 1) ^ wptr_wclk ;

如果写成这样会有什么问题?
发表于 2016-6-20 11:03:29 | 显示全部楼层
请问楼主,对于同频不同相的async FIFO, 最小深度应该为多少才能保证没有throughput issue?
 楼主| 发表于 2016-6-20 11:30:47 | 显示全部楼层
回复 6# haimo

我看到你在 Verilog基本电路设计之一的帖子中也参与了讨论,你现在还问这个问题,说明你还没有完全理解第一篇帖子中,关于单bit异步信号同步处理的首要原则:被同步信号必须是前一个时钟域的DFF信号,详细原因请看前贴。这里为了给你直观理解,我给一个布线完成后的仿真图形给你看看。

sync.jpg



nxt_wptr_wclk_5_是wclk下的组合逻辑信号,wptr_gray[5]是wclk下的DFF信号。图中的wptr_sp1[5]是用rclk抓wptr_gray[5]一拍后得到的。假如rclk直接去抓组合逻辑信号nxt_wptr_wclk_5_,你觉得还能得到图中wptr_sp1[5]这么“干净”的信号么?如果rclk刚好抓到了nxt_wptr_wclk_5_上的尖峰毛刺呢?
 楼主| 发表于 2016-6-20 12:26:27 | 显示全部楼层
回复 7# yaya126

   你是希望一旦读写开始,数据流就能连续不间断处理么。其实就是考虑这样一个问题:一个进水口,一个出水口,先开启进水口等水池有一半水时,再打开出水口。假如进水速度和出水速度每时每刻完全一样,那么水池永远也不会变满,也不会流空,类似于FIFO永远不会写爆也不会读爆。假如进水和出水只是平均速度一样,但某些短时间统计上会存在差异,那么水池就有可能出现变满或者流空,短时间进出水差异越大,这种变满或者流空的概率就越大。为了不让它变满或者流空,你只能加大水池容量。


   具体到你的问题,与你的两个时钟是否来自于相同源头,以及频偏jitter等有关系。两个时钟频率一样,其实是指在一段较长时间内,平均频率是一样的,但对某一段很短的时间窗口来看,频率会存在差异。如果是PLL出来的两个同频时钟信号,其频偏差异,一般来说使用8层FIFO是可以应对的。当然设计能够容忍FIFO读写数据间断,也就不用考虑这么多了。
发表于 2016-6-20 13:20:48 | 显示全部楼层
本帖最后由 haimo 于 2016-6-20 13:28 编辑

回复 8# 杰克淡定

但是在我给出代码中 wptr_gray = (wptr_wclk >> 1) ^ wptr_wclk ;
而wptr_wclk是dff输出,所以wptr_gray是没有毛刺的,对吗?
还是有毛刺的,因为用了组合逻辑。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-11-24 07:14 , Processed in 0.021370 second(s), 7 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表