2. FIFO核心原理:先进先出原则、读写指针机制、空/满标志判断
大家好,我是你们的硬件工程师老K。今天咱们来聊聊FIFO最核心的三个东西:先进先出、读写指针、空满判断。这三个概念,说白了就是FIFO的灵魂。你把这三点吃透了,后面写代码、调时序、debug都会顺很多。
2.1 先进先出原则——排队买奶茶的智慧
FIFO的全称是First In First Out,翻译过来就是“先进先出”。这个概念其实特别生活化。你想想看,排队买奶茶,谁先来谁先拿到,这就是FIFO。
在数字电路里,FIFO就是一个数据队列。数据从一端写入,从另一端读出。先写进去的数据,必须先被读出来。这个顺序不能乱。
为什么一定要先进先出? 我遇到过不少刚入行的同学问我这个问题。其实原因很简单:很多应用场景要求数据保持原有的顺序。比如你采集一段音频信号,先采到的样本必须先处理,否则声音就乱套了。再比如网络数据包,顺序乱了,接收端根本拼不回去。
核心要点: FIFO不改变数据的相对顺序。它就像一个透明的管道,数据进去是什么顺序,出来还是什么顺序。
2.2 读写指针机制——两个“游标”的故事
FIFO内部是怎么实现先进先出的?靠的就是两个指针:写指针(Write Pointer)和读指针(Read Pointer)。
我个人习惯把这两个指针想象成两个“游标”。写指针指向下一个要写入的位置,读指针指向下一个要读出的位置。每次写入一个数据,写指针就往后移动一格。每次读出一个数据,读指针也往后移动一格。
这两个指针,说白了就是两个计数器。它们循环递增,从0走到FIFO深度-1,然后回到0重新开始。这就是所谓的“环形缓冲区”思想。
我给大家画个图,一看就明白了:
你看这个图,写指针指向D3位置,说明D0、D1、D2已经写入了。读指针指向D1位置,说明D0已经被读走了。两个指针之间的区域,就是当前FIFO里存着的数据。
小技巧: 我建议你在设计FIFO时,把指针宽度设得比地址位宽多1位。这样判断空满会方便很多。后面我会详细讲为什么。
2.3 空/满标志判断——FIFO的“红绿灯”
空标志和满标志,是FIFO对外最重要的两个状态信号。它们告诉外部电路:现在能不能写?能不能读?
空标志(Empty Flag): 当读指针追上了写指针,说明FIFO里一个数据都没有了。这时候读操作是无效的,读出来的数据是垃圾。
满标志(Full Flag): 当写指针追上了读指针,说明FIFO已经存满了。这时候写操作会覆盖掉还没读走的数据,造成数据丢失。
嗯,这里要注意一个关键点:空和满的时候,两个指针的值是相等的!那怎么区分呢?
我刚开始学的时候也困惑过这个问题。后来老师告诉我一个方法:看指针的额外标志位。
具体做法是这样的:
- 给每个指针多加一个标志位,比如叫wrap bit
- 每次指针从最大值回到0时,这个标志位翻转一次
- 空状态:两个指针的值相等,且标志位也相等
- 满状态:两个指针的值相等,但标志位不同
说白了,就是用这个额外的标志位来区分“空”和“满”。这个方法在实际工程中非常常用。
避坑指南: 我曾经在一个项目里吃过亏。当时设计了一个深度为16的FIFO,指针用4位二进制表示。结果空满判断总是出错。后来发现,当指针从15回到0时,如果不加额外标志位,空和满根本分不清。从那以后,我所有FIFO设计都老老实实加了标志位。
咱们来看一段简单的Verilog代码,演示空满判断的逻辑:
// 假设FIFO深度为16,指针宽度为5位(4位地址+1位标志)
reg [4:0] wr_ptr; // 写指针
reg [4:0] rd_ptr; // 读指针
// 空标志:指针完全相等
assign empty = (wr_ptr == rd_ptr);
// 满标志:地址位相等,但标志位不同
assign full = (wr_ptr[4] != rd_ptr[4]) &&
(wr_ptr[3:0] == rd_ptr[3:0]);
这段代码看着简单,但背后有个细节:为什么满标志要判断地址位相等且标志位不同?因为满的时候,写指针刚好比读指针多绕了一圈。标志位不同,就表示“多绕了一圈”。
2.4 三种常见判断方式对比
在实际工程中,空满判断有好几种实现方式。我给大家整理了一个表格,方便对比:
| 判断方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 额外标志位法 | 指针多1位标志位,空满时地址相同但标志位不同 | 实现简单,资源消耗少 | 指针宽度增加1位 |
| 计数器法 | 用一个计数器记录FIFO中数据个数 | 空满判断直观,还能知道剩余空间 | 计数器逻辑稍复杂,深度大时计数器位宽大 |
| 格雷码法 | 指针用格雷码编码,跨时钟域时更安全 | 适合异步FIFO,抗亚稳态 | 格雷码转二进制需要额外逻辑 |
我个人最常用的是额外标志位法。原因很简单:它够简单,够可靠。在同步FIFO里,这个方法几乎不会出问题。如果是异步FIFO,我建议用格雷码法,因为跨时钟域传递时格雷码更安全。
总结一下: FIFO的核心原理其实不复杂。先进先出是规则,读写指针是工具,空满标志是状态。这三个东西组合在一起,就构成了FIFO最基本的骨架。你把这个骨架搭好了,后面加什么功能都方便。
好了,这一章的内容就到这里。记住我刚才说的:指针多加一位标志位,空满判断就不会搞混。这个经验是我用实际教训换来的,希望对你有帮助。