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

标题: Verilog基本电路设计之三(异步FIFO) [打印本页]

作者: 杰克淡定    时间: 2016-6-18 12:33
标题: Verilog基本电路设计之三(异步FIFO)
本帖最后由 杰克淡定 于 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
请教楼主,如果异步FIFO不是2的整数倍次方,该如何处理?
作者: 杰克淡定    时间: 2016-6-18 16:26
本帖最后由 杰克淡定 于 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
回复 3# 杰克淡定


   明白了,多谢您的解答!
作者: 07048097    时间: 2016-6-19 09:22
其实原理上还是原来的异步处理原则,如果另一个时钟阈的信号要在目标时钟阈被处理(文中是被比较),那么必须同步过去,而是用格雷码的意义文中说了(就是将本来要多比特传递的计数信息以单比特的方法传递),概括不对请指正
作者: haimo    时间: 2016-6-20 09:33
您说:上面需要注意的一点就是,格雷码必须在本时钟域下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 ;

如果写成这样会有什么问题?
作者: yaya126    时间: 2016-6-20 11:03
请问楼主,对于同频不同相的async FIFO, 最小深度应该为多少才能保证没有throughput issue?
作者: 杰克淡定    时间: 2016-6-20 11:30
回复 6# haimo

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

( , 下载次数: 272 )



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
回复 7# yaya126

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


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

回复 8# 杰克淡定

但是在我给出代码中 wptr_gray = (wptr_wclk >> 1) ^ wptr_wclk ;
而wptr_wclk是dff输出,所以wptr_gray是没有毛刺的,对吗?
还是有毛刺的,因为用了组合逻辑。
作者: yaya126    时间: 2016-6-20 13:38
回复 9# 杰克淡定
这是之前别人问我的一个问题, Depth>=8有可能多了,我觉得这个问题的实质考虑的是,一个写信号,如何传递到读时钟域影响not_empty, 再回传到写时钟域影响not_full, 3拍过去,3拍回来,6好像就可以了。我需要做个仿真看看。
另外共享两篇SNUG关于ASYNCFIFO的文章,开拓下思路,一篇和本文思路一致,另一篇思路较新颖。

( , 下载次数: 95 ) ( , 下载次数: 86 )
作者: 杰克淡定    时间: 2016-6-20 13:44
回复 10# haimo

wptr_gray = (wptr_wclk >> 1) ^ wptr_wclk ;
wptr_wclk是dff输出,wptr_gray一样有毛刺,这是组合逻辑。


建议你去把数字逻辑电路基础的竞争冒险复习一下,对你的设计提高有好处。
作者: haimo    时间: 2016-6-20 15:06
回复 12# 杰克淡定

想明白了,谢谢。受益匪浅
作者: 浩然若枫    时间: 2016-6-21 08:58
很不错,学习了。
LZ准备搞个系列吗?大力支持呀……
作者: cqugjw    时间: 2016-6-21 17:15
回复 7# yaya126

同频不同相就是async的,而不是sync的了,另外深度是要看你的应用场景的。
作者: cqugjw    时间: 2016-6-21 17:30
我问楼主两个问题:
1. 你知道你定义的二维数组会被综合成什么电路么?
2. 你知道你组合逻辑中的‘==’会被综合成什么电路么?

另外建议你把同一个时钟域的always块写到一起,不要两个时钟域混到一起。
作者: yaya126    时间: 2016-6-21 18:36
回复 16# cqugjw
听大神高论,来科普下你的理解。
作者: 杰克淡定    时间: 2016-6-21 19:21
回复 16# cqugjw

1、这里的二维数组会综合成寄存器
2、以两个单bit的变量为例,a==b一般会直接综合成库里的同或门XNR,也可以由反向器与门或门之类组合得到,综合工具会在满足时序前提选择最小面积类型。如果是变量和常量比较,那就简单不用说了。

不过我没明白你问这个问题有何用意,或者你有何特殊看法?

最后,你说的不同时钟域分开写,的确,你的说法是正确的。规范的编码,应该把不同时钟域分成单独的.v来写,这里为了显示阅读方便而放在了一个.v。
作者: yaya126    时间: 2016-6-21 21:30
回复 15# cqugjw
我没有说是sync 吧?
作者: yaya126    时间: 2016-6-21 21:30
回复 15# cqugjw
我没有说是sync 吧?
作者: yaya126    时间: 2016-6-21 21:30
回复 15# cqugjw
我没有说是sync 吧?
作者: dyytx    时间: 2016-6-21 23:21
回复 15# cqugjw


   同一个PLL分出的时钟,同频反相算是sync还是async?
作者: yaya126    时间: 2016-6-22 10:29
回复 22# dyytx
同一PLL,同频反相算SYNC. 倍频都算。
作者: cqugjw    时间: 2016-6-22 10:44
回复 17# yaya126

额 我不是大神,交流一下。我们coding style不一样,我不用二维数组,像‘==’这种我一般也不会写,会直接写成门级的组合逻辑表达。
作者: cqugjw    时间: 2016-6-22 10:47
回复 18# 杰克淡定


   谢谢答复。1. 实际使用中会例化成block memory吧?2. 因为我习惯把==这种用异或门表达,不习惯直接写==,所以了解下。

在同一个.v里也可以把同时钟域的always块放在一起。

个人见解仅供参考。
作者: cqugjw    时间: 2016-6-22 10:48
回复 21# yaya126

sorry,是我看错了~~
作者: yaya126    时间: 2016-6-22 10:58
回复 24# cqugjw
FIFO的里面数组,不写成2维数组还真不好表示,当然你可以手动inst memory.
==和逻辑表达式没区别,综合工具会根据SDC timing约束,选择它认为的最优逻辑表达,你想综合出来的和你想象的逻辑一样,手动搭cell,并且标明DONT_TOUCH, 综合里面标明看见DONT_TOUCH就不要优化。
作者: cqugjw    时间: 2016-6-22 11:02
回复 27# yaya126

使用中还是应该例化成block memory吧,因为二维数组综合出来占用FPGA的寄存器资源,像distribution memory。
作者: 杰克淡定    时间: 2016-6-22 11:09
回复 25# cqugjw


   一般我们说的FIFO,存储体是指DFF。你如果用SRAM作存储体实现FIFO功能,那样的电路习惯称为双口RAM控制器,不再称为FIFO。当需要的存储容量较大时,会使用双口RAM代替FIFO,因为在大容量时SRAM比DFF面积优势明显。
    关于写成“==”还是直接写成逻辑,我的看法是,你觉得用直接用这个逻辑门来实现电路更优,那可以直接写成逻辑。否则没有必要,因为DC综合工具其实是很聪明的,它会自动优化选择最合适的驱动能力和面积大小的std cell,应该给工具更大的优化发挥空间。
作者: cqugjw    时间: 2016-6-22 11:16
回复 29# 杰克淡定

谢谢耐心解答,有了新的理解和认识。
作者: 杰克淡定    时间: 2016-6-22 11:22
回复 28# cqugjw


  看了回复发现,可能你主要是面向FPGA设计的。我所有的讨论,是面向IC设计的,IP设计或者SOC设计,在整个IC设计流程中,我们只在FPGA验证环节使用到FPGA,FPGA只作为IC设计的验证手段之一。所以大家的出发点可能存在差异,IC设计着眼点在于最终流片得到的芯片功能面积功耗最优,而面向FPGA设计的可能需要根据FPGA资源作出取舍。不过,FPGA设计或者IC设计,它们也有很多相通的地方。
作者: 杰克淡定    时间: 2016-6-22 13:56
回复 28# cqugjw

看了回复发现,可能你主要面向的是FPGA设计。我这里讨论的,都是面向IC设计的,以硅流片为最终形态,FPGA只是作为一种验证手段存在于整个IC设计流程的FPGA验证阶段。IC设计着眼点都以最终芯片的功能面积功耗最优为出发点,而专门面向FPGA的设计可能会根据资源作出取舍。不过这两者在RTL设计阶段很多是相通的,但是从网表以后差别就很大了
作者: cqugjw    时间: 2016-6-22 16:21
回复 31# 杰克淡定

我都有所了解。FPGA中是有built-in的fifo的,可以直接例化。
IC设计中的memory库据我了解,像two-port register file最小可以支持到4bitx8depth。
所以我一般不会采用二维数组,只是直接调用register file,个人习惯吧。
作者: 没有关系    时间: 2016-9-8 09:00
总结的很nice,正在学习异步fifo
作者: 荒漠小草    时间: 2016-9-8 14:26
学习,很受启发!!!!!!!!!
作者: 雪山一角    时间: 2016-9-9 14:31
不太明白满信号是怎么判断的?
assign af_full = (wptr_gray == {~rptr_sp2[4],~rptr_sp2[3],rptr_sp2[2:0]}) ;
作者: 杰克淡定    时间: 2016-9-9 15:52
回复 36# 雪山一角


   用格雷码直接比较得到满信号。你要是觉得难理解,转成二进制码再看看就明白了。
作者: komatsu001    时间: 2016-9-10 23:00
请问楼主,读写有可能在同时进行吗?
作者: komatsu001    时间: 2016-9-10 23:10
assign af_full  = (wptr_gray == {~rptr_sp2[4],~rptr_sp2[3],rptr_sp2[2:0]}) ; 这句话中的wptr_gray 和rptr_sp2相差连个时钟(假如读写同时变化的),会不会出现延迟问题,不知道这样说楼主是否理解,换个说法就是假如来个rptr_sp100(rptr_sp延时100个时钟),这是是不是就会出现问题。
作者: 杰克淡定    时间: 2016-9-12 09:03
回复 39# komatsu001


    当前周期的写指针与两周期前的读指针比较,只会出现FIFO实际上还没有满但是报出“满”信号,而不会出现已经写爆了才报出“满”信号。没有满但是报出满信号,是不会有问题的,相当于FIFO空间留了裕量而已。而实际已经写爆了才报出“满”信号就错了。
作者: komatsu001    时间: 2016-9-12 10:53
回复 40# 杰克淡定
哦哦 明白了,楼主还有一个问题,有没有这种情况,读写两边频率相差比较大,那么快时钟域在采慢时钟的时候可能会漏掉部分地址,假如漏掉的地址正好是满(或空)地址,这是会不会出现漏判?
作者: 诠释幸福    时间: 2016-9-15 23:34
学习中!!!
作者: 杰克淡定    时间: 2016-9-18 09:04
回复 41# komatsu001


   只有超前判出空或者满的可能,不会漏判,原因跟上面一样。
作者: amandafly    时间: 2016-9-18 10:32
谢谢楼主分享经验,祝楼主健康幸福!!
作者: gongx16    时间: 2016-9-20 20:52
受益匪浅,谢谢
作者: fengwwwye    时间: 2016-9-20 23:46
表示看不懂,还是要多学学啊!
作者: 我没逗你玩儿    时间: 2016-9-24 20:16
多谢楼在分享~~~期待更多的精品
作者: 我没逗你玩儿    时间: 2016-9-27 21:23


   
回复  老阮

你的问题很犀利,嗯。比如说,深度改成6
二进制码跳变:   0000 0001 0010 0011 0100 0101 ...
杰克淡定 发表于 2016-6-18 16:26


我觉得可以这样:
Gray    addr  
000      0
---------------
001      1
011      2
010      3
-----    ----
110      4
111      5
101      6
----------------
100      7

选择中间六个gray,读写地址为gray码对应的二进制-1
作者: haiming_2003    时间: 2016-11-1 15:48
回复 1# 杰克淡定

学习了。。。
作者: 马玉奇    时间: 2016-11-25 20:06
看帖子很有启发,大神讨论真是面面俱到
作者: yzy0704    时间: 2016-12-7 07:25
LZ 真的是非常优秀!上班的时候空闲了就学习你的设计思路~
作者: yzy0704    时间: 2016-12-7 07:26
LZ真的非常优秀!上班闲下来了就学习你的帖子~
作者: xiaogou1233    时间: 2016-12-30 09:17
跟随大神的脚步,学习学习
作者: ati1tw    时间: 2017-1-17 09:47
回复 22# dyytx
算Sync
作者: hfut0288    时间: 2017-5-5 17:26
期待写的更多,有没有主页可以长期关注的?
作者: Apple_eert    时间: 2017-8-8 20:20
新手请教大神,我对您的代码做了仿真,读写使能一直为高,对读写时钟进行设置,只有在读写时钟相同时,不会产生空满标志位。其他如果二者时钟比大于2,则会读出数据发生错误;如果小于2,则会产生空位1_0或满为1_0,读出数据正确。正确的异步FIFO是,读写频率不同,写入读出数据宽度不同,同时不出现空满为1的情况,并且读出数据正确吗?您的示例代码中,写入读出数据宽度是均为32位,所以只有读写频率相同时,才不产生空满标志位1的情况?
作者: 杰克淡定    时间: 2017-8-9 08:57
回复 56# Apple_eert

你把读写使能一直拉高,那就是不管FIFO当前是何空满状态,一直读写FIFO,没有哪个FIFO可以这样操作的。写之前要先判断非满,读之前要先判断非空,这是FIFO正确操作的前提。
作者: Apple_eert    时间: 2017-8-9 09:52
回复 57# 杰克淡定


   谢谢大神指点,我把送的激励中的读写使能设为高,但用作写FIFO和读FIFO的使能分别和非满,非空做与运算,这样无论读写时钟差多少,仿真功能都正确。
作者: 马奎高    时间: 2017-11-9 14:45
楼主你好,看过你的帖子,请教个问题,这种异步fifo是基于先同步后比较的,对于另外一种先比较后同步的话您有涉猎吗,如果知道的话,为什么后一种更快,我一直不明白它的优点
作者: 杰克淡定    时间: 2017-11-9 19:17
回复 59# 马奎高

不好意思,没有明白你说的先同步后比较和先比较后同步是什么意思?
作者: ls1065271400    时间: 2017-12-20 19:15
本帖最后由 ls1065271400 于 2017-12-20 19:17 编辑

楼主大大,按照你的代码进行了仿真,这个问题是什么原因?望赐教 ( , 下载次数: 96 )
作者: ls1065271400    时间: 2017-12-20 19:20
楼主大大,按照你的代码仿真的,这个是什么原因啊?望赐教。 ( , 下载次数: 100 )
作者: sdlyyuxi    时间: 2017-12-29 18:57
回复 62# ls1065271400

看着好像正常啊,因为你的写时钟本身就频率高,所以到了后面,一旦写满,那就要等读操作来腾出空间,才能接着写,因此出现这种满信号周期出现的情况吧。
作者: jackstrive    时间: 2018-5-17 11:35
thanks
作者: zqq624723237    时间: 2018-5-24 11:05
楼主有技术博客可以分享一下吗?
作者: nini816    时间: 2018-5-24 15:50
Good Job!!!CMOS集成电路设计手册 第3版 数字电路篇
作者: zzzjjjqqq666    时间: 2018-9-18 23:15
回复 1# 杰克淡定


   感谢分享!!!
作者: yanzhang98    时间: 2018-9-19 07:46
# DLY会减低仿真速度
作者: ASIC_Peng    时间: 2019-5-14 09:40
楼主,你写的时候不判断fifo 满,最后指针指向满地址后,继续写会覆盖掉数据啊。
always@(posedge clk_wr )
begin
         if(wr_en && !fifo_full)
          fifo_mem[wr_addr[3:0]]<=wr_data;
         else ;
end

作者: xiaopangwa    时间: 2019-5-14 17:53
感谢分享
作者: gyong22    时间: 2019-5-29 14:00
good info.
作者: wuzhipingao    时间: 2019-8-24 23:29
楼主写的几个帖子受益匪浅,感谢楼主。不知道楼主有没有打算再开帖子,给我们普及下哈
作者: tvman2015    时间: 2020-2-29 17:34
very good post
作者: lllluo    时间: 2020-7-24 11:23
assign af_wlevel = wptr_wclk - rptr_bin ;
assign af_rlevel = wptr_bin - rptr_rclk ;
assign af_half_full = (af_rlevel >= 5'h7) ;
我看不明白这里用写指针减去读的地址,是判断写满吗?前面的代码也没有提到 af_wlevel啊
作者: gpchen    时间: 2020-7-30 00:15
不错啊,看看
作者: 乐行者独行    时间: 2021-10-17 19:42
学习了,谢谢!
作者: ttuan    时间: 2023-2-15 10:46
tkx for share
作者: zlguo    时间: 2025-9-2 14:29


   
我没逗你玩儿 发表于 2016-9-27 21:23
我觉得可以这样:
Gray    addr  
000      0


满逻辑怎么产生呢?




欢迎光临 EETOP 创芯网论坛 (原名:电子顶级开发网) (https://bbs.eetop.cn/) Powered by Discuz! X3.5