异步FIFO设计的一些注意事项

时间:2021-05-07 23:32:46

异步FIFO的结构基本上是是按照下面的思路来设计:

1. 读写的指针分别用gray code sync到另外一个clock domain,sync的时候至少打2拍。

2. 读写指针最高位各加一个bit用来判断空和慢,对于FIFO来说,永远是读指针追写指针,当指针相同的时候,用加的这个最高bit来

    判断是谁追上谁,继而判断空和满。

 

除了上面的设计要点,还有以下几点需要注意:

1. 当两个时候频率差别很大时

  • 快时钟的读/写指针sync到慢时钟时,gray code就不是按照1次变化1个bit的方式进行了,而是有可能一次变化好几个bit,这样gray code的好处就体现不出来了
  • 对于这种情况,我们先分析一下对于空满标志的影响,先假设写时钟慢,读时钟快的情况,如果在sync的时候不会出现亚稳态,那么sync到写时钟的读指针永远是1个写时钟period之前的值,也就是说永远是较早的值,这样对于满的判断就会比较保守,也就永远不会出错
  • 接着上面的分析,如果在第一级sync register(cycle 1)中出现了亚稳态,表示在cycle 1的时刻,读时钟也在变化,这个变化肯定导致至少读了一个数据出去了,cycle 1时刻,写与不写是基于上一个时钟采样的值判断的,也就是说在这个读数据操作发生之前的情况,所以在cycle 2的时候,这个错误的亚稳态sync到写时钟域中,如果这个错误的值导致full产生,这样不会导致错误发生,但会导致效率降低,如果这个错误的值没有导致full的产生,写动作继续,也不会发生错误,因为这之前读动作至少发生一次,所以写不会覆盖数据,因此,不管怎样,都不会出错
  • 那如果下一拍还是亚稳态呢?没关系,还是按照之前的分析,读操作又发生了至少一次了,所以下一次再写也不会覆盖数据,因此不管发生几次亚稳态,由于发生亚稳态的时刻必定是在读指针变化的时候,这个时刻肯定发生读了,所以就会腾出空间来写,而full的判断是在之后1个cycle发生的,所以才不会导致数据被覆盖,也就是说,在full等于1的时候,sync过来用于判断的读时钟在第一级sync register的时候必定是稳定的,不可能产生亚稳态。
  • 当sync register基数大于2时,也不会产生错误,但是会导致空满判断延迟更多,效率降低

2. 怎么在写端判断有多少个空间可写,以及在读端判断有多少个数据可读呢

  • 用sync过来的写指针判断肯定不行,设想如果发生亚稳态,那么sync过来的数据对于空满判断结果不会导致数据出错,但是用来计算具体个数的话,肯定会出现偏大或者偏小的情况,如果你用这个数据来做一些trigger的话,肯定会导致错误情况发生,即使错误概率很低。
  • 有人可能会说我对sync过来的指针采集连续几拍进行判断,如果递增就OK,如果出现先递增,后变小就是假的,这个数据我就不用,这样也不行,也有概率会出错,比如两次连续亚稳态都是偏大,而且都是递增呢?
  • 这个时候我觉得可靠的办法就是在写端记录写了多少个数据,到了一个阈值后,就利用电平双握手传递到读,然后启动trigger,在读端也一样,先counter读走多少个数,到一定阈值以后也用电平双握手传递到写端。