本文使用 Zhihu On VSCode 创作并发布
概述
异步FIFO用来在两个异步时钟域间传输数据。
如上图所示,两个系统“System X” 和 “System Y” 分别工作在两个不同的时钟域,“System X”将数据写入 FIFO,“System Y” 将数据从FIFO中读出。“fifo_full” 和 “fifo_empty” 信号负责对上溢(overflow)和下溢(underflow)的控制,如果在“fifo_full”为高时,不应该再写入数据,否则会将之前写入的数据覆盖掉。当“fifo_empty” 信号为高时,不应该读取数据,否则会读出无效的数据。FIFO数据“先进先出”,先写入的数据先读取。
结构框图
异步FIFO结构如上图所示
- 第1部分是双口RAM,用于数据的存储。
- 第2部分是数据写入控制器
- 第3部分是数据读取控制器
- 读指针同步器
使用写时钟的两级触发器采集读指针,输出到数据写入控制器。 - 写指针同步器
使用读时钟的两级触发器采集写指针,输出到数据读取控制器。
数据读写控制器
读写指针
- 在同步FIFO中,通常使用二进制计数器实现读写指针。
例如深度为16的同步FIFO,将读写指针位宽设置为5,低四位用作RAM的地址,最高位用于判断FIFO满。
当读写指针相等时,表示写入的数据都被读取出来,此时FIFO为空。
当读写指针第四位相等,而最高位相反时,表示写指针比读指针多循环了一次,此时FIFO为满。
对于异步FIFO,需要将写指针从写时钟域同步到读时钟域,产生空信号,需要将读指针从读时钟域同步到写时钟域,产生满信号。上述结构框图中4,5部分使用两级同步器来减小跨时钟域产生亚稳态的概率。
由于需要同步多位宽的指针信号,如果指针继续使用二进制计数器,当指针从 11111 跳变到 00000 时,每以位数据都需要同步,产生亚稳态的概率会很高。5bit数据每一位都有可能采集出错,可能会将五位数据任何可能的组合采样并同步到新的时钟域中。
可以使用格雷码代替二进制数进行跨时钟域的传输。
格雷码的特点:
- 由于具有相邻的两个数据只有一位数据不同的特性,最高位和最低位也只有一位数据不同,适合用于跨时钟域的传输,降低错误发生的概率。
- 只有使用n位格雷码表示 2^n 个数据,格雷码才具有上述特性。
格雷码和二进制数的转换:
二进制数转格雷码:
wire [4:0] gray;
wire [4:0] bin;
assign gray = {bin[4], bin[4:1] ^ bin[3:0]}
格雷码转二进制数:
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地址信号,将其转换为格雷码进行跨时钟域传输。
空满信号产生
如上图所示,使用4位格雷码作为深度为8的FIFO的读写指针。
将格雷码转换成四位二进制数,使用二进制数低三位作为访问RAM的地址。
- 与同步FIFO类似,当读写指针相等时,得出FIFO为空
- 当写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满。
同步器
使用移位寄存器实现同步器。
双口RAM设计与仿真
设计一个深度位宽可调的双口RAM,默认参数深度为16,位宽为8。
设计和测试文件见文章末尾。
仿真波形:
- 连续写入16个数据:
- 连续读取16个数据:
- 同时写入、读取16个数据:
异步FIFO的设计与仿真
- 连续写入16个数据:
- 连续读取16个数据:
工程源码
链接:https://pan.baidu.com/s/1d7HPLnj1pjXMPgWE5epdvQ
提取码:pxgc