笔试|面试|FPGA知识点大全系列(9)FIFO深度计算详解


前言

嗨,今天来继续学习FPGA相关知识点~


38.FIFO的深度计算问题

FIFO的最小深度问题,可以理解为两个模块之间的数据传输问题;只有在读取速度慢于写入速度的情况下,我们才需要一个FIFO,来暂时的寄存这些没有被读出去的数据;

一个最主要的逻辑思想是:

确定FIFO的大小,就是要找到在写入过程中没有被读取的数据的个数;即FIFO的深度等于未被读取的数据的数量。

现在考虑一种实例,A时钟域数据发往B时钟域,将会出现以下几种情况:

1️⃣情况1:fa>fb,且在读和写中都没有空闲周期;

例如:

写入频率fa = 80MHz,读取频率fb = 50MHz。
突发长度即要写入的数据数目为120个。

计算如下:

写入一个数据需要的时间 = 1 / 80MHz = 12.5ns;
写入突发事件中所有数据需要时间 = 120 * 12.5 = 1500ns;
读取一个数所需时间 = 1 / 50MHz = 20ns;
所有数据写入完成后使用1500ns,1500ns可以读出数据为 = 1500 / 20 = 75个。
所以,要在FIFO中存储的剩余数据量为 120 - 75 = 45;
故:设计的FIFO的最小深度为45!

2️⃣情况2:fa>fb,两个连续的读写之间有一个时钟周期延迟;

这种情况和情况1️⃣一样,仅仅是人为的制造了某种混乱;

3️⃣情况3:fa>fb,在读和写中都有空闲周期;

例如:
写入频率fa = 80MHz,读取频率fb = 50MHz。
突发长度即要写入的数据数目为120个。
两个连续的写之间空闲周期为1,两个连续的读之间空闲周期为3;

计算如下:
可以理解为每两个时钟周期写入一次数据,每四个时钟周期读出一个数据。
写入一个数据需要的时间 = 2 * (1 / 80MHz) = 2 * 12.5 = 25ns;
写入突发事件中所有数据需要时间 = 120 * 25 = 3000ns;
读取一个数所需时间 = 4 * (1 / 50MHz) = 4 * 20 = 80ns;
所有数据写入完成后使用3000ns,可以读出数据为 = 3000 / 80 = 37.5 ≈ 37个。
所以,要在FIFO中存储的剩余数据量为120 - 37 = 83;
故:设计的FIFO的最小深度为83!

4️⃣情况4:fa>fb,读写使能的占空比给定;

例如:
写入频率fa = 80MHz,读取频率fb = 50MHz。
突发长度即要写入的数据数目为120个。
写使能占空比为50%,读使能占空比为25%。
这种情况和情况3️⃣一样,没有什么区别;

**【注】**这里的情况2️⃣和情况4️⃣仅仅是为了说明同一个问题可以通过不同的方式来提问。

5️⃣情况5:fa<fb,在读和写中都没有空闲周期;

例如:
写入频率fa = 30MHz,读取频率fb = 50MHz。
突发长度即要写入的数据数目为120个。

计算如下:
在这种情况下,深度为1的FIFO就足够了,因为不会有任何数据的丢失,因为读比写快。

6️⃣情况6:fa<fb,在读和写中都有空闲周期

例如:
写入频率fa = 30MHz,读取频率fb = 50MHz。
突发长度即要写入的数据数目为120个。
两个连续的写之间空闲周期为1,两个连续的读之间空闲周期为3;

计算如下:
可以理解为每两个时钟周期写入一次数据,每四个时钟周期读出一个数据。
写入一个数据需要的时间 = 2 * (1 / 30MHz) = 2 * 33.33 = 66.667ns;
写入突发事件中所有数据需要时间 = 120 * 66.667 = 8000ns;
读取一个数所需时间 = 4 * (1 / 50MHz) = 4 * 20 = 80ns;

所有数据写入完成后使用8000ns,可以读出数据为 8000 / 80 = 100个。
所以,要在FIFO中存储的剩余数据量为120 - 100 = 20;
故:设计的FIFO的最小深度为20!

7️⃣情况7:fa=fb,在读和写中都没有空闲周期。

例如:
写入/读取频率fa = fb = 30MHz。
突发长度即要写入的数据数目为120个。
计算如下:
如果clka和clkb之间没有相位差,则不需要FIFO;
如果clka和clkb之间有一定的相位差,一个深度为“1”的FIFO就够了。

8️⃣情况8:fa=fb,在写和读中都有空闲周期

例如:
写入频率fa = 50MHz,读取频率fb = 50MHz。
突发长度即要写入的数据数目为120个。
两个连续的写之间空闲周期为1,两个连续的读之间空闲周期为3;

计算如下:
可以理解为每两个时钟周期写入一次数据,每四个时钟周期读出一个数据。
写入一个数据需要的时间 = 2 * (1 / 50MHz) = 2 * 20 = 40ns;
写入突发事件中所有数据需要时间 = 120 * 40 = 4800ns;
读取一个数所需时间 = 4 * (1 / 50MHz) = 4 * 20 = 80ns;
所有数据写入完成后使用4800ns,可以读出的数据为4800 / 80 = 60个。
所以,要在FIFO中存储的剩余数据量为120 - 60 = 60;
故:设计的FIFO的最小深度为60!

9️⃣情况9:数据速率如下所示;

例如:
写入80个数据,需要100个时钟周期,(写入的时钟随机);
读出8个数据,需要10个时钟周期,(读取的时钟随机)。
计算如下:
①上述表明,书写频率等于读出频率
②读和写都可以在任何随机的时刻发生,以下是一些可能性。
在这里插入图片描述
在上述情况中,完成写入所需的周期如下:
在这里插入图片描述
在FIFO设计中,为了更稳妥的考虑,我们选择最坏的情况下进行数据传输,以此来设计FIFO的深度,以避免数据丢失;
最坏的情况即:写和读之间的数据率差异应该最大,因此对于写操作,考虑最大的数据速率,对于读操作,考虑最小的数据速率。
故:考虑上述可能性中的第四种情况(即所谓的“背靠背”情况):

计算如下:
160个时钟周期内,写入160个数据;
数据的读取率为8个数据/10个时钟周期;
在160个时钟周期内可以读取的数据量为:160 * 8 / 10 = 128。
因此,需要存储在FIFO中的剩余字节数为160 - 128 = 32。
故:设计的FIFO的最小深度为32!

1️⃣0️⃣情况10:以不同的形式给出写入和读取的规则。

例如:
fa = fb / 4;
Tenb = Ta * 100;
enb的占空比为25%。

计算如下:

在这种情况下,就需要假设一些数值:
设:fb = 100MHz。则,fa = 1 / 4 * 100 = 25MHz。
则:Ta = 40ns,Tenb = 4000ns;
又因为写使能占空比25%。
则:写使能时间为4000ns / 4 = 1000ns。

】此处认为fb为写时钟,因为只有这样,才能是写入的比读出的快,当然了,把fa当做写时钟,下面就没法算了,哈哈。

突发长度 = 1000ns / 10ns = 100个;
在写入的1000ns内,可以读出 1000ns / 40ns = 25个;
所以,要在FIFO中存储的剩余数据量为100 - 25 = 75;
故:设计的FIFO的最小深度为75!

】在此情况下,理解清楚规则非常之重要!

笔试题实例:

假设两个异步时钟clk_a和clk_b,clk_a = 148.5M,clk_b = 140M。如图所示,clk_a时钟域中连续1920个16bit的数据通过data_valid标记,有效数据之后,紧接着720个无效数据时钟周期。请问,该数据通过异步fifo同步到clk_b时钟域,异步fifo的最小深度是多少?请写出计算过程。
在这里插入图片描述
【解析】
考虑情况9️⃣中case-4案例,即所谓的“背靠背”情况:
在a时钟域通过1920 * 2 = 3840个Ta传输3840个数据;

Ta = 1 / 148.5MHz ≈ 6.734ns;
Tb = 1 / 140MHz ≈ 7.143ns。
3840 *6.734 =25858.56ns;
在25858.56ns内,通过clk_b读取的个数为:25858.56 / 7.143 ≈ 3620个。
那么,剩余未读完的个数为3840 - 3620 = 220;

即FIFO的深度最小为220。

不过我觉得,只要理解了FIFO深度计算的原理,公式不公式的就不重要了。
最后,需要注意的是,我们在本文通篇计算的都是最小FIFO深度,但是在实际应用中,尤其是异步FIFO的应用中,需要使用格雷码计数,这就要求FIFO的深度为2的整数次幂,否则格雷码计数到最大值跳变为0时,将出现多位变化的情况,不符合设计。异步FIFO深度不是2的整数次幂情况下,则可能需要特殊处理,需要使用别的编码方式了。

本文参考:

关注“FPGA学习者”后台回复【FIFO深度】获取文档原文及翻译版本。


往期精彩

笔试|面试|FPGA知识点大全系列(1)
笔试|面试|FPGA知识点大全系列(2)
笔试|面试|FPGA知识点大全系列(3)
笔试|面试|FPGA知识点大全系列(4)
笔试|面试|FPGA知识点大全系列(5)
笔试|面试|FPGA知识点大全系列(6)
笔试|面试|FPGA知识点大全系列(7)之异步FIFO设计
笔试|面试|FPGA知识点大全系列(8)之时序分析

### 计算FIFO队列的深度计算机科学中,特别是操作系统和硬件设计领域,理解并计算FIFO(First In First Out)队列的深度对于优化性能至关重要。FIFO是一种数据结构,在这种结构里最先被存储的数据项也是最早能被移除的数据项。 #### FIFO队列深度的概念 FIFO队列的深度指的是该队列能够容纳的最大元素数量。这个参数直接影响系统的吞吐量、延迟特性以及资源利用率。例如,在CAN总线通信协议下,中断机制中的FIFO接收缓存MB0~5用于暂存接收到的消息帧[^3]。如果缓冲区已满而新的消息继续到来,则会触发溢出条件,并设置相应的事件标志位来通知上层应用处理异常情况。 #### 如何确定合适的FIFO深度? 为了合理设定FIFO队列的大小,通常需要考虑以下几个方面: - **应用场景需求**:不同的应用程序有不同的实时性和可靠性要求。比如在网络传输或者多媒体流媒体播放场景中,可能更倾向于较大的缓冲空间以减少丢包率;而在某些嵌入式控制系统里面则追求更低延时响应时间,此时较小容量但快速周转可能是更好的选择。 - **输入速率与输出速率差异**:理想情况下两者应该保持一致,但在实际环境中往往存在波动甚至突发流量高峰现象。因此有必要评估最坏状况下的峰值负载能力,并据此预留足够的余量给临时性的高负荷冲击提供保护屏障作用。 - **内存成本考量**:增加更多级联单元固然可以提高稳定性表现,不过也会相应占用额外物理地址空间从而抬升整体造价水平。所以还需要综合权衡性价比因素做出折衷决策方案。 具体到编程实现层面来说,可以通过定义一个固定长度数组作为底层容器模型,利用两个指针变量分别指向当前写入位置(head) 和读取起点(tail),并通过模运算操作完成循环覆盖效果模拟无限增长趋势。下面给出一段简单的Python伪代码示例展示这一过程: ```python class CircularBuffer(object): def __init__(self, size_max): self.max = size_max # 队列最大尺寸 self.data = [] # 存储数据列表 def append(self, x): # 添加新元素函数 """Append an element at the end of buffer""" self.data.append(x) if len(self.data) == self.max: self.cur = 0 # Full buffer reached, start overwriting from beginning. else: self.cur = (self.cur+1)%self.max def remove(self): # 移除旧元素函数 assert not self.is_empty(), &#39;Empty Buffer&#39; x = self.data[self.cur] del self.data[self.cur] return x def is_empty(self): # 判断是否为空函数 return len(self.data) == 0 ``` 上述例子仅适用于教学演示目的而非生产环境部署版本。真实项目开发过程中建议采用成熟可靠的第三方库文件或框架组件简化编码工作量的同时确保功能完备性及安全性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值