你问我FIFO有多深?

我们通常需要异步FIFO用作两个不同时钟域的模块之间的数据缓冲,确保不会丢失数据,只有在读速率快,写速率慢的时候,需要使用FIFO才能缓存来不及读取的数据。反之??

通常因为读速率慢于写速率,慢的模块来不及读取的数据要被缓存下来,所以说,FIFO的工作模式应该是,数据突发写入的形式,Bursts。也就是隔一段时间突发的写一组数据如果连续写入的话,因为读速率慢于写速率,FIFO肯定会写满,FIFO的深度就需要无穷大,才能确保数据不丢失

我们需要知道,读写频率,突发速率,突发长度进而确定FIFO的最小深度

上文的意思,已经明确,我们需要的FIFO深度是因为读取速率慢于写入速率而未能读取的数据量。明确了这一点原理,就可以不需要记任何公式,而直接计算任何情况下的传输问题;看下图:

Case-1:fA > fB 读写之间没有空闲周期

写速率fA = 80MHz

读速率fB = 50MHz

突发长度Burst Length = 120

读写之间没有空闲周期,是连续读写一个突发长度

Sol:

写一个数据需要的时间 = 1 / 80MHz = 12.5ns

写一个突发需要的时间 = 120 * 12.5ns = 1500ns

读一个数据需要的时间 = 1 / 50MHz = 20ns

每1500ns,120个数据被写入FIFO,但读一个数据需要20ns的时间

可以计算出,1500ns内读出多少个数据,1500 / 20 = 75

剩下的没有读出,就存在FIFO中,则需要120 - 75 = 45

所以这种情况下,需要的FIFO最小深度为45

Case-2:fA > fB 在两个连续读写之间有一个周期的延迟

Sol:

这个题目是制造了一些假象,这其实和Case-1的情况是一样的,因为两个连续的读写之间通常都会有延迟。解决方法,如同Case-1

Case–3:fA > fB读写都有空闲周期(IDLE Cycles)

写速率fA = 80MHz

读速率fB = 50MHz

突发长度Burst Length = 120

两个连续写入之间的空闲周期为 = 1

两个连续读取之间的空闲周期为 = 3

Sol:

两个连续写入之间的空闲周期为1的意思是,每写入一个数据,要等待一个周期,再写入下一个数据。这也可以理解为每两个周期,写入一个数据。

两个连续读取之间的空闲周期为3的意思是,每读取一个数据,要等待三个周期,再读取下一个数据。这也可以理解为每四个周期,读取一个数据。

写一个数据需要的时间 = 2 * (1 / 80MHz) = 25ns

写一个突发需要的时间 = 120 * 25ns = 3000ns

读一个数据需要的时间 = 4 * (1 / 50MHz) = 80ns

每3000ns,120个数据被写入FIFO,但读一个数据需要80ns的时间

可以计算出,3000ns内读出可以多少个数据,3000 / 80 = 37.5

剩下的没有读出,就存在FIFO中,则需要120 - 37.5 = 82.5 约等于 83

所以这种情况下,需要的FIFO最小深度为83

Case-4:fA > fB并给出了读写使能的百分比

写速率fA = 80MHz

读速率fB = 50MHz

突发长度Burst Length = 120

写使能占得百分比为 = 50% = 1 / 2

读使能占得百分比为 = 25% = 1 / 4

Sol:

用你聪明的大脑想一想,这是不是和Case-3也是一模一样呢,写使能占得百分比为50%,即每两个周期写入一个数据。读使能占得百分比为25%,即每四个周期读取一个数据

Case-5:fA < fB 读写操作无空闲周期(每两个连续读写之间有一个周期延迟)

Sol:

这类题目,因为读取速率大于写入速率,FIFO永远不会被写满,所以FIFO深度为1就够了

Case-6:fA < fB 读写操作有空闲周期(读写使能占得百分比问题)

写速率fA = 30MHz

读速率fB = 50MHz

突发长度Burst Length = 120

两个连续写入之间的空闲周期为 = 1

两个连续读取之间的空闲周期为 = 3

Sol:

两个连续写入之间的空闲周期为1的意思是,每写入一个数据,要等待一个周期,再写入下一个数据。这也可以理解为每两个周期,写入一个数据。

两个连续读取之间的空闲周期为3的意思是,每读取一个数据,要等待三个周期,再读取下一个数据。这也可以理解为每四个周期,读取一个数据。

写一个数据需要的时间 = 2 * (1 / 30MHz) = 66.667ns

写一个突发需要的时间 = 120 * 66.667ns = 8000ns

读一个数据需要的时间 = 4 * (1 / 50MHz) = 80ns

每8000ns,120个数据被写入FIFO,但读一个数据需要80ns的时间

可以计算出,8000ns内读出可以多少个数据,8000 / 80 = 100

剩下的没有读出,就存在FIFO中,则需要120 - 100 = 20

所以这种情况下,需要的FIFO最小深度为20

Case-7:fA = fB 读写操作无空闲周期(每两个连续读写之间有一个周期延迟)

Sol:

很好理解

如果读写时钟为同一个时钟,则可以不需要FIFO

如果读写时钟存在相位差,FIFO深度为1,也是够了

Case-8:fA = fB 读写操作有空闲周期(读写使能占得百分比问题)

写速率fA = 50MHz

读速率fB = 50MHz

突发长度Burst Length = 120

两个连续写入之间的空闲周期为 = 1

两个连续读取之间的空闲周期为 = 3

Sol:

同样的解题思路。

两个连续写入之间的空闲周期为1的意思是,每写入一个数据,要等待一个周期,再写入下一个数据。这也可以理解为每两个周期,写入一个数据。

两个连续读取之间的空闲周期为3的意思是,每读取一个数据,要等待三个周期,再读取下一个数据。这也可以理解为每四个周期,读取一个数据。

写一个数据需要的时间 = 2 * (1 / 50MHz) = 40ns

写一个突发需要的时间 = 120 * 40ns = 4800ns

读一个数据需要的时间 = 4 * (1 / 50MHz) = 80ns

每4800ns,120个数据被写入FIFO,但读一个数据需要80ns的时间

可以计算出,4800ns内读出可以多少个数据,4800 / 80 = 60

剩下的没有读出,就存在FIFO中,则需要120 - 60 = 60

所以这种情况下,需要的FIFO最小深度为60

Case-9 如果数据速率如下所示

读写速率相等

每100个时钟写入80个数据

每10个时钟读取8个数据

突发长度为160

Sol:

写速率的其他20个周期的位置是随机的,所以就有了下面几种情况:

为了保证数据的传输不丢失,我们考虑到最坏的情况。

考虑的最坏的情况,就是写数据速率和读数据速率之间的差别最大。即写数据速率最大,读数据速率最小。

写操作最坏得情况是Case-4 ,即两次连续的突发写入,又称“背靠背”的情况。

即为在160个周期内写入160个数据。

读数据速率读出每个数据的时间为 = 8 / 10

所以160个周期读出数据的个数为 160 * (8 * 10) = 128

剩下的没有读出,就存在FIFO中,则需要160 - 128 = 32

所以这种情况下,需要的FIFO最小深度为32

Case-10:如下所示

写入时钟20MHz

读出时钟40MHz

每1000个时钟周期写入500个数据

每4个时钟周期读出1个数据

读写数据位宽一致。

Sol:

考虑到“背靠背”的情况突发长度则为500 * 2 = 1000

则为每1000个时钟周期写入1000个数据

每4个周期,读取一个数据。

写一个数据需要的时间 = 1 / 20MHz = 50ns

写一个突发需要的时间 = 1000 * 50ns = 50000ns

读一个数据需要的时间 = 4 * (1 / 40MHz) = 100ns

每50000ns,120个数据被写入FIFO,但读一个数据需要100ns的时间

可以计算出,50000ns内读出可以多少个数据,50000 /100 = 500

剩下的没有读出,就存在FIFO中,则需要1000- 500 = 500

所以这种情况下,需要的FIFO最小深度为500

SDRAM中应用

在SDRAM的应用中,我们通常使用的读写FIFO是突发长度的2倍,比如突发长度为256,那FIFO的深度设置为512,使得FIFO始终保持半满的状态。可以保证数据的传输

<think>好的,用户现在问的是关于深度不是2的整数次幂的异步FIFO,比如深度为10的情况,如何管理RAM地址指针和格雷码计数,以及空满信号的判断。之前的讨论是针对深度为2^n的情况,现在需要扩展到非2次幂的情况,这个问题可能更复杂一些。 首先,我需要回忆一下异步FIFO的基本原理。通常,使用格雷码是为了避免跨时钟域时的亚稳态问题,因为格雷码每次只改变一个位。在深度为2^n的情况下,地址指针的位宽是n+1位,这样可以覆盖整个地址空间,并通过比较格雷码的最高位和其他位来判断空满。 但深度为10的话,不是2的幂,常规的格雷码方案可能不适用。这时候可能需要调整地址指针的位宽或者采用不同的方法。用户可能想知道在这种情况下如何调整设计,同时保持正确的空满判断逻辑。 接下来,我需要考虑非2次幂FIFO的设计方法。常见的方法有两种:一种是仍然使用2^n的地址空间,比如深度10的话,用16个地址,但实际只使用其中的10个,这样可能会浪费空间,但设计起来相对简单。另一种是使用非2次幂的格雷码转换,但需要调整指针的比较方式,这可能比较复杂。 用户可能更关心的是第二种方法,即如何精确管理10个地址而不浪费空间。这时候可能需要使用模运算来处理指针的环绕,但格雷码在这种情况下是否还能保持每次只改变一个位的特性呢?这可能需要特殊处理。 此外,空满信号的判断逻辑也会不同。在传统的2^n深度情况下,满信号是当写指针比读指针多一圈,即差值等于深度。但在深度为10的情况下,可能需要类似的逻辑,但地址指针的位宽可能需要调整,或者使用额外的计数器来跟踪数据量。 可能的解决方案包括: 1. 扩展地址指针到最接近的2的幂,比如用4位地址(16个位置),但只使用10个,这样空满判断可以沿用原来的方法,但会有未使用的地址,可能导致资源浪费。 2. 使用非2次幂的格雷码,通过调整比较逻辑来判断空满,可能需要更复杂的比较逻辑,比如使用二进制比较然后转换为格雷码,或者使用模运算。 用户可能希望有一个具体的例子,比如如何设计深度为10的FIFO,包括地址指针的位宽、格雷码转换方法以及空满条件的具体判断逻辑。需要详细说明每个步骤,并验证其正确性。 还需要考虑跨时钟域同步的问题,确保读写指针的安全传递。由于深度不是2的幂,可能需要额外的逻辑来处理指针环绕,例如当指针超过深度时,取模运算,但格雷码的转换可能需要特别处理。 可能遇到的挑战包括如何保证格雷码在非2次幂情况下仍然每次只改变一个位,或者在指针环绕时正确转换地址。如果直接使用二进制指针取模,然后转换为格雷码,可能无法保持单比特变化,导致同步问题。 因此,可能的解决方法是仍然使用2次幂的地址空间,但实际深度小于该值,并通过空满条件来限制指针的有效范围。例如,使用4位地址(0-15),但实际只允许指针在0-9之间循环,当写指针到达9时,下一个地址是0,而不是10。这样,虽然地址空间是16,但实际使用10个,通过比较指针的位置来判断是否满。这种情况下,满的条件是写指针比读指针多10,但由于地址是模16的,需要调整比较逻辑。 或者,可以使用单独的计数器来跟踪写入和读取的数量,但这会增加设计的复杂度,并且在跨时钟域时可能需要同步多个计数器,不太可靠。 最终,可能需要结合两种方法:使用扩展的地址指针(2的幂次),但实际深度较小,并通过调整空满判断逻辑来处理非2次幂的情况。例如,当写指针和读指针的差值等于实际深度时,认为FIFO满,同时使用格雷码来同步指针的高位,确保跨时钟域的安全性。 需要具体举例说明,比如深度10,地址指针使用4位(0-15),实际使用0-9。当写指针从9回到0时,地址指针加1变为10,但取模后变为0,同时格雷码转换需要考虑这种非连续的地址变化是否会导致多位变化,从而影响同步。 这可能会有问题,因为地址10(二进制1010)转换为格雷码是1111,而地址9(1001)转换为格雷码是1101,两者之间的转换有两位变化,这违反了格雷码单比特变化的特性,可能导致同步错误。 因此,这种方法不可行,必须找到另一种方式。另一种方法是使用二进制指针,并在比较时转换为格雷码,但实际深度为10,地址从0到9,指针增加到9后回到0。此时,需要确保每次地址变化只有一位改变,但二进制地址在这种情况下无法做到,所以必须使用某种调整后的格雷码,或者采用不同的编码方式。 这可能比较复杂,因此常见做法是使用最接近的2次幂深度,如16,但实际只使用10个位置,虽然浪费资源,但设计更简单可靠。或者,使用双倍缓冲或其他方法,但可能超出用户的问题范围。 因此,用户的问题可能需要解释在非2次幂情况下,如何调整设计,可能的解决方案及其优缺点,并给出具体示例。需要明确说明地址指针的位宽,格雷码转换方法,以及空满判断逻辑的具体实现步骤,同时指出可能的挑战和注意事项。</think>#### **1. 非2次幂深度FIFO的核心挑战** 当FIFO深度$N$不是2的整数次幂(例如$N=10$)时,传统基于格雷码的异步FIFO设计面临以下问题: - **地址指针无法直接映射到格雷码**:格雷码要求地址空间为$2^n$,否则无法保证每次地址递增仅改变1个比特。 - **空满判断失效**:传统满条件(指针差值等于深度)依赖$2^n$的地址回绕特性,非2次幂会导致错误判断。 --- #### **2. 解决方案:虚拟扩展地址空间** 为兼容格雷码特性,需将实际深度扩展到最近的2次幂($M=16$),但逻辑上限制有效地址范围为$0 \sim N-1$(即$0 \sim 9$)。设计要点如下: **地址位宽定义**: - **物理地址位宽**:$m = \lceil \log_2 M \rceil = 4$位(覆盖$0 \sim 15$) - **有效地址范围**:$0 \sim 9$(实际使用) - **虚拟扩展区域**:$10 \sim 15$(物理存在但逻辑禁用) **格雷码转换规则**: - 写指针/读指针仍按4位二进制→格雷码转换: $$G[3:0] = B[3:0] \oplus \{B[4], B[3:1]\}$$ 其中$B[4]$为虚构的第5位(始终为0,因物理地址仅4位) --- #### **3. 空满判断逻辑的修正** 由于实际深度为10,需重新定义满条件: **二进制域判断**(在写时钟域): - **满条件**:$(W_{ptr} - R_{ptr}) \% M \geq N$ 即写指针比读指针多$N$个地址(考虑回绕) - **空条件**:$W_{ptr} == R_{ptr}$ **格雷码同步策略**: 1. 写指针$W_{ptr}$转换为格雷码$W_{gray}$,同步到读时钟域。 2. 读指针$R_{ptr}$转换为格雷码$R_{gray}$,同步到写时钟域。 3. **空判断在读时钟域进行**,**满判断在写时钟域进行**。 --- #### **4. 完整设计示例(深度=10,M=16)** ##### **4.1 指针管理逻辑** ```verilog // 写指针控制(写时钟域) always @(posedge wclk or negedge wrst_n) begin if (!wrst_n) wptr_bin <= 4'b0; else if (!full && wr_en) wptr_bin <= (wptr_bin == 15) ? 4'b0 : (wptr_bin + 1); end // 转换为格雷码并同步到读时钟域 assign wptr_gray = wptr_bin ^ (wptr_bin >> 1); sync_cell #(.STAGES(2)) sync_wptr2rclk (.clk(rclk), .din(wptr_gray), .dout(wptr_gray_sync)); // 读指针控制(读时钟域) always @(posedge rclk or negedge rrst_n) begin if (!rrst_n) rptr_bin <= 4'b0; else if (!empty && rd_en) rptr_bin <= (rptr_bin == 15) ? 4'b0 : (rptr_bin + 1); end // 转换为格雷码并同步到写时钟域 assign rptr_gray = rptr_bin ^ (rptr_bin >> 1); sync_cell #(.STAGES(2)) sync_rptr2wclk (.clk(wclk), .din(rptr_gray), .dout(rptr_gray_sync)); ``` ##### **4.2 空满判断实现** ```verilog // 空判断(读时钟域) assign empty = (rptr_gray == wptr_gray_sync); // 满判断(写时钟域) wire [4:0] wptr_ext = {1'b0, wptr_bin}; // 扩展为5位避免溢出 wire [4:0] rptr_ext = {1'b0, rptr_bin_sync}; // 同步后的读指针 assign diff = wptr_ext - rptr_ext; assign full = (diff >= 10) || (diff <= -6); // 处理回绕的差值 ``` **关键解释**: - **满条件计算**: 当$W_{ptr} - R_{ptr} \geq 10$或$\leq -6$(即$16-10=6$)时,实际数据量达到10。 例如: - $W=14$, $R=4$ → $14-4=10$ → 满 - $W=2$, $R=8$ → $2-8=-6$ → 满(实际差值=10) --- #### **5. 深度为10的FIFO工作流程验证** **场景模拟**: 1. **初始状态**: - $W_{ptr}=0$, $R_{ptr}=0$ → 空信号有效 2. **写入10个数据**: - $W_{ptr}$从0递增到9 → 实际地址0~9被写入 - 第10次写入时,$W_{ptr}$跳转到10(虚拟地址),但RAM不操作此地址 - **满条件触发**:$W_{ptr}=10$(二进制1010 → 格雷码1111) $R_{ptr}=0$(格雷码0000) → 差值=10,满信号有效 3. **读取数据时的回绕**: - 当$R_{ptr}=9$时,下一次读取回到0,差值计算自动处理回绕 --- #### **6. 非2次幂设计的潜在问题与优化** **问题**: - **地址浪费**:物理RAM需支持16地址,但仅用10个,利用率62.5% - **满判断复杂度**:需进行带符号的差值比较,增加逻辑延迟 **优化方案**: - **动态指针映射**:将虚拟地址$10 \sim 15$映射到$0 \sim 5$,形成“折叠”地址空间: ```verilog // RAM地址生成 assign waddr = (wptr_bin < 10) ? wptr_bin : wptr_bin - 10; ``` - **格雷码修正**:需重新设计格雷码转换规则,确保映射后的地址变化仍满足单比特跳变。 --- #### **7. 终极设计选择建议** | **方案** | **优点** | **缺点** | |------------------------|-----------------------------|------------------------------| | 虚拟扩展地址空间 | 兼容传统格雷码设计,可靠性高 | 地址利用率低 | | 自定义非2次幂格雷码 | 地址利用率100% | 需复杂的状态机控制,易出错 | | 混合二进制比较 | 无需格雷码同步 | 空满判断需跨时钟域比较,风险高 | **推荐选择**:在深度偏离2次幂较小时(如10 vs 16),优先使用**虚拟扩展地址空间**方案;若深度接近下一个2次幂(如14 vs 16),则推荐直接使用16深度
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值