异步fifo 订阅
使用FIFO同步源自不同时钟域的数据是在数字IC设计中经常使用的方法,设计功能正确的FIFO会遇到很多问题,探讨了两种不同的异步FIFO的设计思路。两种思路都能够实现功能正确的FIFO。本文所研究的FIFO,从硬件的观点来看,就是一块数据内存。 展开全文
使用FIFO同步源自不同时钟域的数据是在数字IC设计中经常使用的方法,设计功能正确的FIFO会遇到很多问题,探讨了两种不同的异步FIFO的设计思路。两种思路都能够实现功能正确的FIFO。本文所研究的FIFO,从硬件的观点来看,就是一块数据内存。
信息
源    自
不同时钟域的数据
端口数
两个端口
外文名
Asynchronous FIFO
介    绍
是一块数据内存
中文名
异步FIFO
宏定义简介
使用FIFO同步源自不同时钟域的数据是在数字IC设计中经常使用的方法,设计功能正确的FIFO会遇到很多问题,探讨了两种不同的异步FIFO的设计思路。两种思路都能够实现功能正确的FIFO。本文所研究的FIFO,从硬件的观点来看,就是一块数据内存。它有两个端口,一个用来写数据,就是将数据存入FIFO;另一个用来读数据,也就是将数据从FIFO当中取出。与FIFO操作相关的有两个指针,写指针指向要写的内存部分,读指针指向要读的内存部分。FIFO控制器通过外部的读写信号控制这两个指针移动,并由此产生FIFO空信号或满信号。对于异步FIFO而言,数据是由某一个时钟域的控制信号写入FIFO,而由另一个时钟域的控制信号将数据读出FIFO。也就是说,读写指针的变化动作是由不同的时钟产生的。因此,对FIFO空或满的判断是跨时钟域的。如何根据异步的指针信号对FIFO的满状态或空状态进行正确的判断是本文研究的重点。此外,设计过程中的一些细节问题也将在文中涉及到。1 指针以及满空信号的产生为了更好地说明问题,先探讨一下同步FIFO指针移动以及满空信号的产生过程。对于同步FIFO,读写指针都指向一个内存的初始位置,每进行一次读写操作,相应的指针就递增一次,指向下一个内存位置。当指针移动到了内存的最后一个位置时,它又重新跳回初始位置。在FIFO非满或非空的情况下,这个过程将随着读写控制信号的变化一直进行下去。如果FIFO处于空的状态,下一个读动作将会导致向下溢出(underflow),一个无效的数据被读入;同样,对于一个满了的FIFO,进行写动作将会导致向上溢出(overflow),一个有用的数据被新写入的数据覆盖。这两种情况都属于误动作,因此需要设置满和空两个信号,对满信号置位表示FIFO处于满状态,对满信号复位表示FIFO非满,还有空间可以写入数据;对空信号置位表示FIFO处于空状态,对空信号复位表示FIFO非空,还有有效的数据可以读出。当读指针和写指针相等也就是指向同一个内存位置的时候,FIFO可能处于满或空两种状态。可以通过不同的方法判断或区分FIFO究竟是处于满状态还是空状态,也就是究竟是写指针从后赶上了读指针,还是读指针从后赶上了写指针。本文所应用的方法是设置一个额外的状态位,指针由它的地址位以及状态位组成。地址位随着相应的操作递增,指针由内存的最后位置返回到初始位置的时候状态位取反。因此,当读写指针的地址位和状态位全部吻合的时候,读写指针经历了相同次数的循环移动,也就是说,FIFO处于空状态(图1(a));如果读写指针的地址位相同而状态位相反,写指针比读指针多循环一次,标志FIFO处于满状态(图1(b))。 (2)二进制指针可以用于任意大小的FIFO;格雷码指针只能用于大小为2的幂的FIFO。2 二进制指针和格雷码指针的同步二进制指针是由一位状态位和若干位二进制编码的地址位组成的(例如由三位地址和一位状态位构成的指针的变化:0000→0001→0010→0011→0100→0101→0110→0111→1000→1001→1010→1011→1100→1101→1110→1111)。如果在不同的时钟域内直接同步二进制指针,有可能产生问题。例如,当读指针从0111向1000变化的时侯,指针所有的位都要变化,如果写时钟恰好在读指针的变化时刻采样,写者得到的读指针值有可能是从0000到1111中的任何一个。所以二进制指针不宜被直接同步,但可以通过一对握手信号同步二进制指针。例如,读指针被读者存入一个寄存器时,读者就发出一个就绪信号。当写者看到就绪信号时,读取读指针,发出一个收到数据的确认信号。当读者看到确认信号时,就收回就绪信号,然后用当前的读指针值刷新寄存器(图2)。在收到确认信号前,存放读指针的寄存器内容保持不变,确保了被读取的指针的正确性。为了能够在不同的时钟域内直接同步指针,可以对指针使用格雷码的编码方式,也就是指针每次移动只变化一位,这样就避免了由于指针多位同时变化而无法直接同步的问题。图3表示了一个三位地址的内存用于格雷码编码的FIFO。n位地址的内存需要n+1位的格雷码。使用最高位(也就是第n-1位)作为状态位,从第n—2到第0位作为地址的第n-2位到第0位;对状态位(也就是第n-1位)和第n-2位进行位异或运算产生地址的第n-2位(也就是地址的最高位)。通过对图3的观察可以得知,四位格雷码编码的指针和三位二进制加状态位编码的指针具有同样的功能:指针产生的地址循环遍历8个内存地址,每一次遍历后状态位取反。值得注意的一点,是当指针从0100变化到1100的时候,相应的地址由100变为000,状态位和一位地址同时发生了变化,所以不能同步由另一个时钟域格雷码指针产生的状态位和地址位,此时应该直接由另一个时钟域同步指针来完成,由同步后的指针产生相应的状态位和地址位。3 保守的满空判断对于异步FIFO设计,无论是采用握手还是直接同步的方法来获取对方时钟域的指针,对满空信号的判断总是“保守”的。(1)满空信号的复位(满信号复位表示FIFO非满,空信号复位表示FIFO非空)和实际FIFO的情况相比有一定的延迟。例如,空信号是由读者用读指针与同步或握手后得到的写指针进行比较产生的。由于同步或握手需要一定的时间,在这段时间,写者可能向FIFO写入新的数据,写指针发生了变化,此时FIFO已经非空,但此刻空信号仍然没有复位。对于写者而言,满信号的复位也会遇到相同的问题。不过,在通常情况下,FIFO只要确保不会向下溢出或向上溢出,复位的延迟就不会导致向下溢出或向上溢出,是可以接受的。(2)满空信号的置位(满信号置位表示FIFO满,空信号置位表示FIFO空)和FIFO的实际情况相比没有延迟。例如,同样考虑FIFO为空的情况,读者使用读指针和同步或握手后的写指针进行比较,由于FIFO为空,写动作不会发生,相应的写指针也保持不变,因此读者获得的就是当时的写指针值。这样就能马上对空信号置位。同样地,在FIFO满的情况下,由于读指针不发生变化,写者得到的是当前的读指针值,能够马上判断FIFO为满。从以上两点的讨论可以得出结论,FIFO满空判断是保守的,写者可能在FIFO还有一定空间时停止写数据,但不会在FIFO已经满了的情况下继续写数据;读者可能在FIFO还有一些有效的数据时停止读数据,但是不会在FIFO已经空的情况下继续读数据。保守的满空判断能够满足FIFO的功能要求。4 二进制指针和格雷码指针的比较二进制指针和格雷码指针两者各有优缺点:?(1)由于通过握手同步,指针可以有多位同时变化,二进制指针每次移动可以跳跃过任意的长度,这样给FIFO的某些功能的实现带来了方便(例如,硬件直接控制FIFO从缓存的数据流中丢弃一个出错的包);而格雷码指针一般只能做递增或递减的移动。(3) 与直接同步相比,通过握手同步需要多时钟周期,因此二进制指针的满空判断比格雷码指针的满空判断更“保守”;对于设计一个容量很大且由内存构成的FIFO来说,由于保守判断而损失的空间可以忽略;但对于一个容量较小的FIFO而言,这种“保守”可能是无法接受的。例如,一个由8个寄存器组成的FIFO,对于格雷码编码的指针,最坏情况下,FIFO被写者判断为满时,里面实际只存有五个数据(使用两级同步器,可能需要三个周期同步,而在这三个周期内都有读动作发生);而对与二进制编码的指针,FIFO被判为满时,FIFO里可能只有三个数据或更少(使用两级同步器,最好的情况下,需要五个周期握手,而在这五个周期里都有读动作发生)。设计者在两种方法的比较选择上应该考虑到以上的几点。本文介绍的异步FIFO的设计方法的两种不同尽路都能够实现功能正确的异步FIFO。对这两种方法设计的FIFO的优缺点所做的简要分析,对FIFO的设计具有指导意义。
收起全文
精华内容
下载资源
问答
  • 异步FIFO

    2020-08-04 21:25:06
    异步FIFO常用情况FIFO的空满标志FIFO指针格雷码格雷码下的空满信号重点最小深度FIFO的计算代码 最近两天晚上在看异步FIFO,之前也使用过异步FIFO,但是一直没有仔细搞懂原理。这次准备写篇博客记录一下。 常用情况 ...


    最近两天晚上在看异步FIFO,之前也使用过异步FIFO,但是一直没有仔细搞懂原理。这次准备写篇博客记录一下。

    常用情况

    以下两种情况,经常会使用到异步FIFO
    1.跨时钟域问题
    2.位宽不一致时(例如 输入8bit 输出16bit)

    FIFO的空满标志

    一个深度为(2^n)的FIFO,其地址位宽需要n+1位

    • MSB作为指针折回标志,当指针完成一轮读或写,MSB就翻转一次。所以当wptr和rptr相遇时,如果MSB=1,为满;MSB=0,为空。

    FIFO指针格雷码

    由于二进制在跨时钟域上可能有所有位同时翻转的可能,而格雷码每次只有一位变化,可以大大降低亚稳态出现的可能。

    格雷码下的空满信号

    因为地址是格雷码,所以不能单纯的比较n位是否相同来判断空满,而要考虑格雷码的对称性。

    • 空:仍为wptr和rptr完全相等。
    • 满:wptr和rptr的最高位和次高位都不同,而其余位都相同。

    重点

    因为在地址的同步逻辑中常使用打两拍来消除亚稳态,所以同步时常有延迟的现象,那么这会导致什么问题吗?
    写快读慢的情况下

    • fifo_empty的产生:这时候从读端看进去,要将wptr同步到rptr,所以wptr可能慢于实际wptr,不满足读空的条件时没问题;当条件满足时,因为延迟的关系,实际可能wptr>=rptr,fifo实际有写入数据,但这写不影响使用,这种情况叫虚空
    • fifo_full的产生:这时候从写端看进去,要将rptr同步到wptr,所以rptr可能慢于实际wptr,满足写满的条件时,没有问题;当条件不满足时,由于延迟的关系,实际可能rptr>=wptr-fifo_depth,这时候信号可能显示为full,实际已经多读了,这种情况叫虚满

    写慢读快的情况分析类似

    最小深度FIFO的计算

    • 如果是同步FIFO问题:

    例如
    在这里插入图片描述
    考虑背靠背情况,最坏在160cycle中写入了160个数据,而这16个cycle只能读出16*8=128个数据,FIFO中还有32个数据,所以FIFO的最小深度为32.

    • 如果是异步FIFO,将上述情况改为
      在这里插入图片描述
      那么背靠背情况,80个cycle里写入了80个数据,而读侧在这些时间内只能读出
      8*(80cylce/10cycle)*(100MHz/200MHz)=32
      所以FIFO里至少还有48个数据
      深度需要2^6 = 64

    代码

    后续补充

    展开全文
  • FIFO根据输入输出时钟是否一致,分为同步FIFO与...本文以异步FIFO与同步FIFO的异同入手,在比较过程中逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,最终实现异步FIFO,并进行了仿真、调试、以及验证

    FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO。同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的不同步可能会导致亚稳态,导致FIFO工作异常,设计较为复杂;在之前的记录中,我们对同步FIFO的设计进行了分析:

    Verilog实现FIFO专题(3-同步FIFO设计)

    此处我们不再对同步FIFO进行介绍而直接以异步FIFO与同步FIFO的异同为线索,逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,并进一步实现异步FIFO。

    目录

    一、异步FIFO与同步FIFO工作流程比较

    1、同步FIFO

    2、异步FIFO

    二、异步FIFO的空满检测

    1、同步FIFO的空满检测

    2、异步FIFO的空满检测

    计数检测空满:

    指针比较检测空满:

    扩展指针比较检测空满:

    格雷码指针比较检测空满:

    三、异步FIFO的同步处理

    1、同步方式

    2、延迟对FIFO设计的影响

    结论:

    FIFO满检测:

    FIFO空检测:

    四、异步FIFO设计

    1、端口设计

    外部端口

    内部信号

    2、功能描述

    3、实现代码

    4、仿真验证

    五、参考文献


    一、异步FIFO与同步FIFO工作流程比较

    1、同步FIFO

    同步FIFO的读写控制信号以及数据均处于同一时钟域,即:FIFO在同一时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

    2、异步FIFO

    异步FIFO的工作内容与同步FIFO类似:FIFO在时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

    但是异步FIFO的控制并不像同步FIFO那么简单,因为异步FIFO工作在不同的时钟域,这就带来了一些问题:

    (1)如何进行空满检测?还能像同步FIFO中通过计数值来判断吗?

    (2)需要同步电路

    二、异步FIFO的空满检测

    1、同步FIFO的空满检测

    同步FIFO的空满检测可以通过计数很简单的实现:

    读写逻辑是同一个时钟,因此可以在每次时钟来临时进行判断,如果不执行读写操作/同时读写,则计数值不变;只执行读操作,计数值减1;只执行写操作,计数值加1;

    如果计数值为0,则说明FIFO空,只能写不能读(直到写入一次数据,计数值加1,FIFO不再为空才能执行读操作);

    如果计数值为FIFO深度,则说明FIFO满,只能读不能写(直到读出一次数据,计数值减1,FIFO不再为满才能执行写操作);

    2、异步FIFO的空满检测

    计数检测空满:

    异步FIFO不能采用同步FIFO这种计数方式来实现空满检测,因为用两个时钟去控制同一个计数器的加剪很明显是不可取的。

    如果不明白为什么不能用两个时钟控制同一个计数器,可以查阅:Verilog中always@()语句双边沿触发(语法与综合的差异)

    指针比较检测空满:

    读写指针指向读写操作面向的FIFO地址空间,因此空满检测的另一个思路是比较读写指针。每次写操作执行,写指针加1;而每次读操作执行,读指针加1,因此:

    FIFO空发生在:读指针追上写指针时;

    FIFO满发生在:写指针追上读指针时;

    但是这种处理仍存在一个问题,就是读写指针相等时,难以区分FIFO是空还是满。

    扩展指针比较检测空满:

    如上分析,直接比较读写指针时存在一个特例:读写指针相等时,难以区分FIFO是空还是满。

    因此,有人提出,将指针进行高位扩展。即指针加宽一位,当写指针超出FIFO深度时,这额外的一位就会改变。FIFO的深度决定了指针扩展前(即除了最高位的其余位)的宽度,而这扩展的一位与FIFO深度无关,是为了标志指针多转了一圈,因此:

    当读写指针完全相同时,FIFO空;

    当读写指针高位不同,其余位完全相同时,FIFO满;

    经过指针扩展,可以明确的进行空满检测。但是这种处理仍然不够,因为异步FIFO读写时钟相互独立,分属不同时钟域,相邻二进制地址位变化时,不止一个地址位都要变化,这样指针在进行同步过程中很容易出错,比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大。

    格雷码指针比较检测空满:

    如上分析,直接比较扩展读写指针时可能因多位改变导致错误。因此,进一步采用gray码形式的指针,利用格雷码每次只变化一位的特性,降低同步发生时错误的概率。如图,为一个深度为8FIFO的格雷码指针(绿色框中):

    0-7为真实的FIFO地址,而8-15是指针多转一圈以后的地址(8-0,9-1...)。应注意,此时指针按照格雷码方式进行编码,不能再用二级制指针的比较方式来判断空满。比如:位置6(0101)和位置9(1101),除最高位外其余位均相等。但是很明显,位置9实际对应位置1处的存储空间。

    因此,格雷码指针下的空满检测条件为:

    当最高位和次高位均相同,其余位相同:FIFO空

    当最高位和次高位均相反,其余位相同:FIFO满

    因此,最终的空满检测方式为:将二进制指针转换为格雷码,用于另一时钟域接收,随后按照检测条件进行检测。

    二进制指针转换为格雷码的详情见:Verilog实现二进制码与格雷码转换  此处不再展开。

    三、异步FIFO的同步处理

    1、同步方式

    判断FIFO空满状态时,需要在读FIFO时获取写时钟域的写指针,与读指针比较来判断FIFO是否为空;需要在写FIFO时获取读时钟域的读指针,与写指针比较来判断FIFO是否为满;

    也就是说,判断空满状态时牵扯到跨时钟域问题,需要进行同步;

    采用两级寄存器打两拍的方式进行同步,具体实现见:亚稳态专题

    2、延迟对FIFO设计的影响

    异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,将读写指针同步后再进行比较,判断FIFO空满状态。但是因为在同步指针时需要时间(如延迟两拍同步),而在这个同步的时间内有可能还会写入/读出新的数据,因此同步后的指针一定是小于或者等于当前实际的读/写指针,那么此时判断FIFO满空状态时是否会出错?是否会导致错误?

    结论:

    先说结论:异步逻辑进行同步时,不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。

    FIFO满检测:

    FIFO满检测发生在写时钟域,将读指针同步到写时钟域后再和写指针比较,进行FIFO满状态判断。因为同步时间的存在,同步后的读指针一定是小于或者等于当前真正的读指针(同步时间内可能出现了读操作,导致读指针增加),所以此时判断FIFO为满不一定是真满,这样更保守(即:写指针=同步读指针<=真实读指针),这样可以保证FIFO的特性:FIFO空之后不能继续读取。

    FIFO空检测:

    FIFO空检测发生在读时钟域,将写指针同步到读时钟域后再和读指针比较,进行FIFO空状态判断。因为同步时间的存在,同步后的写指针一定是小于或者等于当前真正的写指针(同步时间内可能出现了写操作,导致写指针增加),所以此时判断FIFO为空不一定是真空,这样更保守(即:读指针=同步写指针<=真实写指针),这样可以保证FIFO的特性:FIFO满之后不能继续写入。

    四、异步FIFO设计

    1、端口设计

    外部端口

    1、读时钟信号clk_r,作为异步FIFO的读驱动信号

    2、写时钟信号clk_w,作为异步FIFO的写驱动信号

    3、异步复位信号rst_n

    // 写FIFO相关

    4、数据输入信号din[DW-1:0],作为FIFO数据写入端,DW数据为位宽

    5、写使能we

    // 读FIFO相关

    6、数据输出信号dout[DW-1:0],作为FIFO数据输出端

    7、读使能re

    // 标志相关

    8、FIFO满标志full,FIFO满时不能再写入数据

    9、FIFO空标志empty,FIFO空时不能再读出数据

    内部信号

    1、读指针wp,作为读地址(FIFO读写只能顺序进行,不能外部设置,因此为内部信号)

    2、格雷码读指针wp_g,供写时钟域同步后判断FIFO满使用

    3、写指针rp,作为写地址

    4、格雷码写指针wp_g,供读时钟域同步后判断FIFO空使用

    5、[DW-1:0]ram[0:Depth-1],数据存储空间,Depth为FIFO深度

    2、功能描述

    读逻辑:

    clk_r来临,re有效,并且FIFO非空(empty=0)时,将读指针rp对应地址处的数据读出至dout;

    // 读操作
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            dout <= {DW{1'bz}};
        else if(!empty & re)
            dout <= ram[rp[AW-1:0]];
        else
            dout <= dout;
    end
    

    读指针逻辑

    clk_r来临,re有效,并且FIFO非空(empty=0)时,进行一次读操作,rp加1(即顺序读出),并进行格雷码转换,生成对应rp_g;

    // 读指针
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            rp <= {AW{1'b0}};
        else if(!empty & re)
            rp <= rp+1'b1;
        else
            rp <= rp;
    end
    

    写逻辑:

    clk_w来临,we有效,并且FIFO不满(full=0)时,将din写入写指针wp对应地址处;

    //写操作
    always@(posedge clk_w)
    begin
        if(!full & we)
            ram[wp[AW-1:0]] <= din;
        else
            ram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
    end
    

    写指针逻辑

    clk_w来临,we有效,并且FIFO不满(full=0)时,进行一次写操作,wp加1(即顺序存储),并进行格雷码转换,生成对应wp_g;

    //写指针
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            wp <= {AW{1'b0}};
        else if(!full & we)
            wp <= wp+1'b1;
        else
            wp <= wp;
    end
    

    格雷码指针生成逻辑:

    利用二进制与格雷码的转换关系,将二进制指针转换为格雷码指针,用于另一个时钟域的同步接收;

    // 二进制指针转换为格雷指针
    assign wp_g = (wp>>1) ^ wp;
    assign rp_g = (rp>>1) ^ rp;
    

    格雷码指针同步逻辑:

    读时钟域,同步写地址,用于空逻辑判断;写时钟域,同步读地址,用于满逻辑判断;

    // 读时钟域,写地址同步
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wp_m <= {AW{1'b0}};
                wp_s <= {AW{1'b0}};       
            end
        else
            begin
                wp_m <= wp_g;
                wp_s <= wp_m;    
            end       
    end
    // 写时钟域,读地址同步
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rp_m <= {AW{1'b0}};
                rp_s <= {AW{1'b0}};       
            end
        else
            begin
                rp_m <= rp_g;
                rp_s <= rp_m;    
            end       
    end
    

    空满检测逻辑:

    根据格雷码指针判断逻辑,进行空满检测,应注意:

    读时钟域,同步写地址。读格雷码指针同步后写格雷码指针比较,用于空逻辑判断;

    写时钟域,同步读地址,写格雷码指针同步后读格雷码指针比较,用于满逻辑判断;

    // 空满检测,使用同步后的格雷指针?
    assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
    assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
    

    3、实现代码

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: guoliang CLL
    // 
    // Create Date: 2020/03/24 20:51:06
    // Design Name: 
    // Module Name: afifo
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module afifo
    #(parameter DW = 8,AW = 4)//默认数据宽度8,FIFO深度16
    (
        input clk_r,
        input clk_w,
        input rst_n,
        input we,
        input re,
        input [DW-1:0]din,
        output reg [DW-1:0]dout,
        output empty,
        output full
        );
    // internal signal
    parameter Depth = 1 << AW;//depth of FIFO 
    reg [DW-1:0]ram[0:Depth-1];
    reg [AW:0]wp;  //point
    reg [AW:0]rp;
    wire [AW:0]wp_g;//Gray point
    wire [AW:0]rp_g;
    reg [AW:0]wp_m;//mid_point for syn
    reg [AW:0]rp_m;
    reg [AW:0]wp_s;//point after syn
    reg [AW:0]rp_s;
    // FIFO declaration
    // 二进制指针转换为格雷指针
    assign wp_g = (wp>>1) ^ wp;
    assign rp_g = (rp>>1) ^ rp;
    // 空满检测,使用同步后的格雷指针?
    assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
    assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
    // 读指针
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            rp <= {AW{1'b0}};
        else if(!empty & re)
            rp <= rp+1'b1;
        else
            rp <= rp;
    end
    //写指针
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            wp <= {AW{1'b0}};
        else if(!full & we)
            wp <= wp+1'b1;
        else
            wp <= wp;
    end
    // 读操作
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            dout <= {DW{1'bz}};
        else if(!empty & re)
            dout <= ram[rp[AW-1:0]];
        else
            dout <= dout;
    end
    //写操作
    always@(posedge clk_w)
    begin
        if(!full & we)
            ram[wp[AW-1:0]] <= din;
        else
            ram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
    end
    // 读时钟域,写地址同步
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wp_m <= {AW{1'b0}};
                wp_s <= {AW{1'b0}};       
            end
        else
            begin
                wp_m <= wp_g;
                wp_s <= wp_m;    
            end       
    end
    // 写时钟域,读地址同步
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rp_m <= {AW{1'b0}};
                rp_s <= {AW{1'b0}};       
            end
        else
            begin
                rp_m <= rp_g;
                rp_s <= rp_m;    
            end       
    end
    endmodule
    

    4、仿真验证

    测试文件如下:

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: CLL guoliang
    // 
    // Create Date: 2020/03/24 21:22:45
    // Design Name: 
    // Module Name: afifo_tsb
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module afifo_tsb(
    
        );
    // port declaration
    reg clk_r;
    reg clk_w;
    reg rst_n;
    reg we;
    reg re;
    reg [7:0]din;
    wire  [7:0]dout;
    wire empty;
    wire full;
    //clk
    initial
    begin
        clk_r = 1'b0;
        forever #25 clk_r = ~clk_r;
    end
    initial
    begin
        clk_w = 1'b0;
        forever #10 clk_w = ~clk_w;
    end
    // 
    initial
    begin
        rst_n = 1'b1;
        din = 1'b0;
        re = 1'b0;
        we = 1'b0;
        #50 rst_n = 1'b0;
        #50 rst_n = 1'b1;
        // only write
        we = 1'b1;
        repeat(20) #20 din = din+1'b1;
        // only read 
        we = 1'b0;
        re = 1'b1;
        repeat(20) #50; 
        // read and write
    //    we = 1'b1;
    //    re = 1'b1;
    //    din = 1'b0;
    //    repeat(20) #20 din = din+1'b1;     
    end
    // inst
    afifo inst2(
        .clk_r(clk_r),
        .clk_w(clk_w),
        .rst_n(rst_n),
        .we(we),
        .re(re),
        .din(din),
        .dout(dout),
        .empty(empty),
        .full(full)
    );
    endmodule
    
    

    仿真结果如下:

    篇幅有限,仿真结果不再展开分析。提醒自己,应注意仿真测试是很必要的,通过功能仿真能暴露出设计上的不足、缺陷、以及实现过程中因粗心等导致的其余问题;

    因此,如何设计测试文件也具有重要意义。测试文件容易编写,但是如何使得测试文件能全面的对设计进行检测,高效准确的对设计进行测试,无疑是一门学问;

    我只简单记录一下,我调试时关注的部分

    1、写逻辑

    数据能否在写时钟驱动下,顺序写入FIFO中对应地址;FIFO满时,是否停止写入;

    2、读逻辑

    能否在读时钟驱动下,顺序读出FIFO中对应数据;FIFO空时,是否停止读出;

    3、满判断

    设计能否在写时钟驱动下,同步读指针,并且在适当位置产生满标志;

    3、空判断

    设计能否在读时钟驱动下,同步写指针,并且在适当位置产生空标志;

    RTL电路如下:

    五、参考文献

    Verilog实现FIFO专题(3-同步FIFO设计)

    异步FIFO的设计

    Verilog中always@()语句双边沿触发(语法与综合的差异)

    Verilog实现二进制码与格雷码转换

    亚稳态专题



     

    展开全文
  • 异步fifo

    2020-06-27 11:25:34
    异步fifo 具体要求 异步fifo:输入输出速率不一样 用100mhz写入0-1023,并用10mhz读出 设计架构 Fifo是一种先进先出的缓存区只能顺序写入数据,顺序 读出数据,其数据地址由内部读写指针自动加 1 完成,不能像...

    项目名称

    异步fifo

    具体要求

    异步fifo:输入输出速率不一样

    用100mhz写入0-1023,并用10mhz读出

    设计架构

    Fifo是一种先进先出的缓存区只能顺序写入数据,顺序 读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可 以由地址线决定读取或写入某个指定的地址,读请求打开时,数据会自动被读出。

    需要注意锁相环为高电平复位.

    rdfull:读满信号,fifo满未读出时为真

    rdempty:读空信号,fifo全读出时为真

    wrfll:写满信号,fifo写满是为真

    wrempty:写空信号,fifo没写任何数据时为真 

    fifo与ram和rom不同,数据读出之后fifo中就不存在数据,但是ram和rom中的数据被读出之后数据依然存在

    代码设计

    verilog代码设计

    创建pll,生成100mhz和10mhz的时钟

     

    创建fifo ipcore 

    顶层模块设计

    module fifo_top(
    	input				clk,
    	input				rst_n,
    	output	[9:0]	q
    );
    //产生100m写时钟和10m的读时钟
    wire clk_100m;
    wire clk_10m;
    wire locked;
    my_pll  my_pll(
    	.areset(!rst_n),
    	.inclk0(clk),
    	.c0(clk_100m),
    	.c1(clk_10m),
    	.locked(locked)
    );
    //写fifo控制模块
    wire wrfull;
    wire wrempty;
    wire wrrerq;
    wire [9:0] wrdata;
    wire rdfull;
    wire rdempty;
    wire rdreq;
    wr_fifo  wr_fifo(
    	.wrclk(clk_100m),
    	.rst_n(locked),
    	.wrfull(wrfull),
    	.wrempty(wrempty),
    
    	.wrrerq(wrrerq),
    	.wrdata(wrdata)
    );
    //fifo模块
    my_fifo  my_fifo(
    	.data(wrdata),
    	.rdclk(clk_10m),
    	.rdreq(rdreq),
    	.wrclk(clk_100m),
    	.wrreq(wrrerq),
    	.q(q),
    	.rdempty(rdempty),
    	.rdfull(rdfull),
    	.wrempty(wrempty),
    	.wrfull(wrfull)
    );
    //读fifo控制模块
    rd_fifo rd_fifo(
    	.rdclk(clk_10m),
    	.rst_n(locked),
    	
    	.rdfull(rdfull),
    	.rdempty(rdempty),
    	.rdreq(rdreq)
    );
    endmodule

     写fifo控制模块

    module wr_fifo(
    	input					wrclk,
    	input					rst_n,
    	input					wrfull,
    	input					wrempty,
    	
    	output	reg		wrrerq,
    	output reg[9:0]	wrdata
    	
    );
    
    reg state;
    always@(posedge wrclk or negedge rst_n)
    	if(!rst_n)
    		begin
    			state<=0;
    			wrrerq<=0;
    			wrdata<=10'd0;
    		end
    	else begin
    		case(state)
    			0:	begin
    					if(wrempty)   //fifo中没有写任何数据
    						begin
    							wrrerq<=1;
    							state<=1;
    						end
    					else begin
    						wrrerq<=0;
    						state<=0;
    						wrdata<=10'd0;
    					end
    				end
    			1:	begin
    					if(wrfull)
    						begin
    							wrrerq<=0;
    							wrdata<=10'd0;
    							state<=0;//跳回原来状态,等待下一次写入
    						end
    					else begin
    						wrrerq<=1;
    						wrdata<=wrdata+1'b1;
    					end
    				end
    			default:;
    		endcase
    	end
    endmodule

    读fifo控制模块

    module rd_fifo(
    	input					rdclk,
    	input					rst_n,
    	
    	input					rdfull,
    	input					rdempty,
    	output	reg		rdreq
    );
    reg state;
    always@(posedge rdclk or negedge rst_n)
    	if(!rst_n)
    		begin
    			state<=0;
    			rdreq<=0;
    		end
    	else begin
    		case(state)
    			0:	begin
    					if(rdfull)
    						begin
    							state<=1;
    							rdreq<=1;
    						end
    					else begin
    						state<=0;
    						rdreq<=0;
    					end
    				end
    			1:	begin
    					if(rdempty)
    						begin
    							state<=0;
    							rdreq<=0;
    						end
    					else begin
    						state<=1;
    						rdreq<=1;
    					end
    				end
    			default:;
    		endcase
    	end
    endmodule

    仿真代码

    `timescale 1ns/1ns
    module fifo_top_tb;
    	reg			clk;
    	reg			rst_n;
    	wire 	[9:0]	q;
    
    fifo_top fifo_top(
    	.clk(clk),
    	.rst_n(rst_n),
    	.	q(q)
    );
    
    initial clk=0;
    always #10 clk=~clk;
    
    initial begin
    	rst_n=0;
    	#200;
    	rst_n=1;
    end
    
    endmodule

    仿真结果

    出现写空信号时,打开写请求数据在下一个时钟上升沿开始写入,当写入数据之后,fifo中写空信号为低电平

     fifo中写满之后,发出写满信号,写请求关闭

    fifi中写满之后发出读满信号,读请求打开开始读数据

    fifo数据读完之后,发出读空信号,读请求关闭。

     

     

     

    展开全文
  • 异步fifo_异步FIFO

    2020-11-21 15:35:49
    本文使用 Zhihu On VSCode 创作并发布概述异步FIFO用来在两个异步时钟域间传输数据。如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将...

    c1f00869e8eb848939aa3e38e134f2d2.png
    本文使用 Zhihu On VSCode 创作并发布

    概述

    异步FIFO用来在两个异步时钟域间传输数据。

    616bf61e3324cdb4a964c6e0bcc96a0f.png

    如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将数据从FIFO中读出。“fifo_full” 和 “fifo_empty” 信号负责对上溢(overflow)和下溢(underflow)的控制,如果在“fifo_full”为高时,不应该再写入数据,否则会将之前写入的数据覆盖掉。当“fifo_empty” 信号为高时,不应该读取数据,否则会读出无效的数据。FIFO数据“先进先出”,先写入的数据先读取。

    结构框图

    687d69356200031631853b62086d9875.png

    异步FIFO结构如上图所示

    1. 第1部分是双口RAM,用于数据的存储。
    2. 第2部分是数据写入控制器
    3. 第3部分是数据读取控制器
    4. 读指针同步器
      使用写时钟的两级触发器采集读指针,输出到数据写入控制器。
    5. 写指针同步器
      使用读时钟的两级触发器采集写指针,输出到数据读取控制器。

    数据读写控制器

    读写指针

    • 在同步FIFO中,通常使用二进制计数器实现读写指针。
      例如深度为16的同步FIFO,将读写指针位宽设置为5,低四位用作RAM的地址,最高位用于判断FIFO满。

    2132122cd2573a8648c7d9fa37f66c7d.png

    当读写指针相等时,表示写入的数据都被读取出来,此时FIFO为空。

    c38beee49f34444feeeb54d081025ff0.png

    当读写指针第四位相等,而最高位相反时,表示写指针比读指针多循环了一次,此时FIFO为满。

    对于异步FIFO,需要将写指针从写时钟域同步到读时钟域,产生空信号,需要将读指针从读时钟域同步到写时钟域,产生满信号。上述结构框图中4,5部分使用两级同步器来减小跨时钟域产生亚稳态的概率。

    由于需要同步多位宽的指针信号,如果指针继续使用二进制计数器,当指针从 11111 跳变到 00000 时,每以位数据都需要同步,产生亚稳态的概率会很高。5bit数据每一位都有可能采集出错,可能会将五位数据任何可能的组合采样并同步到新的时钟域中。

    可以使用格雷码代替二进制数进行跨时钟域的传输。
    格雷码的特点:

    • 由于具有相邻的两个数据只有一位数据不同的特性,最高位和最低位也只有一位数据不同,适合用于跨时钟域的传输,降低错误发生的概率。
    • 只有使用n位格雷码表示 2^n 个数据,格雷码才具有上述特性。

    格雷码和二进制数的转换:
    二进制数转格雷码:

    a29f7e989c31e569822d6d84640da3e6.png
    wire [4:0] gray;
    wire [4:0] bin;
    assign gray = {bin[4], bin[4:1] ^ bin[3:0]}

    格雷码转二进制数:

    a9d06856e0f0ab7df5a72ef934961293.png
    wire [4:0] gray;
    reg [4:0] bin;
    integer i;
        always @ (gray)
        begin    
            bin[4]=Gry[4];    
            for(i=3;i>=0;i=i-1)        
                bin[i]=bin[i+1]^gray[i];
        end

    我们使用二进制数器作为RAM地址信号,将其转换为格雷码进行跨时钟域传输。

    空满信号产生

    a0b299ea3c38c8d427c5648a8acda2a5.png

    如上图所示,使用4位格雷码作为深度为8的FIFO的读写指针。
    将格雷码转换成四位二进制数,使用二进制数低三位作为访问RAM的地址。

    • 与同步FIFO类似,当读写指针相等时,得出FIFO为空

    2fbe10ebc57a9a54da46f2a8ebafb561.png
    • 当写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满。

    同步器

    使用移位寄存器实现同步器。

    双口RAM设计与仿真

    设计一个深度位宽可调的双口RAM,默认参数深度为16,位宽为8。
    设计和测试文件见文章末尾。

    仿真波形:

    • 连续写入16个数据:

    c61a26ae1f28892e30607528813f0113.png
    • 连续读取16个数据:

    cc466868748a6ad5921701a3c3c15075.png
    • 同时写入、读取16个数据:

    56c40f2fb97939c6b649361a7a84354e.png

    异步FIFO的设计与仿真

    • 连续写入16个数据:

    f0874f6589e93dccb1f94fd3d3cf84ef.png
    • 连续读取16个数据:

    e1a349ea2e899e093264a1b51bf65a92.png

    工程源码

    链接:https://pan.baidu.com/s/1d7HPLnj1pjXMPgWE5epdvQ
    提取码:pxgc

    展开全文
  • 异步fifo_异步FIFO设计

    2020-12-18 22:46:20
    由于知乎编辑器对markdown支持性问题,本文为直接上传markdown文档,格式混乱且图像不全,格式完整版请移步个人博客异步FIFO设计​qiankun214.github.io1.设计目标设计一个参数可配置的异步FIFO,要求:FIFO深度从4...
  • 异步FIFO本文是对Clifford E. Cummings文章的粗浅翻译,如有错误,欢迎指出。1.0 摘要异步FIFO是一种FIFO设计,数据从一个时钟域进入到FIFO,在另一个时钟域读取数据,并且两个时钟域是异步。异步FIFO的用于将数据从...
  • 大家都知道,在处理异步路径时,异步FIFO的使用,是最常见的处理方式。但是,针对于异步FIFO的约束问题,还是非常值得深究的。是否异步FIFO就是完全可以异步处理呢?接下来就跟大家一起分析一下。02 分类我们在实现...
  • 异步FIFO设计

    2020-10-23 02:38:39
    异步FIFO设计_电子技术交流网本文介绍如何应用美国QUICKLOGIC公司的QUICKRAM器件设计高速、高可靠异步FIFO(Asynchronous FIFO)。
  • 本系列分为以下部分:1、FIFO深度计算2、同步fifo设计3、fifo与格雷码以及异步fifo设计计划分三次更新完毕,本次为本系列终结篇!本次增加异步FIFO设计,异常有趣!格雷码的相关知识关于同步fifo的设计疑惑了半天,...
  • 异步FIFO结构

    2015-08-08 21:46:34
    异步FIFO
  • 最近一位童鞋跟我聊到一个异步fifo的问题,还是很有水准的。我贴在这里给大家看一下:异步fifo需要将读写的pointer作比较产生满和空信号,但是假如fifo的两个时钟域的clk相差特别大,pointer在做跨时钟域转换的时候...
  • 本文使用 Zhihu On VSCode 创作并发布概述异步FIFO用来在两个异步时钟域间传输数据。如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将...
  • 但是在大规模的数字电路设计中,多时钟域往往是不可避免的,这样就会产生了跨时钟域传输等问题,为了解决跨时钟域问题,我们常常采取的方法是握手协议和异步FIFO做数据缓冲区,异步FIFO既可以使相异时钟域数据传输的...
  • 本文使用 Zhihu On VSCode 创作并发布跨时钟域同步(异步FIFO)之前学习了跨时钟域下的单bit信号同步的方法,这些单bit信号多是作为控制信号或者标志信号来使用,再实际的项目中,处理多bit数据也是十分常见的,即...
  • 前言 | 鱼叔在面试的时候,经常会被问到异步FIFO的问题,之前准备的时候一知半解,现在有时间了于是便想好好整理这个设计,希望这篇文章能给予你启发。1. 相关资料Kevin Zhang:异步FIFO设计(1)​zhuanlan.zhihu....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,748
精华内容 699
热门标签
关键字:

异步fifo