
新板卡回来第一件事别急着写业务逻辑先验证高速串行链路本身能不能跑通。我做过好几个带光纤口的 FPGA 项目最常用的开板手段就是做 Aurora 回环测试FPGA 内部发数据通过光模块发出去再绕回接收端比对收发数据是否一致。这一步能把 PCB 布线、GTX 高速收发器配置、参考时钟、光模块选型全部串起来验证链路干净了后面做 Aurora 协议通信、上层业务逻辑才有底。本文以 Xilinx Artix-7 系列 FPGA 为例使用 Aurora 8B/10B IP核按照从 IP 配置到上板实测的完整流程带你搭一个最小可用的光纤回环测试工程。适合刚接触 FPGA 高速接口的开发者和准备调光口但还没想好怎么下手的同学也适合想快速排查板级链路问题的老手做个参考。1. 为什么回环测试要用 Aurora而不是自己写高速收发逻辑1.1 回环测试到底在验证什么回环测试这个动作听起来简单就是把发送和接收短接起来看数据对不对。但放在光纤通信场景里它验证的层面远比“数据对不对”要多板级信号完整性GTX 差分引脚到光模块座之间的 PCB 走线、过孔、阻抗连续性是否正常。光模块与光纤链路光模块本身是否正常工作发射和接收光功率是否在合理范围。时钟方案是否正确GTX 参考时钟是否稳定、频率是否符合配置用户逻辑时钟是否和 GT 域对齐。IP 配置是否有效Aurora 的复位时序、通道初始化、8B/10B 编解码链路是否正常。所以回环测试是板级硬件调试和 FPGA 逻辑调试交汇处的一项工作。直接硬啃复杂协议头前先把这条最基础的链路打通能省下大量排查时间。1.2 Aurora IP核 vs 自研 GT 收发逻辑很多 FPGA 工程师上手高速接口时会冒出“自己写一个高速收发逻辑”的念头。我早期也这么干过但踩了一圈坑之后结论非常明确除非公司有专门的 GT 底层专家否则不要自研。原因很简单GT 高速收发器本质是一个混合信号模块包含 PLL、CDR、串并转换、8B/10B 编解码、通道绑定、时钟补偿等多个子模块。在 Xilinx 的文档体系里光 GT 相关的原语和属性就够看很久全部自己把控的难度和风险极高。Aurora 8B/10B 是 Xilinx 提供的免费 IP官方做好的东西不用非要自己造轮子性价比很低。官方 IP 有一整套初始化、复位、错误处理机制例化后直接给 AXI4-Stream 接口业务逻辑只面对数据通路极大降低上层设计复杂度。1.3 和误码仪、IBERT 方案怎么配合做回环测试时有些人会推荐直接用 IBERT也就是 Xilinx 的集成误码率测试工具。IBERT 也很强它能直接通过 JTAG 配置 GT 并测试误码率基本不写 HDL。但 IBERT 测的是物理层不会经过 Aurora 协议层。实际项目里光模块的链路和 Aurora 的复位、通道初始化逻辑是紧密耦合的IBERT 跑通了不代表 Aurora 能正常建立链路。所以我的习惯是先用 IBERT 快速筛查硬件问题确认 GT 的收发通道本身没问题。再用 Aurora 回环工程做协议层验证确认 IP 配置和用户逻辑无误。这篇文章讲的就是第二步也就是 Aurora 回环工程的做法。2. 配置 Aurora IP核之前先啃下这四个概念2.1 8B/10B 编码与线路速率的关系Aurora 8B/10B 协议基于 8B/10B 编码每个字节在传输时被编码成 10 bit因此线路速率 有效数据速率 × 1.25。这个 20% 的开销必须从一开始就看清否则后面计算用户时钟很容易出错。比如线路速率配置为 5Gbps那么有效数据速率就是 4Gbps。如果数据位宽选择 32 bit也就是 4 字节并行用户时钟频率 4Gbps / 32 bit 125MHz。如果线路速率配的是 6.6Gbps32 bit 位宽下的用户时钟就是 6.6G / 40 165MHz。实际工程里我比较喜欢选“5Gbps 线路速率 125MHz 参考时钟 32 bit 数据位宽”这个组合。原因后面细说核心是用户时钟恰好也是 125MHz省了不少跨时钟域处理的麻烦。2.2 成帧模式与流模式Aurora 8B/10B IP核支持两种接口模式Framing 和 Streaming。Framing 模式把数据组织成帧帧之间有专门的帧标记适合需要以帧为单位的通信场景。用户需要处理帧头、帧尾、帧间隔逻辑相对复杂一些。Streaming 模式更像一个管道只要 tvalid 和 tready 握手成功数据就持续往里灌没有帧的概念非常适合回环测试这种纯数据流验证。做回环测试我建议直接选 Streaming 模式。它把协议层的复杂性降到最低我们只需要关心数据通路的正确性。等以后做真正的业务通信再根据协议需求切换成 Framing 模式不迟。2.3 通道绑定与多通道概念Aurora 协议支持把多条 GT 通道绑定成一个逻辑通道实现更高的数据吞吐量。比如 4 个通道绑定后128 bit 并行数据宽度。回环测试通常只需要一个通道因为我们要验证的最小单元是一个光模块对应一个 GT 通道。先把单通道打通再加多通道才有意义。否则单通道链路都建立不起来多通道绑定之后的通道对齐和偏斜补偿会让你更头疼。2.4 时钟补偿机制光纤通信两端的参考时钟不可能完全同频一定存在微小偏差。Aurora 8B/10B 协议通过周期性插入时钟补偿序列来解决这个问题。理解这一点对调试有实际意义如果你在抓波形时发现数据流中偶尔穿插了一些看起来不像业务数据的字节那不是错误而是时钟补偿序列。在做数据比对时需要把这段时间的数据屏蔽掉否则会出现误报。Aurora IP核在用户接口层已经处理了这些细节但我们要知道它的存在避免在调试时对着数据流怀疑人生。3. Aurora 8B/10B IP核配置实操每个选项都要知道后果3.1 新建 IP 核与接口模式选择在 Vivado 的 IP Catalog 里搜索 Aurora 8B/10B双击打开配置界面。第一页主要选择协议版本和接口模式。Protocol Version 一般选最新即可但要注意板卡上另一端的设备是否支持对应版本。回环测试时收发两端都是同一个 IP核不存在兼容问题直接选默认版本。Interface 选择 Streaming前面已经说过原因。右侧会自动显示数据位宽选项这一步同时也会显示预估的用户时钟频率方便你反推线路速率配置。3.2 线路速率、参考时钟与数据位宽的联动关系Line Rate 和 Ref Clock 是两个强关联的参数。GTX 的参考时钟频率必须满足其内部 PLL 的分频要求。Xilinx GTX 的参考时钟一般是线速率除以某个整数常见配置如下线路速率推荐参考时钟数据位宽用户时钟1.25Gbps125MHz16 bit62.5MHz2.5Gbps125MHz16 bit125MHz3.125Gbps156.25MHz32 bit78.125MHz5Gbps125MHz32 bit125MHz6.6Gbps165MHz32 bit165MHz注意参考时钟并一定只能用表里推荐的值但必须满足 GTX 的 PLL 计算规则。如果你不确定就用 IP 配置界面里的 Validate 按钮检查Vivado 会直接告诉你这个组合能不能用。为什么我偏爱 5Gbps 125MHz 参考时钟 32 bit 位宽因为这个组合下用户时钟、参考时钟都是 125MHz。125MHz 是最常见的板载时钟频率很多开发板出厂就带一个 125MHz 有源晶振连改板都不用。而且用户逻辑工作在 125MHz和以太网、DDR、PCIe 等常见 IP 的时钟域天然接近后续集成不用再绕一大圈跨时钟域。3.3 GT 参考时钟引脚指定配置页里会让你选择 GT RefClk 的引脚位置。这里必须查板卡的原理图看 GT 参考时钟接到了哪个 Bank、哪个引脚。这里有个常见的坑GTX 的参考时钟不一定和光模块所在的 Bank 相同它可能来自专门的参考时钟输入引脚。如果配置错了IP核综合布线时就会报错甚至布线成功后链路也起不来。Artix-7 上常见的做法是光模块放在 Bank 216 或 Bank 217 附近参考时钟用板载 125MHz 差分晶振连接到对应 Bank 的 GTREFCLK 引脚。具体以你自己的板卡原理图为准别想当然。3.4 流控、时钟补偿与 CRC配置页还有几个次要选项回环测试阶段建议保持默认或者关闭Flow Control回环测试不需要流控关闭即可。Clock Compensation保持使能这是协议正常工作所必需的。CRC回环测试可以关掉。开了 CRC 会多一些校验逻辑但流模式下对数据通路没有实质帮助反而增加出错时排查的复杂度。LFSRIP核内部自带的回环测试模式后面我会专门提到但不是我们这次的主线。3.5 生成 IP核后要关注哪些文件点击 Generate 之后Vivado 会输出一堆文件。用不到全部文件但至少要知道这几个aurora_8b10b.xciIP核配置文件重新生成工程时靠它恢复配置。aurora_8b10b.v / .vhdIP核的顶层封装只暴露 AXI4-Stream 接口和复位时钟等引脚。aurora_8b10b_exdes.v官方示例设计顶层这是最重要的参考模板后面我们改工程就从这个文件入手。aurora_8b10b_gt_frame.v / aurora_8b10b_gt_stream.vGT 层的例化如果要做 GT 层调试需要打开它看一眼。4. 回环工程搭建基于 example design 改出自己的测试逻辑4.1 为什么不从空白工程开始每次提到“从 example design 改起”都会有人问为什么不能自己写一个干净的顶层。原因很简单Aurora IP核的复位、时钟初始化、GT 例化之间的连接关系非常繁琐自己写需要花大量时间而且容易漏掉细节。比如 GT 的 txresetdone 和 rxresetdone 信号必须先稳定Aurora 的 reset 释放时序才正确这些知识不在正规文档里翻半天根本发现不了。所以强烈建议直接从 example design 开始先把官方工程跑通再逐步替换成自己的逻辑。这既是最高效的做法也是官方推荐的做法。4.2 example design 顶层结构解析生成 IP核后在 IP Sources 里找到 example design 文件 aurora_8b10b_exdes.v展开后大概包含这些模块aurora_8b10b_init复位和初始化控制负责产生 reset 信号并等待 GT 复位完成。aurora_8b10b_support核心例化层包含 GT 实例、时钟模块、状态机。顶层用户逻辑默认是一个计数器产生递增数据流把数据发到 TX 接口同时把 RX 接口的数据接收后通过一个 FIFO 回环到 TX。example design 默认的逻辑其实已经是一个回环测试了内部把 RX 收到的数据直接送回到 TX 去发送。但这有一个问题默认例子里数据是直接从 RX 返回 TX没有做数据比对和误码统计。我们需要改出一版带有数据比对功能的用户逻辑。4.3 自写用户逻辑递增码产生与误码统计我习惯把 example design 里的用户逻辑整体替换成一个专门写的模块名字叫 loopback_test_top。这个模块做三件事产生一个递增的 32 bit 数据序列作为发送源。将 Aurora RX 接口收到的数据与期望值比对统计错误字节数。通过 LED 或串口输出链路状态和误码计数。核心代码如下你可以直接参考module loopback_test_top ( input wire user_clk, input wire user_rstn, input wire channel_up, input wire lane_up, output reg [31:0] s_axi_tx_tdata, output reg s_axi_tx_tvalid, input wire s_axi_tx_tready, input wire [31:0] m_axi_rx_tdata, input wire m_axi_rx_tvalid, input wire m_axi_rx_tlast, output reg [31:0] error_count, output reg [31:0] frame_count, output reg test_done ); reg [31:0] tx_counter; reg [31:0] rx_expect; assign s_axi_tx_tdata {8hA5, 8h5A, tx_counter[15:0]}; // 发送端持续发送递增数据 always (posedge user_clk or negedge user_rstn) begin if (!user_rstn) begin tx_counter 32d0; s_axi_tx_tvalid 1b0; end else if (channel_up) begin s_axi_tx_tvalid 1b1; if (s_axi_tx_tvalid s_axi_tx_tready) begin tx_counter tx_counter 32d1; end end else begin s_axi_tx_tvalid 1b0; end end // 接收端与期望值比对 always (posedge user_clk or negedge user_rstn) begin if (!user_rstn) begin error_count 32d0; frame_count 32d0; rx_expect 32d0; test_done 1b0; end else begin if (m_axi_rx_tvalid channel_up) begin if (m_axi_rx_tdata[15:0] ! rx_expect[15:0]) begin error_count error_count 32d1; end frame_count frame_count 32d1; rx_expect rx_expect 32d1; end if (error_count 32d1000) begin test_done 1b1; end end end endmodule这段代码有几个细节要说明发送数据前插入了固定字节 A5_5A这样在调试时能快速从波形上找到数据边界。接收比对只比对了低 16 位也就是计数器的内容高 16 位是固定头字节不需要比对。error_count 作为只增不减的计数器超过阈值后拉高 test_done方便在调试时用逻辑分析仪抓取错误现场。当然上面的代码是流模式下的简化版本。如果打开了 tlast 信号需要额外处理帧边界逻辑但回环测试中我们用 Streaming 模式不需要考虑帧边界。4.4 替换 example design 中的用户逻辑打开 example design 的顶层文件找到类似下面这样的代码段aurora_8b10b_example_design #( ... ) u_example_design ( ... );把这部分替换成你的 loopback_test_top 实例化并把 Aurora 的 user_clk、user_rstn、channel_up、lane_up 等信号接到新模块上。注意user_rstn 不要直接用外部复位建议用 example design 里经过同步处理的复位信号。Aurora 对复位时序要求严格异步复位可能导致 GT 初始化异常。channel_up 拉高前不要向 TX 接口发送任何数据。一定要把它作为发送使能的条件之一。4.5 引脚约束不是随便定的工程要跑到板子上必须正确约束引脚。最少需要以下几组系统时钟板级 50MHz 或 100MHz用于 MMCM 产生 GT 参考时钟和用户时钟。GT 参考时钟差分时钟引脚命名类似 gt_refclk_p、gt_refclk_n。SFP 收发差分对gt_rxp、gt_rxn、gt_txp、gt_txn。板级指示灯引脚用于观察 channel_up、lane_up 和错误状态。约束文件里最容易被忽略的是时钟约束。GT 参考时钟和用户时钟虽然理论上同源但在 Vivado 中必须显式声明它们的时钟关系否则时序分析根本不过。一个常用的约束片段create_clock -name sys_clk -period 10.000 [get_ports sys_clk_p] create_clock -name gt_refclk -period 8.000 [get_ports gt_refclk_p] set_clock_groups -asynchronous -group [get_clocks sys_clk] -group [get_clocks gt_refclk]这里的 period 根据实际晶振频率调整。125MHz 差分时钟对应 period 8ns。如果还有用户时钟也应该与 GT 参考时钟之间做好异步时钟域划分避免跨时钟时序报告满天飞。5. 上板实测从加载比特流到看懂链路状态5.1 连接硬件与加载比特流回环测试的物理连接相当简单把一根光纤模块插入 SFP 座另一端插回同一个模块的 RX 口。没错就是用一根光纤把模块的 TX 和 RX 短接起来也叫做外部回环。如果用的是 SFP 光模块方向别接反了。光模块的 TX 口是发射端要用光纤跳线连接到另一个光模块的 RX 口。同一个模块的外回环需要一根专门的光纤跳线把相邻的 TX 和 RX 连起来或者直接把模块的 TX、RX 用光纤转接头短接。不同光模块类型连接方式有差异动手前先看模块资料。接好之后呢下载比特流。观察以下几个信号lane_up只要 GT 层完成了初始化lane_up 就会拉高说明物理层已经准备好。channel_up在单通道配置下channel_up 是 Aurora 协议层初始化完成的标志通常比 lane_up 晚一点。channel_up 拉高后用户逻辑的 AXI4-Stream 接口才能收发数据。把这两个信号分别点亮一个 LED调试时一目了然。5.2 用近端回环快速拆分问题上板后如果 channel_up 一直不拉高别急着怀疑协议先做一次近端回环测试。所谓近端回环是指在 GT 内部直接把发送端的串行数据绕回接收端。它不经过光模块和光纤物理链路只验证 FPGA 内部 GT 通路。Aurora example design 顶层预留了 LOOPBACK 端口通常映射为 3 bit 信号常见的值如下LOOPBACK 值含义3b000正常模式数据经过光模块3b010近端 PMA 回环GT 内部收发短接3b100远端 PMA 回环一般在接收端测试使用把 LOOPBACK 设为 3b010也就是近端回环。如果此时 lane_up 能拉高、数据比对无误码说明 FPGA 的 GT 通路和 Aurora 配置没问题问题大概率出在光模块或外部物理链路上。反之如果近端回环都起不来问题就在 FPGA 内部逻辑配置或硬件本身。5.3 数据比对的判定标准近端回环跑通后切回正常模式做外回环。此时观察误码计数和发送计数。一个好的回环测试应该满足channel_up 在复位释放后几百毫秒内拉高并且稳定保持。error_count 持续为 0。如果 error_count 缓慢增长或者瞬间飙升说明链路质量有问题。持续运行 24 小时以上无误码。这个标准虽然保守但对验证工业级光纤链路来说很有必要。有些干扰问题并不是立刻出现的。板卡发热后电源纹波变大或者光纤接头松动造成光功率漂移都需要长时间运行才能暴露出来。5.4 用 ILA 抓数据流定位异常位置如果误码统计不为 0直接用 ILA 观察 Aurora 的用户接口信号。把 s_axi_tx_tdata、s_axi_tx_tvalid、s_axi_tx_tready、channel_up、error_count 都抓下来。调试中我遇到过一个很隐蔽的问题数据错误总是在某一个固定位置出现看起来像周期性错误。查了好久才发现是 GT 参考时钟的频谱纯净度不够参考时钟引脚旁边的一颗开关电源频率刚好造成了干扰。后来在 PCB 上给参考时钟引脚加了滤波电容问题消失。所以 ILA 抓波形不仅仅要看数据对不对还要关注错误的分布规律是均匀散布还是集中在某个特定区域这往往对应着不同的问题根源。6. 实测中常见的坑与排查链路6.1 channel_up 拉不起来的完整排查链路这是最经典的问题没有之一。channel_up 不拉高说明 Aurora 协议握手没有完成。排查步骤按这个顺序来确认 GT 参考时钟引脚约束正确。用 ILA 抓 GT 的 txoutclk 或 rxoutclk如果有时钟输出说明参考时钟基本正常。确认复位释放时序正确。Aurora IP核要求 GT 的 txresetdone 和 rxresetdone 稳定拉高一段时间后user_reset 才能释放。example design 里已经处理好了但如果你自己改了复位逻辑很容易踩这个坑。检查 lane_up 是否拉高。lane_up 是 GT 层对齐完成的标志如果 lane_up 都没有说明问题在物理层或 GT 配置。此时回环类型、参考时钟频率、GTX 电源这些都是怀疑对象。如果 lane_up 正常但 channel_up 不拉高问题多半在协议层也就是 Aurora 状态机没有成功握手。常见原因是两端协议版本不一致或者链路速率不匹配。6.2 数据比对全错或数据乱序的处理channel_up 正常但数据比对大量错误。这不是链路不稳定而是数据通路有逻辑问题。先看发送端发送的数据字节序是否与接收端一致。Aurora IP核里有个配置和 GT 的字节顺序有关如果发送端和接收端配置的 Endianness 不一样接收端看到的数据会乱掉。再看时钟域接收数据出现在 user_clk 域吗Aurora IP核的输出接口已经同步到 user_clk 域了但如果你在用户逻辑里又做了一次跨时钟处理容易把时序搞乱。最后看复位释放后的状态channel_up 拉高之前RX 数据接口可能来一些无效数据如果在 channel_up 之前就执行数据比对会得到一堆假错误。务必在比对逻辑里加入 channel_up 条件。6.3 偶发误码但抓不到规律偶发误码是最难处理的问题因为它在时间上不可预测。可能跑一小时出现一个错误也可能一天都正常。这一类问题往往和信号完整性有关而不是逻辑问题。可以按以下优先级排查检查光模块的光功率是否在接收灵敏度范围内。用光功率计测一下或者在模块诊断寄存器里读光功率寄存器。如果光功率偏低先清洁光纤接头再检查光纤弯折半径。确认参考时钟的抖动是否满足要求。GTX 参考时钟对抖动很敏感如果用普通的时钟发生器最好用频谱仪看一下相位噪声。如果测试条件不允许可以尝试更换一个低抖动的时钟源做对照实验。检查板级电源。GTX 收发器对模拟电源的噪声容忍度很低如果供电纹波偏大偶发误码几乎是必然的。这类问题的处理思路是先隔离变量再逐项排除不能拿着逻辑分析仪乱抓。6.4 SFP 模块兼容性问题用第三方 SFP 模块时经常遇到和官方模块行为不一致的情况。这不是模块坏了而是 SFP 模块内部的 CDR、差分摆幅、均衡策略各不相同。Aurora IP核侧可以通过修改 GT 的 RX 均衡参数来适配不同模块。在 example design 里GT 的 TXDIFFCTRL、TXPOSTCURSOR、TXPRECURSOR 等参数都是以参数形式暴露出来的。碰到眼图不理想的情况可以适当调整这些值。举个例子有次调试 5Gbps 线路速率手头的 SFP 模块在 1.25Gbps 下工作正常升到 5G 后就频繁误码。后来把 TXDIFFCTRL 从默认的 12 调到了 15同时改了一下预加重参数误码率降到了 1e-15 以下问题解决。7. 回环测试之后的下一步回环测试通过只代表链路是通的距离真正的高速通信还差几步。如果项目接下来要做 Aurora 协议通信可以把回环逻辑替换为实际的协议模块把 uart、以太网或者自定义业务数据接到 Aurora 的 AXI4-Stream 接口上。这时要重点验证的就不再是链路本身而是协议数据的组帧、解帧、以及与业务模块的握手时序。如果项目要跑更高的速率比如从 5Gbps 提升到 10Gbps那就不能继续用 Aurora 8B/10B 了得换 Aurora 64B/66B 或者更高速率的接口 IP。速率提升后参考时钟、数据位宽、GT 配置都需要重新评估回环测试也要重新做一遍。我个人做项目的习惯是每一轮板卡改动后都会先跑回环测试保留上一轮的测试日志和误码统计然后把新的测试结果做对比。这样不仅能看到链路是否变差还能从数据变化趋势里推测出问题出现在哪一次改动上。养成这个习惯之后调试效率明显提升。另外多说一句回环测试的工程文件一定要留存最好和项目的版本管理放在一起。几个月后如果遇到新问题翻出当时的回环测试工程重新跑一遍往往能快速确认是硬件改版导致的问题还是新逻辑引入的问题。这个习惯救过我很多次。