/DC_fifo

Primary LanguageVerilogGNU Lesser General Public License v2.1LGPL-2.1

介绍

使用verilog编写的异步fifo,读写端口各有一组时钟、读写使能、读写端口、满空指示、fifo使用量。在源码中对每个模块都进行注释,易于学习参考。
fifo_async.v为源文件,fifo_async.pdf为RTL视图。
testbench文件夹中有建立好的仿真工程,分别是VCS+Verdi和iverilog+gtkwave。喜欢哪个用哪个,配好环境make就行了。
iverilog+gtkwave加入windows支持。
日后还会持续更新完善

模块接口

写入侧
wclk 写时钟
wdata 数据输入端口
w_en 置1写使能
w_full 置1为写满
wuse fifo使用量
读取侧
rclk 读时钟
rdata 数据输出端口
r_en 置1读使能
r_empty 置1为读空
r_ok 数据有效
ruse fifo使用量

讲解

何为FIFO

FIFO( First Input First Output),说人话就是搞一个容器,先进去的东西先出来。当数据生产与数据使用的动作不同步的时候,就需要FIFO作为缓存。
举个例子,当我们看直播的时候,观看到的画面是连续而且平稳的,但是实际网络传输,速度有波动,时快时慢。为了让我们看直播的时候,画面不会因为网络波动而卡卡卡,就使用了FIFO的**。FIFO是一段存储器,有一定容量。来自网络端的直播画面,时快时慢地往FIFO里面写,只要别写爆了或者网络太烂数据跟不上,电脑显示端就能非常平稳地接收画面。一边写一边收,先写进去的画面会被先接收,FIFO本身的容量抹平了网络波动的影响,带来了流畅的画面。
这就是FIFO。

同步与异步

在同一个时钟域下,一边是时快时慢地生产数据,另一边需要能平稳地使用数据,使用同步FIFO就可以满足数据缓冲的需求。FIFO的空间不宜太大,满足需求即可,大了浪费资源;也不宜太小,太小FIFO的缓冲作用不明显。同步FIFO解决的是同一个时钟域下数据收发的问题。
在大型系统中,不可避免的会遇到数据跨时钟域通信的问题。不同的时钟域,若频率不同,则相互之间肯定是不同步的;若频率相同,时钟源不同,也存在频率漂移和相位偏差,同样需要看作不同步。如果只是单bit控制信号,拓展有效周期、打两拍就能解决问题了。而面对大量数据的跨时钟域传输,就需要使用异步FIFO。

逻辑框图

注:本逻辑框图仅与本人提供的源码匹配,若有更好的方案欢迎讨论。
注1:本模型的重点放在FIFO的逻辑结构上,双端口RAM的具体结构不作研究。
异步FIFO逻辑结构
异步FIFO的逻辑框图如图所示。
左侧为写控制器,用于接收写入的数据、写地址控制、写满状态判定、写地址转格雷码等。
中间是双端口RAM,特性是可以用异步的时钟进行读写。
右侧为读控制器,用于读出FIFO的数据、读地址控制、读空状态判定、读地址转格雷码等。
下方是转换成格雷码的读写地址,通过打两拍的方式,将数据同步到对面的控制器。

细节分析

FIFO与双端口RAM

FIFO的功能是数据先入先出,而双端口RAM可以异步地进行读写数据,这两者是如何结合的?
参考下面的循环队列视频,就能理解FIFO如何用线性存储器实现了。
循环队列

读写控制器

读写控制器是FIFO功能的重要组成部分。
以写控制器为例。从端口可以看出,FIFO的数据输入输出是不需要地址的。因此若需要将数据正确地写入双端口RAM,写控制器需要生成对应的写指针。当FIFO写入数据的时候,写指针+1。
为了防止读得太慢导致FIFO被写爆,写控制器会根据读控制器传来的读指针进行判定。如果写指针比读指针快了一圈,快要追上读指针的时候,会发出写满信号(w_full),通知外部设备不要再写了。
读控制器的功能也是同理,只不过读写指针互换,而且读指针追上写指针的时候,进行的是读空判定(r_empty)。
我编写的异步FIFO,和读写指针相关的所有寄存器,都比双端口RAM的地址宽度多1位,且多出的1位在最高位上。对于读写控制器来说,只需要对比读写指针的最高位,就能知道读写指针是不是跑在同一圈,简化了读空写满的检测步骤。
极简FIFO 下面举一个例子,建立一个深度为8的FIFO,RAM的地址宽度为3位,读写指针的宽度为4位。
此时写指针wptr与读指针rptr指向RAM的同一个地址。
若wptr=4'b0100,rptr=4'b0100,两者在同一圈,读空标志置1。
若wptr=4'b0100,rptr=4'b1100,读指针快一圈,写满标志置1。

二进制与格雷码

能看到这里的人,想必应该都明白二进制编码是什么。
格雷码和二进制编码的不同之处在于,二进制编码的数值+1,连续进位会导致多个位的状态发生变化,而格雷码数值+1在任何情况下都只会有1位发生变化。读写指针跨时钟域同步会遇到各种亚稳态的情况,指针的变化又只会不断+1,因此利用格雷码的特性可以极大降低数据出错的概率。
二进制与格雷码的相互转换可以使用组合逻辑完成。
二进制转格雷码,将二进制码右移一位,然后与原二进制码进行异或。
二进制转格雷码
读指针转换的verilog实现assign rptr_grey = (rptr >> 1) ^ rptr;
格雷码转二进制
格雷码转二进制,稍微麻烦一点。
读时钟域的写指针格雷码转二进制的verilog使用行为级描述实现,实际综合成组合逻辑。

integer j;
always @ (rq2_wptr_grey)
begin
    w2rptr[ASIZE-1]=rq2_wptr_grey[ASIZE-1];
    for(j=ASIZE-2;j>=0;j=j-1)
        w2rptr[j]=w2rptr[j+1]^rq2_wptr_grey[j];
end

如果觉得不错,可以去B站支持一下详解异步FIFO原理与Verilog模型

功能仿真

进入两种仿真文件夹查看