概述
在之前的一篇文章中我们已经总结了格雷码的原理和使用,本篇将继续多比特跨时钟域设计系列,总结 异步FIFO 的设计。
本篇介绍的 异步FIFO 设计原理是基于上一篇文章推荐的论文《Simulation and Synthesis Techniques for Asynchronous FIFO Design》,我将会从以下几点来总结:
- FIFO整体架构及模块划分
- 读写指针是怎么工作的
- 如何产生满空信号
- 读写时钟频率差异对FIFO功能有影响吗
- 如何产生将满空(almost full/empty)信号
FIFO整体架构及模块划分
可以看到异步FIFO的设计主要有5部分组成:
- FIFO Memory
- 双口RAM存储数据
- sync_r2w
- 同步读数据指针到写时钟域
- sync_w2r
- 同步写数据指针到读时钟域
- wptr_full
- 处理写指针和满信号的逻辑
- rptr_empty
- 处理读指针和空信号的逻辑
Memory不用多说,在FPGA中,我们可以用block ram也可以用distributed ram。由于使用格雷码的原因,同步器也比较简单,只是最简单的两级寄存器同步器。所以异步FIFO设计的重难点就在 读写指针和满空信号 的处理上。
读写指针是怎么工作的
FIFO的读写指针即读写地址,但也不完全是。在异步FIFO设计中,读写指针一般比真正用来读写Memory的地址要多一位,这是为了判断满空信号。(下文我不会再特意区分读写指针和读写地址)
写指针总是指向下一个要写入的地址。对于一个写操作,数据会被写入写指针指向的地址,然后写指针加一指向下一个要写入的地址。
读指针则总是指向下一个要读的地址。要读的数据也会提前准备在输出数据端口上,对于一个读操作,Receiver可以直接获取数据端口的数据,然后读指针加一指向下一个要读的数据。
如何产生满空信号
空信号是用来防止FIFO underflow(即从一个空FIFO里面读数据)的标记,只有当我们从FIFO里面读数据时才可能出现underflow,所以空信号的产生逻辑放在读时钟域。
空信号的产生比较简单,直接对比读写指针是否相等即可(读指针追上写指针),注意这里的写指针是同步到读时钟域之后的写指针。为了empty信号的寄存器输出,比较时读指针我们可以用 rgraynext 而不是 rptr(rptr要比rgraynext晚一拍)。
assign rempty_val = (rgraynext == rq2_wptr);
always_ff @(posedge rclk or posedge rrst)
if(rrst)
rempty <= 1'b1;
else
rempty <= rempty_val;
满信号是用来防止FIFO overflow的标记(即向一个满FIFO里面写数据),只有当我们向FIFO里面写数据时才可能overflow,所以满信号的产生逻辑放在写时钟域。
满信号的产生稍微复杂点,由于N比特格雷码的低N-1比特具有对称性(上一篇总结格雷码提到的),比较读写指针时我们像二进制码那样仅仅判断第N比特不等而低N-1比特相等是不够的,会产生错误的满信号,如图2所示。为了产生正确的满信号,我们需要同时判断以下三个条件:
- 第N-1比特不相等
- 第N-2比特不相等
- 低N-3比特相等
与空信号类似,为了full信号的寄存器输出,对于写指针我们可以用 wgraynext 而不是 wptr(wptr要比wgraynext晚一拍)。
assign wfull_val = ((wgraynext[N] != wq2_rptr[N])
&&
(wgraynext[N-1] != wq2_rptr[N-1]) &&
(wgraynext[N-2] == wq2_rptr[N-2]) &&);
always_ff @(posedge wclk or posedge wrst)
if(wrst)
wfull <= 1'b0;
else
wfull <= wfull_val;
读写时钟频率差异对FIFO功能有影响吗
对于异步FIFO来说,读写时钟的频率一般是不一样的。当指针从快时钟域同步到慢时钟域时,在慢时钟域可能会miss掉一些指针的值,比如指针在快时钟域值的变化是1,2,3,4,5,6,但是同步到慢时钟域之后只有1,3,6。这种情况带来两个需要讨论的问题:
被同步的格雷码值变化了两次但是只被采样了一次,说明在采样期间有不止一个bit发生了变化,这会导致多比特同步的问题吗?
答案是 NO。同步多比特会导致错误是因为有多个比特在时钟采样沿附近发生值的变化。而上述问题描述的场景下,在时钟采样沿之前格雷码已经变化了两次,说明只有第二次是靠近采样沿,其实对于格雷码来说还是单比特采样。
被同步的格雷码值变化了两次但是只被采样了一次,这可能会导致FIFO没有及时产生满空信号而使得FIFO overflow 或 underflow吗?
答案是 NO。假设现在写时钟域为快时钟域,按照我们的设计,满信号的产生在写时钟域端,在慢时钟域的读指针变化为4,5,6,同步到写时钟域只看到5,那么写指针追上同步后的读指针即5时会立即assert满信号。可以看到这里的满信号实际上是一个更“悲观”的满信号,因为慢时钟域的读指针已经是6了,FIFO没有真正的满。这会导致FIFO功能错误吗,不会!只是效率稍微受到影响。同样的,空信号也可能是一个“悲观”的空信号,但不会更“乐观”导致FIFO underflow。
如何产生将满空(almost full/empty)信号
有一些设计要求判断almost full/empty,其实也比较简单,我们只需要在判断满空的基础上加一些余量就好了。
localparam ALMOST_EMPTY_MARGIN = 4;
localparam ALMOST_FULL_MARGIN = 4;
assign rempty_val = (rgraynext+ALMOST_EMPTY_MARGIN == rq2_wptr);
assign wfull_val = (wgraynext+ALMOST_FULL_MARGIN == wq2_rptr);
总结
本篇文章总结了异步FIFO设计的基本原理和设计。想要获取更多细节的读者可以自行阅读本篇文章的参考论文。
参考资料
- 《Simulation and Synthesis Techniques for Asynchronous FIFO Design》
最后
以上就是漂亮学姐为你收集整理的异步fifo_FPGA 设计之 跨时钟域(五 - 异步FIFO)的全部内容,希望文章能够帮你解决异步fifo_FPGA 设计之 跨时钟域(五 - 异步FIFO)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复