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

跨时钟域设计实践:异步FIFO与握手协议的实现与验证指南

二牛学FPGA二牛学FPGA
技术分享
2天前
0
0
10

Quick Start(快速上手)

  1. 安装 Vivado 2023.1 以上版本,并新建一个 RTL 工程,目标器件选择 Xilinx Artix-7 XC7A35T。
  2. 创建顶层模块,例化一个异步 FIFO IP 核(FIFO Generator),配置深度为 16、数据位宽为 8、使用独立时钟 wr_clk 和 rd_clk。
  3. 编写 Testbench,生成两个不同频率的时钟:wr_clk = 100 MHz,rd_clk = 75 MHz,并产生写使能和读使能信号。
  4. 实现一个简单的握手协议模块:发送端产生 req 信号,接收端检测到 req 后拉高 ack,发送端检测到 ack 后拉低 req,完成一次数据传输。
  5. 运行行为仿真(behavioral simulation),观察 FIFO 的 empty、full、wr_ack 和 rd_ack 信号是否按预期变化。
  6. 执行综合(synthesize)并查看时序报告(timing summary),确保所有跨时钟路径(wr_clk → rd_clk)的 setup/hold 约束满足。
  7. 完成实现(implement)并生成比特流,下载到开发板(如 Nexys A7),通过逻辑分析仪(ILA)抓取握手信号和 FIFO 数据。
  8. 验收:确认握手协议的数据传输无丢失,FIFO 未出现 overflow/underflow,Fmax 满足设计要求。

前置条件与环境

项目推荐值替代方案
器件/板卡Xilinx Artix-7 XC7A35T (Nexys A7)Intel Cyclone IV / Lattice ECP5
EDA 版本Vivado 2023.1Quartus Prime 22.1 / Radiant 2022.2
仿真器Vivado Simulator (Xsim)ModelSim / Questa / Verilator
时钟/复位外部 100 MHz 晶振,板上复位按钮(低有效)PLL 分频生成多时钟
接口依赖UART 或 GPIO 用于数据观察ILA / VIO 核
约束文件XDC 文件需包含时钟定义、异步时钟组(set_clock_groups)SDC 文件(Intel)

说明:以上环境为推荐配置,替代方案在工具链命令和约束语法上略有差异,但设计原理完全一致。

目标与验收标准

本实战的目标是掌握跨时钟域(CDC)设计的两种经典方案:异步 FIFO 和握手协议。验收标准如下:

  1. 功能点:握手协议能可靠地传递单比特或多比特数据(每次传输一个字节),FIFO 能缓存多个数据并保证无丢失。
  2. 性能指标:FIFO 最大工作频率(Fmax)≥ 150 MHz(Artix-7 典型值),握手协议在 100 MHz 时钟下无亚稳态错误。
  3. 资源消耗:FIFO 使用 1 个 BRAM(深度 16),握手协议使用约 20 个 LUT + 10 个 FF。
  4. 验收方式:仿真波形中握手信号无毛刺、FIFO 的 empty/full 标志正确;上板后通过 ILA 抓取的数据与发送端一致。

实施步骤

1. 工程结构与模块划分

推荐工程结构如下(以 Vivado 为例):

src/
├── top.v                  # 顶层模块,例化 FIFO 和握手模块
├── fifo_wrapper.v         # 异步 FIFO IP 核的包装器
├── handshake_tx.v         # 发送端握手模块
├── handshake_rx.v         # 接收端握手模块
└── tb_top.v               # 测试平台
constraints/
└── top.xdc                # 时序约束

注意:FIFO IP 核使用独立时钟模式,写时钟域和读时钟域分别连接不同的时钟源。

2. 关键模块实现

握手协议(发送端)

module handshake_tx (
    input wire clk,
    input wire rst_n,
    input wire [7:0] data_in,
    input wire send_en,
    output reg req,
    input wire ack,
    output reg [7:0] data_out
);
    // 状态机:IDLE -> WAIT_ACK -> DONE
    localparam IDLE = 2'b00, WAIT_ACK = 2'b01, DONE = 2'b10;
    reg [1:0] state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= IDLE;
        else
            state <= next_state;
    end

    always @(*) begin
        next_state = state;
        case (state)
            IDLE: if (send_en) next_state = WAIT_ACK;
            WAIT_ACK: if (ack) next_state = DONE;
            DONE: next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            req <= 1'b0;
            data_out <= 8'b0;
        end else begin
            case (state)
                IDLE: if (send_en) begin req <= 1'b1; data_out <= data_in; end
                WAIT_ACK: ; // 等待 ack
                DONE: req <= 1'b0;
            endcase
        end
    end
endmodule

握手协议(接收端)

module handshake_rx (
    input wire clk,
    input wire rst_n,
    input wire req,
    output reg ack,
    output reg [7:0] data_out
);
    // 双触发器同步 req 信号
    reg req_sync1, req_sync2;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            req_sync1 <= 1'b0;
            req_sync2 <= 1'b0;
        end else begin
            req_sync1 <= req;
            req_sync2 <= req_sync1;
        end
    end

    // 状态机:IDLE -> WAIT_REQ_LOW
    localparam IDLE = 1'b0, WAIT_REQ_LOW = 1'b1;
    reg state, next_state;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= IDLE;
        else
            state <= next_state;
    end

    always @(*) begin
        next_state = state;
        case (state)
            IDLE: if (req_sync2) next_state = WAIT_REQ_LOW;
            WAIT_REQ_LOW: if (!req_sync2) next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            ack <= 1'b0;
            data_out <= 8'b0;
        end else begin
            case (state)
                IDLE: if (req_sync2) begin ack <= 1'b1; data_out <= 8'hA5; end // 示例数据
                WAIT_REQ_LOW: if (!req_sync2) ack <= 1'b0;
            endcase
        end
    end
endmodule

异步 FIFO 包装器

module fifo_wrapper (
    input wire wr_clk,
    input wire rd_clk,
    input wire rst_n,
    input wire [7:0] din,
    input wire wr_en,
    input wire rd_en,
    output wire [7:0] dout,
    output wire full,
    output wire empty
);
    // 例化 FIFO Generator IP 核(深度16,位宽8,独立时钟)
    fifo_generator_0 u_fifo (
        .wr_clk(wr_clk),
        .rd_clk(rd_clk),
        .rst(~rst_n),  // IP 核通常高有效复位
        .din(din),
        .wr_en(wr_en),
        .rd_en(rd_en),
        .dout(dout),
        .full(full),
        .empty(empty)
    );
endmodule

3. 时序约束

在 XDC 文件中定义时钟并设置异步时钟组,避免工具对跨时钟路径进行不必要的时序分析:

create_clock -name wr_clk -period 10.000 [get_ports wr_clk]
create_clock -name rd_clk -period 13.333 [get_ports rd_clk]
set_clock_groups -asynchronous -group [get_clocks wr_clk] -group [get_clocks rd_clk]

原因分析:如果不设置异步时钟组,时序分析工具会尝试分析 wr_clk 到 rd_clk 的路径,导致大量 false violation,掩盖真正的时序问题。

4. 仿真验证

编写 Testbench 时,注意以下要点:

  • 生成两个独立时钟,频率分别为 100 MHz 和 75 MHz,相位随机。
  • 模拟写满和读空的边界条件:连续写入 16 个数据后观察 full 信号,连续读取 16 个数据后观察 empty 信号。
  • 握手协议测试:发送端随机间隔发送数据,接收端随机间隔应答,验证数据完整性。

仿真波形应观察到:FIFO 的 wr_ack 在写入数据后拉高,rd_ack 在读取数据后拉高;握手协议的 req 和 ack 信号无毛刺、无亚稳态。

5. 上板调试

使用 ILA(集成逻辑分析仪)抓取关键信号:

  • 设置触发条件:例如 wr_en 上升沿或 rd_en 上升沿。
  • 抓取深度建议 1024,确保能捕获多次传输。
  • 对比仿真与上板结果,确认一致性。

风险边界:上板后若发现数据丢失,首先检查复位同步是否在每个时钟域内独立实现;其次确认 FIFO 深度是否满足最差情况下的数据缓存需求。

验证结果

通过仿真和上板验证,预期结果如下:

  • 握手协议在 100 MHz 时钟下无亚稳态错误,数据传输正确率 100%。
  • FIFO 在 100 MHz 写入、75 MHz 读取时,无 overflow/underflow 发生。
  • FIFO 的 Fmax 达到 150 MHz 以上(Artix-7 典型值)。

排障指南

  • 问题:FIFO 出现 overflow → 检查写使能信号是否在 full 为高时仍然有效;增加 FIFO 深度或降低写入速率。
  • 问题:握手协议数据丢失 → 确认接收端 req 同步使用了双触发器;检查 ack 信号是否在发送端正确同步。
  • 问题:综合时序不满足 → 确认 XDC 中设置了异步时钟组;检查跨时钟路径是否被误约束。

扩展与进阶

  • 深度扩展:将 FIFO 深度扩展到 256 或 1024,测试不同深度下的资源消耗和 Fmax。
  • 性能优化:在握手协议中引入流水线,提高吞吐量。
  • 多比特 CDC:使用格雷码指针的异步 FIFO 处理多比特数据跨时钟域传输。

参考与附录

  • Xilinx UG172: FIFO Generator v13.2 Product Guide
  • Clifford E. Cummings, "Simulation and Synthesis Techniques for Asynchronous FIFO Design"
  • 附录 A:完整 Testbench 代码(略)
  • 附录 B:ILA 配置步骤(略)
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/40471.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
91919.27W3.99W3.67W
分享:
成电国芯FPGA赛事课即将上线
基于FPGA的边缘推理低功耗设计指南:从原理到实现
基于FPGA的边缘推理低功耗设计指南:从原理到实现上一篇
FPGA状态机编码方式对比设计指南:二进制、格雷码与独热码下一篇
FPGA状态机编码方式对比设计指南:二进制、格雷码与独热码
相关文章
总数:944
SystemVerilog FPGA验证实践指南:从接口封装到覆盖率收集

SystemVerilog FPGA验证实践指南:从接口封装到覆盖率收集

本文旨在为FPGA开发者提供一份关于SystemVerilog(SV)在…
技术分享
16天前
0
0
33
0
成电少年学“不忘初芯 砥砺前行”四周年庆活动预告 | 抢鲜Get终极快乐

成电少年学“不忘初芯 砥砺前行”四周年庆活动预告 | 抢鲜Get终极快乐

2018年11月18日成电少年学在电子科技大学广东电子信息工程研究院成立…
技术分享
3年前
2
1
844
0
SystemVerilog for FPGA:面向对象编程在验证中的高效应用

SystemVerilog for FPGA:面向对象编程在验证中的高效应用

在当今复杂的FPGA与ASIC设计项目中,验证工作占据了超过70%的开发…
技术分享
27天前
0
0
50
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容