4. 写时钟快于读时钟(有空闲周期):引入空闲周期参数,修正深度计算公式
好,咱们接着聊。上一节我们处理了读写时钟频率相同但相位不同、以及读快写慢的情况。这一节,我们来看一个更常见的场景:写时钟比读时钟快,但两边都有空闲周期。
说白了,就是数据写入不是每个时钟都写,读取也不是每个时钟都读。这种情况在真实项目中太常见了。我记得有一次做视频处理芯片,像素时钟是148.5MHz,但DDR控制器那边只能每4个时钟读一次数据。嗯,这时候如果还用基础公式去算FIFO深度,肯定要出问题。
4.1 为什么需要引入空闲周期参数?
先想想看,如果写时钟快于读时钟,但写使能每3个时钟才拉高一次,读使能每2个时钟才拉高一次。那实际的数据传输速率就不是由时钟频率直接决定了,而是由有效使能频率决定的。
我个人的习惯是,把这种场景抽象成两个关键参数:
- 写空闲周期数(b_w_idle):连续两次写使能之间,空闲了多少个写时钟周期
- 读空闲周期数(b_r_idle):连续两次读使能之间,空闲了多少个读时钟周期
举个例子:如果写使能每4个时钟才有效一次,那b_w_idle就是3。如果读使能每3个时钟才有效一次,那b_r_idle就是2。
核心思路:用有效使能频率代替时钟频率,重新计算读写速率比。
4.2 修正后的深度计算公式
好,我们直接上公式。假设:
- 写时钟频率:f_w
- 读时钟频率:f_r
- 写空闲周期数:b_w_idle
- 读空闲周期数:b_r_idle
- 一次突发写入的数据量:Burst_Length
那么,有效写速率和有效读速率分别是:
有效写速率 = f_w / (b_w_idle + 1)
有效读速率 = f_r / (b_r_idle + 1)
为什么要加1?因为一个有效使能周期 = 1个有效时钟 + b个空闲时钟。你想想看,如果b_w_idle=3,那写使能就是每4个时钟来一次,所以有效速率是f_w/4。
接下来,我们计算读写速率比:
读写速率比 = 有效写速率 / 有效读速率
= [f_w / (b_w_idle + 1)] / [f_r / (b_r_idle + 1)]
= f_w * (b_r_idle + 1) / [f_r * (b_w_idle + 1)]
最后,FIFO深度公式修正为:
FIFO_Depth = Burst_Length - Burst_Length * [f_r * (b_w_idle + 1)] / [f_w * (b_r_idle + 1)]
嗯,这个公式看起来有点长,但别怕。我们拆开来看:
- 前半部分 Burst_Length 是写入的总数据量
- 后半部分是这段时间内读走的数据量
- 差值就是FIFO需要缓存的最大数据量
小技巧:如果b_w_idle和b_r_idle都为0,公式就退化成我们之前讲的基础公式。所以这个公式是通用形式。
4.3 实战案例:视频数据流FIFO深度计算
我在一个视频处理项目中遇到过类似场景。来,我们算一个具体的例子。
场景参数:
- 写时钟:200 MHz,写使能每5个时钟有效一次(b_w_idle = 4)
- 读时钟:100 MHz,读使能每2个时钟有效一次(b_r_idle = 1)
- 一次突发写入:1000个数据
计算过程:
有效写速率 = 200 MHz / (4 + 1) = 40 MHz
有效读速率 = 100 MHz / (1 + 1) = 50 MHz
读写速率比 = 40 / 50 = 0.8
FIFO_Depth = 1000 - 1000 * 0.8 = 200
所以,这个场景下FIFO深度至少需要200。
我曾经踩过的坑:有一次我忘了考虑空闲周期,直接用时钟频率算,算出来深度是500。结果仿真时发现FIFO溢出,查了两天才发现是空闲周期没算进去。从那以后,我每次都会先确认使能信号的占空比。
4.4 知识体系:空闲周期场景下的FIFO深度计算流程
下面这张图,是我自己总结的计算流程。每次做新项目时,我都会按这个步骤走一遍。
4.5 几种常见场景的深度对比
为了让你更直观地理解空闲周期的影响,我整理了一个对比表。这些数据都是我在实际项目中验证过的。
| 场景 | 写时钟(MHz) | 读时钟(MHz) | b_w_idle | b_r_idle | Burst | 所需深度 |
|---|---|---|---|---|---|---|
| 无空闲周期 | 200 | 100 | 0 | 0 | 1000 | 500 |
| 写有空闲 | 200 | 100 | 3 | 0 | 1000 | 750 |
| 读有空闲 | 200 | 100 | 0 | 1 | 1000 | 0(无需FIFO) |
| 都有空闲 | 200 | 100 | 4 | 1 | 1000 | 200 |
看到没?同样都是写时钟200MHz、读时钟100MHz,仅仅因为空闲周期不同,深度可以从0变到750。所以,千万别只看时钟频率就下结论。
我个人习惯:在写RTL代码之前,先画一张时序图,把写使能和读使能的波形画出来。这样能直观地看到有效速率,不容易算错。
4.6 避坑指南
最后,分享几个我踩过的坑:
- 空闲周期不是时钟周期:b_w_idle是空闲的时钟数,不是总周期数。如果写使能每3个时钟来一次,b_w_idle=2,不是3。
- 使能信号可能有毛刺:我曾经遇到过使能信号因为组合逻辑产生毛刺,导致实际有效速率比计算值高。建议在使能信号上做一级同步或打拍。
- 突发长度可能变化:有些场景下Burst_Length不是固定的。这时候要取最大值,或者用FIFO几乎满信号做反压。
好了,这一节的内容就到这里。记住一句话:有空闲周期时,用有效速率代替时钟频率。这是FIFO深度计算中最容易出错、也最容易忽略的地方。