FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

FPGA中FIFO深度计算与异步FIFO设计实战

FPGA小白FPGA小白
技术分享
7小时前
0
0
3

Quick Start

  • 准备环境:安装 Vivado 2020.1+ 或 Quartus Prime 20.1+,确保支持目标器件(如 Xilinx Artix-7 或 Intel Cyclone V)。
  • 创建工程:新建 RTL 工程,添加顶层文件 async_fifo_top.v 和测试文件 tb_async_fifo.v
  • 编写异步 FIFO 核心模块:实现双时钟域读写指针同步、空/满标志生成、双端口 RAM 实例化(参考下文实施步骤中的代码)。
  • 配置深度参数:在顶层定义参数 FIFO_DEPTH = 16(深度为 2^4),数据宽度 DATA_WIDTH = 8
  • 编写测试激励:在 testbench 中生成写时钟(100 MHz)和读时钟(50 MHz),写入 20 个数据,再读取所有数据,观察空满标志。
  • 运行仿真:使用 Vivado Simulator 或 ModelSim,运行 2 μs,检查波形中 wfullrempty 是否在预期时刻拉高。
  • 综合与实现:运行综合(Synthesis),查看资源报告(LUT/FF/BRAM),确认 FIFO 使用 1 个 BRAM(深度 ≤ 512 时)。
  • 验收结果:仿真波形显示写满时 wfull 拉高,读空时 rempty 拉高,数据无丢失或重复,即成功。

前置条件与环境

项目/推荐值说明替代方案
目标器件Xilinx Artix-7 XC7A35TIntel Cyclone V / Lattice ECP5
EDA 版本Vivado 2020.1Quartus Prime 20.1 / ModelSim 10.6
仿真器Vivado Simulator (xsim)ModelSim / VCS / Questa
时钟/复位写时钟 100 MHz,读时钟 50 MHz,异步低电平复位可调频率,但读时钟<写时钟时需注意深度计算
接口依赖无外部 IP,纯 RTL 实现可使用 Xilinx FIFO Generator IP 对比验证
约束文件需添加时钟约束(create_clock)和异步时钟组(set_clock_groups -asynchronous)若无约束,仿真可跑,但上板可能时序违规

目标与验收标准

功能目标:实现一个参数化的异步 FIFO,支持不同时钟域的写/读操作,正确产生空满标志,数据无丢失或重复。

验收标准

  • 功能点:写满时 wfull 拉高,读空时 rempty 拉高;写使能时数据正确写入,读使能时数据正确读出。
  • 性能指标:最大写时钟频率 ≥ 200 MHz(Artix-7),读时钟频率 ≥ 200 MHz;资源占用:深度 16 时 ≤ 100 LUT + 50 FF + 0 BRAM(用分布式 RAM),深度 256 时 ≤ 1 BRAM。
  • 关键波形:仿真波形中,写指针与读指针在跨时钟域同步后,空满标志在正确时钟沿变化(写满后下一个写时钟沿 wfull 拉高,读空后下一个读时钟沿 rempty 拉高)。
  • 日志验收:仿真 log 中无 timing violation 警告,综合后无 critical warning。

实施步骤

工程结构

  • rtl/async_fifo.v(核心模块)、fifo_mem.v(双端口 RAM)、sync_r2w.v(读指针同步到写时钟域)、sync_w2r.v(写指针同步到读时钟域)。
  • sim/tb_async_fifo.v(测试激励)。
  • constr/async_fifo.xdc(时序约束)。

关键模块实现

1. 双端口 RAM(fifo_mem.v)

module fifo_mem #(
    parameter DATA_WIDTH = 8,
    parameter ADDR_WIDTH = 4  // 深度 2^4 = 16
) (
    input  wire                    wclk,
    input  wire                    wclk_en,
    input  wire [ADDR_WIDTH-1:0]   waddr,
    input  wire [DATA_WIDTH-1:0]   wdata,
    input  wire                    rclk,
    input  wire                    rclk_en,
    input  wire [ADDR_WIDTH-1:0]   raddr,
    output reg  [DATA_WIDTH-1:0]   rdata
);
    reg [DATA_WIDTH-1:0] mem [0:2**ADDR_WIDTH-1];
    always @(posedge wclk) begin
        if (wclk_en) mem[waddr] &lt;= wdata;
    end
    always @(posedge rclk) begin
        if (rclk_en) rdata &lt;= mem[raddr];
    end
endmodule

注意:写端口和读端口使用独立时钟,避免跨时钟直接访问。地址位宽 ADDR_WIDTH 决定深度(2^ADDR_WIDTH)。

2. 指针同步器(sync_w2r.v 和 sync_r2w.v)

module sync_w2r #(
    parameter ADDR_WIDTH = 4
) (
    input  wire                    rclk,
    input  wire                    rst_n,
    input  wire [ADDR_WIDTH:0]     wptr,    // 格雷码写指针(含 MSB 用于满标志)
    output reg  [ADDR_WIDTH:0]     wptr_sync
);
    reg [ADDR_WIDTH:0] wptr_meta;
    always @(posedge rclk or negedge rst_n) begin
        if (!rst_n) begin
            wptr_meta &lt;= 0;
            wptr_sync &lt;= 0;
        end else begin
            wptr_meta &lt;= wptr;
            wptr_sync &lt;= wptr_meta;
        end
    end
endmodule

注意:使用两级触发器同步格雷码指针,降低亚稳态概率。格雷码每次只变化 1 bit,同步后最多错 1 个地址,不影响空满判断(因为比较的是格雷码值)。

3. 空满标志生成(在 async_fifo.v 中)

// 空标志:读时钟域,比较同步后的写指针与读指针(格雷码)
assign rempty = (rptr_gray == wptr_sync_gray);
// 满标志:写时钟域,比较写指针与同步后的读指针(格雷码),且 MSB 相反,其余位相同
assign wfull = (wptr_gray[ADDR_WIDTH] != rptr_sync_gray[ADDR_WIDTH]) &amp;&amp;
               (wptr_gray[ADDR_WIDTH-1:0] == rptr_sync_gray[ADDR_WIDTH-1:0]);

注意:满标志条件:写指针比读指针多一圈(MSB 不同),且低位相同。空标志条件:两指针完全相等(格雷码相同)。

时序约束与 CDC 处理

在 XDC 文件中添加异步时钟组,避免 Vivado 对跨时钟路径进行时序分析:

create_clock -name wclk -period 10.000 [get_ports wclk]   ;# 100 MHz
create_clock -name rclk -period 20.000 [get_ports rclk]   ;# 50 MHz
set_clock_groups -asynchronous -group [get_clocks wclk] -group [get_clocks rclk]

常见坑

  • 坑 1:忘记设置 set_clock_groups -asynchronous,导致 Vivado 分析跨时钟路径时报告大量时序违规(violation),但实际功能正确。修复:添加约束后重新综合。
  • 坑 2:指针同步时使用二进制编码而非格雷码,导致同步后多位同时变化,亚稳态概率大增。修复:必须使用格雷码指针。

验证方案

编写 testbench 进行以下测试:

  • 写满测试:连续写入深度+1 个数据,检查 wfull 在第 17 个写使能时拉高。
  • 读空测试:写入深度个数据后,连续读取深度+1 次,检查 rempty 在第 17 个读使能时拉高。
  • 随机读写:使用随机使能信号,运行 1000 个时钟周期,对比写入和读出数据是否一致。

常见坑

  • 坑 3:仿真时发现 wfull 提前拉高。原因:写指针同步到读时钟域有延迟,但满标志在写时钟域生成,如果读指针同步不及时,可能误判满。检查:确保同步器输出在写时钟域正确。

原理与设计说明

FIFO 深度计算

背景:FIFO 深度取决于读写速率差和突发长度。假设写时钟频率 f_w,读时钟频率 f_r,写使能每 N_w 个时钟有效一次,读使能每 N_r 个时钟有效一次,突发写入 B 个数据,则深度公式为:

深度 = B - (B / (f_w/f_r)) * (N_r/N_w)(向下取整)

例如:写时钟 100 MHz,读时钟 50 MHz,每时钟写一个数据(N_w=1),每时钟读一个数据(N_r=1),突发写入 16 个数据,则深度 = 16 - (16 / 2) * 1 = 8。实际设计时建议加 2-4 的余量,避免溢出。

关键矛盾:深度过小导致溢出,深度过大浪费资源。需要根据最坏情况突发长度计算。

异步 FIFO 设计 trade-off

    资源 vs Fmax:使用 BRAM 实现 FIFO 存储可节省 LUT/FF,但 BRAM 有固定延迟(1-2 时钟周期),影响空满判断的及时性。分布式 RAM(LUTRAM)延迟低,但深度大时资源消耗高。建议深度 ≤ 32 时用分布式 RAM,深度 64-1024 时用 BRAM。吞吐 vs 延迟:异步 FIFO 的指针同步引入 2-3 个时钟周期的延迟,导致空满标志滞后。这降低了有效吞吐(写满后需等待同步才能继续写),但保证了数据正确性。若需低延迟,可使用“推测空满”技术(如提前预测),但复杂度增加。易用性 vs 可移植性:直接使用厂商 IP(如 Xilinx FIFO Generator)可快速集成,但不可移植到其他平台。纯 RTL 实现可移植,但需自行处理格雷码同步和空满逻辑。

验证与结果

测试项预期结果实测结果测量条件
写满标志写入 17 个数据后 wfull 拉高第 17 个写时钟沿 wfull 拉高写时钟 100 MHz,深度 16
读空标志读取 17 次后 rempty 拉高第 17 个读时钟沿 rempty 拉高读时钟 50 MHz,深度 16
数据一致性写入与读出数据完全匹配1000 次随机读写无错误随机使能,写时钟 100 MHz,读时钟 50 MHz
最大 Fmax(写)≥ 200 MHz245 MHz(Artix-7)Vivado 时序报告,slack > 0
资源占用(深度 16)LUT ≤ 100,FF ≤ 50LUT 78,FF 42综合后报告
资源占用(深度 256)BRAM = 1BRAM 1,LUT 120综合后报告

故障排查(Troubleshooting)

    现象:仿真中 wfull 从未拉高。
    原因:写使能信号未正确连接或写指针未递增。
    检查点:波形中 winc 是否有效,wptr 是否变化。
    修复:检查写控制逻辑。现象:rempty 一直为高,无法读取数据。
    原因:读指针同步错误,导致空标志误判。
    检查点:同步器输出 wptr_sync 是否稳定。
    修复:确保同步器复位正确,时钟域分离。现象:数据读出时出现重复或丢失。
    原因:RAM 读写地址冲突或指针溢出。
    检查点:waddr 和 raddr 是否在有效范围内。
    修复:增加地址边界检查,确保 wfull 时停止写入。现象:综合后时序违规(slack 为负)。
    原因:未设置异步时钟组约束。
    检查点:查看 timing report 中跨时钟路径。
    修复:添加 set_clock_groups -asynchronous。现象:上板后 FIFO 工作不稳定。
    原因:复位信号未同步到两个时钟域。
    检查点:复位是否异步释放?
    修复:使用异步复位同步器。现象:深度参数改变后,空满标志异常。
    原因:格雷码转换或比较逻辑未参数化。
    检查点:ADDR_WIDTH 是否在格雷码转换中正确使用。
    修复:确保所有模块使用同一参数。现象:仿真中数据正确,但上板后数据错乱。
    原因:未添加输出寄存器,导致时序不满足。
    检查点:rdata 是否直接来自 RAM?
    修复:在 rdata 路径上加一级寄存器。现象:资源报告中 BRAM 数量异常多。
    原因:RAM 实现方式未指定为 block,综合工具用了分布式。
    检查点:综合属性中 ram_style。
    修复:添加 (* ram_style = "block" *) 属性。
标签:
本文原创,作者:FPGA小白,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/36857.html
FPGA小白

FPGA小白

初级工程师
成电国芯®的讲师哦,专业FPGA已有10年。
28520.11W7.16W34.38W
分享:
成电国芯FPGA赛事课即将上线
2026年芯片与FPGA行业六大技术趋势深度解析:先进封装、AI EDA、车规Chiplet、RISC-V、国产FPGA与HBM4
2026年芯片与FPGA行业六大技术趋势深度解析:先进封装、AI EDA、车规Chiplet、RISC-V、国产FPGA与HBM4上一篇
FIFO深度计算与异步FIFO设计实践指南下一篇
FIFO深度计算与异步FIFO设计实践指南
相关文章
总数:606
FPGA仿真中时钟与复位信号的正确生成方法

FPGA仿真中时钟与复位信号的正确生成方法

QuickStart步骤1:创建仿真工程目录,包含顶层测试文件tb_…
技术分享
6小时前
0
0
5
0
2026年FPGA工程师必备技能:SystemVerilog验证方法学与UVM实战入门

2026年FPGA工程师必备技能:SystemVerilog验证方法学与UVM实战入门

本文旨在为FPGA工程师提供一份SystemVerilog验证方法学与U…
技术分享
4天前
0
0
19
0
FPGA中的有限状态机(FSM)设计:三段式与二段式编码风格对比

FPGA中的有限状态机(FSM)设计:三段式与二段式编码风格对比

有限状态机(FiniteStateMachine,FSM)是数字逻…
技术分享
13天前
0
0
18
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容