异步FIFO为什么要使用格雷码(笔记)

异步FIFO为什么要使用格雷码(笔记)

  • 首先要了解的是异步FIFO使用格雷码的唯一目的就是:
    “即使在亚稳态进行读写指针抽样也能进行正确的空满状态判断”。

那么典型的判断方法是怎样进行的呢?以满状态判断为例,先要对读指针(属于读时钟域)在写时钟域进行抽样,然后才能与写指针进行比较,如果写指针赶上了读指针(多绕一圈),说明已经写满,写操作必须暂停。

为什么2进制(binary编码)指针不适合做空满判断。

事实上2进制读指针在增减时,经常发生多位突变,比如6位地址111111会在下一时刻变成000000,在实际电路中,这个变化过程要持续很长一段时间,会由111111经历6个状态转移到达000000。比如111111-> 101111 -> 100111 ->100110 -> 100100 -> 000100-> 000000。由于写时钟与读时钟不同步,异步的写时钟很可能会在状态不稳定的中间某个状态抽样,这样就会得到错误的读指针,进而做出错误的状态判断,导致系统异常。而且由于多位同时突变,凭借概率论常识可知发生错误的可能性很大。

格雷码(Gray)

那么怎样才能避免这个问题的发生呢?显然,在中间状态抽样,这个是不可避免的,这是异步系统天生的缺陷我们的目标是:即使在中间状态抽样,也要不影响空满状态的判断
符合这个要求的编码方法是每次只能有1个比特发生改变。为什么这么说呢?因为当只有一个比特发生改变时,即使在中间状态抽样,其结果也不外乎两种:递增前原指针和递增后新指针。显然递增后新指针是最新情况的反映,如果抽样到这个指针,那么和我们的设计预期是一致的,如果抽样到递增前的原指针,会有什么结果呢?假设现在抽样读指针那么最坏的情况就是把“不满”判断成了“满”,使得本来被允许的写操作被禁止了,但是这并不会对逻辑产生影响,只是带来了写操作的延迟。同样的,如果现在抽样写指针那么最坏的情况就是把“不空”判断成了“空”,使得本来被允许的读操作被禁止了,但是这也不会对逻辑产生影响,只是带来了读操作的延迟。

显然每次只变化1比特的编码方案可以有效解决中间状态下空满状态的判断问题,格雷码就是这样的一种编码。
格雷码每次只有一位跳变,但也需要使用两级寄存才安全。如果产生了亚稳态,使用两级寄存器后数据很大概率会回到某一个稳定状态,防止亚稳态传播。

在异步的FIFO中,采用格雷码进行计数,相邻的数据仅仅只有1bit变化,这样在两个时钟域同步的时候仅仅可能只有1bit产生亚稳态,通过同步以后,亚稳态可以消除,最坏的情况是这1bit采错,但是即使是采错地址也只是相差1个,这对判断空满标志不会产生影响。

其他看法

  1. 回答一:
    格雷码是的确非常有用。它的特点就是相邻码只有一位发生翻转,比如00->01->11->10->00…
    相比于格雷码,十进制计数容易产生毛刺。
    编写状态机时,我也喜欢用这种相邻状态只发生一位翻转的编码。这样从一个状态切换到下一个状态,就只有一位寄存器发生变化。这么做可以带来降低功耗的好处,因为寄存器的翻转是消耗动态功耗的,而这种编码将翻转的次数降到最低,所有有利于降低功耗。

    但是这里降低功耗的前提是,一个状态到下一个状态的变化也要遵循格雷码连续的变化,否则,就不成立了。比如:如你所说,00<->01<->11<->10这样的变化,确实是有利于降低功耗;但是,状态的变化不一定就是这样变化,它有可能是00<->11,01<->10这样的变化,那么你说的有利于降低功耗就不成立。

  2. 回答二:
    低功耗设计的内容还是很博大精深的。如何合理的选择信号形式,以使得系统在发生状态变化时只有少数信号发生翻转?所依赖的公式为
    P = a ∗ C ∗ V 2 ∗ f P=a*C*V^2*f P=aCV2f
    其中a为信号翻转率,C为系统等效电容,V为电路驱动电压,f为时钟频率。使用格雷码降低功耗就是想使a小下来。当然如果电路中做不到严格的格雷码也没关系,只要是有利于减小a的,就有利于功耗的降低。
    异步FIFO的例子主要是为了多比特同步,这个例子中功耗是次要的因素。

  3. 回答三:
    格雷码有两个作用,一是消除多个比特同时变化带来的潜在竞争与冒险,二是降低功耗(翻转次数减少)。在状态机中通常为了简单起见表示现态与次态的状态参数并不使用格雷码而是二进制码,因为一个状态的变化可能是发散的而非单向的,就是说不同的条件对应不同的次态,这个时候用格雷码对状态进行编码达不到一次只变化一位的目的,除非在特殊情况下,即状态机的变化是单向的,总是由S0到S1,再到S2,等等,这个时候用格雷码就能达到原来的目的。

  4. 回答四:
    但是在实际应用中,往往是将二进制的转换成gray,同时其转换的外加逻辑必然会带来损耗,在多比特的情况,功耗是否会跟多呢?
    Gray码一般是用在不同时钟域的连续数值的传送,即数据必须是递增或递减的;这样,其主要是在asynchronou fifo中使用,感觉功耗并不会下降。


另一个博客

异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,将写地址同步到读时钟域再和读地址比较进行FIFO空状态判断(同步后的写地址一定是小于或者等于当前的写地址,所以此时判断FIFO为空不一定是真空,这样更保守),将读地址同步到写时钟域再和写地址比较进行FIFO满状态判断(同步后的读地址一定是小于或者等于当前的读地址,所以此时判断FIFO为满不一定是真空,这样更保守),这样可以保证FIFO的特性:FIFO空之后不能继续读取,FIFO满之后不能继续写入。

大多数情形下,异步FIFO两端的时钟不是同频的,或者读快写慢,或者读慢写快,这时候进行地址同步的时候,可能会有地址遗漏。
以读慢写快为例

  • 以读慢写快为例,进行满标志判断的时候需要将读地址同步到写时钟域,因为读慢写快,所以不会有读地址遗漏,同步后的读地址滞后当前读地址,所以可能满标志会提前产生。进行空标志判断的时候需要将写地址同步到读地址,因为读慢写快,所以当读时钟同步写地址的时候,必然会漏掉一部分写地址(写时钟快,写地址随写时钟翻转,直到满标志出现为止),那到底读时钟会同步到哪个写地址?不必在意是哪一个,我们关注的是漏掉的地址会不会对FIFO的空标志产生影响。比如写地址从0写到10,期间读时钟域只同步到了2,5,7这三个写地址,漏掉了其他地址。同步到7地址时,真实的写地址可能已经写到10地址,相当于“在读时钟域还没来得及觉察的情况下,写时钟域可能偷偷写了数据到FIFO去”,这样在比较读写地址的时候不会产生FIFO“空”读操作。漏掉的地址也没有对FIFO的逻辑操作产生影响。

我们可以对异步FIFO的地址采用binary编码,这样并不影响异步FIFO的功能,前提是读写地址同步时能够保持正确。这种情况在功能仿真时完全正确,问题只有到时序仿真时才会遇到。毛刺可以说是异步电路的杀手,一个毛刺被触发器采样后会被放大,然后传播,导致电路功能出错。

binary编码的地址总线在跳变时极易产生毛刺,因为binary编码是多位跳变,在实现电路时不可能做到所有的地址总线等长,address bus skew必然存在,而且写地址和读地址分属不同时钟域,读写时钟完全异步,这样地址总线在进行同步过程中出错不可避免,比如写地址在从0111到1000转换时4条地址线同时跳变,这样读时钟在进行写地址同步后得到的写地址可能是0000-1111的某个值,这个完全不能确定,所以用这个同步后的写地址进行FIFO空判断的时候难免出错。

这个时候gray码体现了价值,一次只有一位数据发生变化,这样在进行地址同步的时候,只有两种情况:

1.地址同步正确;
2.地址同步出错,但是只有1位出错;

第一种正确的情况不需要分析,我们关注第二种,假设写地址从000->001,读时钟域同步出错,写地址为000->000,也就是地址没有跳变,但是用这个错误的写地址去做空判断不会出错,最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。所以gray码保证的是同步后的读写地址即使在出错的情形下依然能够保证FIFO功能的正确性,当然同步后的读写地址出错总是存在的(因为时钟异步,采样点不确定)。这里需要注意gray码只是在相邻两次跳变之间才会出现只有1位数据不一致的情形,超过两个周期则不一定,所有地址总线bus skew一定不能超过一个周期,否则可能出现gray码多位数据跳变的情况,这个时候gray码就失去了作用,因为这时候同步后的地址已经不能保证只有1位跳变了。

另外需要将地址总线打两拍,这是为了避免亚稳态传播,理论上将打两拍不能消除亚稳态现象,因为时钟异步,亚稳态不可避免,但是可以极大降低亚稳态传播的概率,低频情况下甚至STA不需要分析这里的异步时序,因为寄存器都可以在一拍内将亚稳态消除,恢复到正常0/1态。而在高频情况下则不一定,尤其在28nm工艺以下,需要检查两级触发器的延迟,保证延迟低,这样可以提高Tr,提高系统MTBF。

参考

  • 异步FIFO:为什么要用格雷码 —— 程序员大本营
  • 异步FIFO—Verilog实现 ——CSDN
  • 今天华为面试题:异步FIFO读时钟是写时钟的100倍,或者写是读的100倍会出现什么问题?

1、写是读的100倍

  • 需要考虑FIFO的设计深度,考虑突发的写入任务不会导致FIFO溢出。
  • 写快读慢,主要考虑的是写满(full)的情况。在写操作在快时钟域下,读地址被写时钟同步一拍(从慢时钟域到快时钟域,使用两级寄存器减小亚稳态),如果存在亚稳态,那么写入时钟获取的是上一次的读地址,最多出现虚满的情况(实际上还差一个才满)。所以这方面不会出现问题。
  • 考虑读空(empty)的情况,地址从快时钟到慢时钟,在读时钟下更新地址时,可能已经写入好几个数据了,若此时判断为空,显然就是虚空。所以这方面也不会出现问题。

2、读是写的100倍

  • 读快写慢,所以很容易出现读空的情况。读空时数据的有效输出不是连续的,如果需要进行相关运算的话,就可能会产生影响(比如乘法器就不需要查看有效标志);如果后续只是数据传输,则不会导致数据丢失或出错等问题。
  • 当然,虽然读时钟很快,但是读使能却可以拉低读的频率(可以假设不读),这样也可能导致写满,但由于格雷码的使用,最多出现虚满或刚好满的情况,不会导致数据溢出,因此也没有问题。

你可能感兴趣的:(FPGA)