ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CameraLink转光纤传输方案:FPGA+GT+Aurora实现超远距离图像采集

CameraLink转光纤传输方案:FPGA+GT+Aurora实现超远距离图像采集 做机器视觉采集的兄弟应该都对 CameraLink 又爱又恨带宽够、协议简单、相机支持多但线缆最长只能拉 7 米一米还得好几十块。产线上相机到采集端距离稍微绕一下信号就开始抖花屏、丢帧轮着来。我这次要聊的项目就是干这个事的把 CameraLink 接到 FPGA 上解出图像数据再通过 GT 高速串行收发器加 SFP 光模块转成光信号一根光纤传出去。实测下来几十公里光纤拉过去图像纹丝不动。整个方案用的是 Xilinx 7 系列 FPGA核心链路是 GT Transceivers Wizard Aurora 8B10B 编解码架构。说白了CameraLink 进来的是 LVDS 差分信号FPGA 把它解成并行图像数据塞进 Aurora 协议栈然后由 GT 收发器串行化经 SFP 光模块转成光信号。接收端再用另一块 FPGA 把光信号还原成 CameraLink 时序或者直接输出给后端图像处理模块。这套东西非常适合工业相机远程采集、医学影像异地传输、高速视觉检测这类场景。下面我会把整个项目的设计思路、模块拆分、IP 配置、工程组织、调试经验全部摊开讲。准备照着做的兄弟只要你用过 Vivado熟悉 Verilog基本可以照着这套流程复刻出来。1. 为什么非要用光纤CameraLink 转光口到底解决了什么问题先聊一个现实问题CameraLink 标准本身设计的时候就只考虑了机箱内部的近距离传输线缆长度超过 7 米信号完整性基本没有保证。你可能会说换长线、加中继器行不行行但成本立刻翻几倍而且 CameraLink 线缆本身是专用线带锁扣、带屏蔽一米上百块拉 20 米你光买线就够买一块开发板了。产线现场还有电磁干扰、地电位差、弯折损耗这些都是 CameraLink 线缆的硬伤。所以把 CameraLink 转成光口本质上有三个好处传输距离从 7 米直接拉到几十公里SFP 光模块的传输能力远超铜缆而且光纤本身抗电磁干扰工业现场电机变频器随便开图像纹丝不动。光纤成本低普通单模双纤 LC 跳线一米几块钱比 CameraLink 专用线便宜一个数量级。电隔离天然具备两端设备通过光纤连接不会因为地电位差打坏接口芯片这在跨设备、跨车间场景里特别重要。选 FPGA 做这个转换而不是用专用转换器原因也很直白专用 CameraLink 转光纤转换器市面上有但价格动辄上万而且只做格式转换不做图像预处理。用 FPGA 的话你可以在同一片芯片里完成 CameraLink 解串、图像格式转换、帧缓存、ROI 截取、Aurora 打包后面再接 DDR、PCIe、HDMI 都行灵活性完全不是一个量级。这个方案适合谁来参考如果你是做工业相机采集、机器视觉系统集成、高速图像传输的工程师或者你是学生想拿一个“FPGA 图像传输”的实战项目去面试这套架构都是很典型的学习样本。它把 LVDS 接口、高速串行收发器、轻量级通信协议、图像数据流处理这些 FPGA 核心技术全部串起来了。2. 整体架构拆解从 CameraLink 到光口的完整数据管线开始写代码之前先把整条数据管线的蓝图画清楚。我习惯把系统拆成五个功能块来看CameraLink 物理接口与解码、图像数据缓存与位宽匹配、Aurora 协议打包、GT 高速收发、以及接收端的反向恢复。每一块单独验证最后再串起来联调这样排错成本最低。2.1 系统数据流发送端和接收端各自做什么发送端 FPGA 的职责是接收 CameraLink 的 LVDS 差分信号解码出并行图像数据和相机控制信号将像素数据按行/帧格式组织好缓存到 FIFO 或者 BRAM 里做跨时钟域处理最后通过 Aurora IP 的 AXI4-Stream 接口把数据灌给 GT 逻辑GT 完成并串转换后经 SFP 光模块发出去。接收端 FPGA 的职责是反过来的光模块收到光信号GT 恢复出串行数据Aurora IP 完成 8B10B 解码和帧同步恢复出并行数据流FPGA 逻辑把这些数据重新映射成 CameraLink 的时序格式再通过 LVDS 发送芯片或者 FPGA 内部的 OSERDES 驱动给后端的 CameraLink 采集卡。如果接收端不需要接采集卡而是直接把图像数据给算法模块那就更简单了只要把 Aurora 恢复的数据流直接接到后端处理管线即可。2.2 时钟方案和带宽计算时钟是整个系统最容易翻车的地方。CameraLink 像素时钟通常由相机提供Base 配置下单像素时钟范围一般是 20MHz 到 85MHz具体看相机型号。像素时钟进来之后FPGA 需要把它当作一个独立时钟域所有 CameraLink 解码逻辑都在这个时钟域里工作。之后数据进入跨时钟域 FIFO再转到 Aurora 用户时钟域。Aurora 8B10B 的线速率选择是有讲究的不是随便填。要保证链路吞吐量大于 CameraLink 的最大数据率还要给 8B10B 编码留出 25% 的带宽余量。这里可以直接算一笔账CameraLink Base 配置是 4 对 LVDS 数据线加 1 对时钟线一个像素时钟周期内传 28 位数据其中 24 位是图像像素数据4 位是相机控制信号。假设像素时钟为 85MHz那么峰值数据率大约是 85M × 28bit ≈ 2.38Gbps。如果用的是 Full 配置12 对数据线加 1 对时钟同样的像素时钟下峰值数据率能到 85M × 84bit ≈ 7.14Gbps。我这次的工程主要为 Base 和 Medium 配置准备以 Base 为主。Base 模式下 85MHz 像素时钟对应的数据率是 2.38Gbps考虑到 Aurora 的 8B10B 编码开销线速率至少需要 2.38Gbps × 10/8 ≈ 2.975Gbps。所以我选了 3.125Gbps 这个档位刚好是 GT 收发器很常用的速率参考时钟 125MHz扩频和预加重都留给自动协商。注意线速率不要压着极限选留一点余量。你算出来理论 2.975Gbps直接选 3.125Gbps 是稳的。如果数据率再高一点可以考虑 3.75G 或者 5G 档位但注意参考时钟频率也要跟着匹配GT 的线速率是分频电路决定的不是随便填都行。2.3 Aurora 8B10B 协议选型为什么不用自定义协议或者硬核 10G MAC很多人会问为什么非要套一层 Aurora我直接用 GT 的 raw 接口自己拼 8B10B 编码不是更省资源吗道理是这么个道理但实际做起来你会发现高速串行链路要考虑的细节太多了字符对齐、通道绑定、时钟补偿、错误检测、流控。这些逻辑全部自己写调试周期至少多出一个月而且很容易出现一些“时好时坏”的灵异问题比如长时间跑链路误码率突然变高最后发现是时钟补偿时序没处理好。Aurora 8B10B 是 Xilinx 提供的免费 IP属于轻量级链路层协议。它自动处理了通道初始化、绑定、时钟补偿、错误检测这些底层的脏活给你暴露一个干净的 AXI4-Stream 接口。在 8B10B 这个速率档位下它内部还带了 CRC 校验可以实时监测链路误码。这个对我的场景是最合适的选择因为你核心精力应该花在图像数据怎么组织、时序怎么对齐上而不是反复调试字符对齐状态机。有一个点要单独说Aurora 和 GT 在 Vivado 里是两个独立的 IP但它们是配合使用的。实际工程里会例化一个 GT Transceivers Wizard 来配置物理层参数然后在 Aurora IP 里选择“内部使用 GT”,这样 Aurora 就直接把 GT 的接口管理起来了。如果你用的是 Aurora 8B10B 的较新版本也可以直接在 Aurora IP 配置界面里勾选包含 GT它会自动帮你把 GT 配置好工程比自己手动连 GT 更干净。2.4 链路层和用户数据接口的关键信号Aurora 8B10B 的用户接口是 AXI4-Stream 协议发送端需要驱动几个关键信号s_axi_tx_tdata 是待发送数据s_axi_tx_tvalid 拉高表示数据有效s_axi_tx_tready 由 IP 输出表示链路可以接收数据s_axi_tx_tlast 用来指示一帧或一个包的结束。接收端则是 m_axi_rx_tdata、m_axi_rx_tvalid、m_axi_rx_tlast 这三个信号为主再加上一个 m_axi_rx_tuser 指示错误状态。理解这个接口并不难它本质上就是一个带握手的数据管道。你要做的就是在 CameraLink 解码逻辑和 Aurora 接口之间加一个 FIFO让两个时钟域的数据平滑交接。发送端什么时候拉高 tvalid很简单只要 FIFO 非空且 Aurora 准备好就把数据送出去。什么时候拉高 tlast看你的帧定义如果你约定一帧图像就是一个 Aurora 帧那就在一行的最后一个像素或者一帧的最后一个像素位置拉高 tlast接收端据此知道一帧结束了。3. CameraLink 接口解码从 LVDS 差分信号到 28 位并行数据CameraLink 接口本质上是 ChannelLink 技术的扩展。相机端把并行图像数据经过 7:1 的并串转换经由 LVDS 差分对发送接收端再解串回并行数据。很多兄弟刚接触这个接口时会被“4 对数据线怎么传 28 位数据”这个问题困惑其实就是 4 对线 × 7 位串行 28 位一个像素时钟周期内完成一次全部传输。3.1 接口芯片方案DS90CR288A 和直连 FPGA 怎么选CameraLink 解码有两种实现方式一种是外部接 CameraLink 解串芯片比如 DS90CR288A芯片负责把 4 对 LVDS 数据线解成 28 位并行 TTL 信号FPGA 只需要把这些并行信号直接采进来就行。另一种是 FPGA 直接接收 LVDS 差分数据用内部的 ISERDES 原语做 7:1 解串。两种方案各有优劣我补充说明一下。接口芯片方案的优点是时序稳定、不需要在 FPGA 里做复杂的位对齐训练芯片内部已经做好了抗偏斜处理FPGA 代码逻辑简单很多适合项目周期紧的情况。缺点是芯片是外购的增加 BOM 成本而且 DS90CR288A 这类芯片输出的是 3.3V 并行信号需要注意电平兼容。直连 FPGA 方案的优点是省掉芯片BOM 更简单ISERDES 解串可以借助 FPGA 内部的延迟链做 per-bit 对齐灵活度高适合批量大、成本敏感的产品。缺点是调试难度大需要对 LVDS 接收端的 tap delay 做训练前期开发时间明显增加。我们这套工程里采用接口芯片方案作为主推方案原因很简单项目目标是快速稳定地打通链路而不是研究 LVDS 信号对齐算法。DS90CR288A 输出的并行信号非常稳定FPGA 侧不需要做任何特殊处理只要锁相环把像素时钟采平就行。如果你希望进一步压低成本后期可以自己在工程里改成 ISERDES 方案数据接口保持不变就可以。3.2 并行数据的组织像素、行有效、帧有效和相机控制信号DS90CR288A 解出来的 28 位数据里24 位是像素数据3 个 8 位通道4 位是相机控制信号。像素数据的排列方式取决于相机是 8 位、10 位还是 12 位输出。对于最常用的 8 位像素格式24 位数据里正好是三个像素分别在 Rx0、Rx1、Rx2 三个字节里对于 10 位或 12 位像素格式不同相机厂商的位映射会有差异需要查相机的 CameraLink 协议手册确认。时钟信号同样由芯片解出DS90CR288A 输出一个与并行数据对齐的像素时钟。FPGA 里用这个时钟作为所有 CameraLink 解码逻辑的时钟域。同时还需要区分图像数据和控制信号LVALLine Valid和 FVALFrame Valid这两个信号是关键LVAL 拉高表示当前时钟周期内输出的是有效像素FVAL 拉高表示当前处于有效帧区间。CameraLink 协议还定义了 DVAL 和 Spare 信号实际工程里我们主要关注 FVAL 和 LVAL。注意在数字相机中帧有效期间可能有行消隐和帧消隐也就是 FVAL 和 LVAL 并非总保持拉高。FPGA 解码逻辑必须对消隐期做正确处理最简单的做法是把 FVAL 为高且 LVAL 为高的像素写入 FIFO同时用逻辑计数出每行像素数和每帧行数把这组计数放到图像数据流里发给接收端接收端据此恢复图像尺寸。3.3 图像数据缓存跨时钟域 FIFO 的正确打开方式CameraLink 像素时钟和 Aurora 用户时钟是两个完全独立的时钟域中间必然要有异步 FIFO 做缓冲。这里有个常见的错误有人直接把像素时钟域的数据接到 Aurora 接口上结果出来的图像要么瞬间丢一行要么数据错位。原因很简单两个时钟域的频率相位都没有关系异步 FIFO 是必需的。FIFO 的设计有几个关键参数数据位宽要和 Aurora 接口对齐。比如 DS90CR288A 输出 24 位像素数据但 Aurora 接口数据位宽可能是 32 位。那就需要一个 24 位写入、32 位读出的位宽转换 FIFO。Vivado 的 FIFO IP 本身就支持不同读写位宽直接配置即可。不过有个坑不同位宽转换时FIFO 的深度计算要按位宽折算否则深度会不对。第二个坑是 FIFO 的空满标志处理。CameraLink 解码逻辑在写入 FIFO 时要判断 almost_full不能让 FIFO 写满后继续写否则会丢数据。Aurora 发送逻辑在读 FIFO 时也要判断 empty防止读空。更稳妥的做法是在发送端做一个标准 AXI4-Stream 从机状态机只要 FIFO 非空且 Aurora 允许发送就持续读 FIFO 并驱动 tdata/tvalid。一幅图像的帧控制信息比如图像分辨率、帧号也需要和像素数据同步传送。我的习惯是把这些信息打包成一个 32 位的头在每帧图像第一个像素之前插入 FIFO。接收端先用状态机识别帧头然后根据帧头里的信息解析后续像素数据这样接收端不需要预先知道相机的配置灵活很多。再说一种更简洁的帧同步方案直接用 Aurora 的帧接口frame interface而不是流接口stream interface。Aurora 8B10B 支持帧模式它允许你把一整帧数据包装成协议帧带 SOFStart of Frame和 EOFEnd of Frame信号。这样做的好处是每帧图像数据天然隔离链路上如果有误码不会跨帧传播接收端不需要自己判断哪里是一帧的开始Aurora 核会自动把帧边界标记出来。我工程里就是用这种模式。4. GT Transceivers Wizard 和 Aurora 8B10B 的配置要点这块是整篇文章的重头戏也是很多新手容易卡住的地方。Vivado 里配置 GT 和 Aurora IP 不是随便点几下就完事的参数选错上板直接起不来链路。我把我的配置经验和调试记录全部写出来。4.1 GT Transceivers Wizard 参数配置线速率、参考时钟、协议模板在 Vivado IP Catalog 里搜 GT Transceivers Wizard打开后第一页选择协议模板。这里有个技巧GT Wizard 提供了一个 Start from Scratch 的选项但如果你的目标是 Aurora 8B10B可以直接在预设模板里选 Aurora 8B10B它会自动设置好很多底层参数比如 TX/RX 的 8B10B 编码使能、逗号码检测等。不过要说明一下现在的 Aurora IP 在较新版本里已经可以直接管理 GT所以 GT Wizard 里具体怎么配取决于你用的 Vivado 版本和 Aurora IP 版本。我用的主流方式是Aurora IP 例化时选择包含 GTAurora IP 内部自己调用 GT Wizard。这种方式最简单你只要在 Aurora IP 配置界面里填好线速率、参考时钟、数据位宽它内部自动生成对应的 GT 配置。如果选择手动方式GT Wizard 里需要打开的地方很多TX/RX 线速率设为 3.125Gbps,参考时钟设为 125MHz,协议模板选 Aurora 8B10B,编码模式不要选 64B66B 而是选 8B10B,时钟校正和 comma 对齐都用默认。参考时钟是怎么定的这里有个固定的对应关系3.125Gbps 的线速率配 125MHz 参考时钟GT 内部的 PLL 倍频到 3.125Gbps。如果是 2.5Gbps可以用 125MHz 或者 100MHz 参考时钟但 125MHz 更标准。记住一个原则参考时钟的频率误差必须控制在 ±100ppm 以内板子上必须用晶振而不是普通的 RC 振荡器给 GT 的 MGTREFCLK 引脚供时钟。GT Wizard 还有一个很关键的页面是 TX/RX 的 equalization 和 drive strength。对于短距离光纤 1 米跳线或者板载光模块默认参数完全够用。但如果光纤很长或者 SFP 模块质量不稳定可以尝试调整 TX 的 pre-cursor 和 post-cursor 参数以及 RX 的 LPM 模式。这些参数不是越大越好一般用默认值先跑通出现误码再回去调。4.2 Aurora 8B10B IP 配置流模式还是帧模式数据位宽怎么选Aurora 8B10B IP 的配置界面里最重要的一项就是数据位宽。选择多少位宽直接决定了用户时钟频率。3.125Gbps 的线速率经过 8B10B 解码后有效数据率是 2.5Gbps。如果数据位宽选 2 字节16 位用户时钟是 156.25MHz选 4 字节32 位用户时钟是 78.125MHz。我建议优先选 32 位宽因为 78MHz 左右对 FPGA 逻辑时序要求低时序收敛容易而且 AXI4-Stream 接口的时钟频率越容易满足FIFO 读出的数据吞吐也够。注意如果你选的相机是 Full 模式数据率会高出两倍多。这时要么提高线速率到 6.25Gbps 或更高要么在发送端做数据压缩。我这边 Base 模式 3.125Gbps 配 32 位位宽是标准做法跑满 85MHz 像素时钟毫无压力。Aurora IP 还要求选择流模式Streaming还是帧模式Framing。我强烈建议用帧模式虽然接口稍微复杂一点但它提供了一个重要能力用户数据被包装成帧帧之间有显式的间隔接收端恢复的 m_axi_rx_tlast 信号会精确地指示每帧的结束。如果你做图像传输没有定义帧的概念那用流模式也行但后续调试会麻烦很多。我工程里全部用帧模式每传一幅图像就是一个 Aurora 帧。Aurora 还有一个重要的初始化信号channel_up 和 lane_up。上电复位之后链路要经历一个握手和初始化过程收发双方交换握手序列、对齐字节、时钟补偿序列最终 lane_up 拉高表示每个通道初始化完成channel_up 拉高表示整个通道组可用。什么时候可以开始传数据必须等 channel_up 等于 1。很多新手忽略了这个信号链路没准备好就往上灌数据自然全部丢失。我一般会在发送逻辑里显式等待 channel_up同时监测 hard_err 和 soft_err一旦出错就重新拉低复位等链路重新 up 之后再发送图像数据。4.3 用户接口设计AXI4-Stream 时序和背压处理Aurora 的用户接口就是一个 AXI4-Stream 从/主接口。发送端FPGA 逻辑作为主设备Aurora 作为从设备的时序有三个要素TVALID、TREADY、TDATA。TVALID 和 TREADY 同时为高时一个数据节拍完成传输。TREADY 是 Aurora 输出给我们的它表示链路当前时刻是否可以接收数据。注意TREADY 可能会在传输过程中突然拉低比如 Aurora 需要插入时钟补偿序列时会临时反压发送端。所以你的发送状态机必须做标准的握手逻辑TVALID 拉高后一定要等 TREADY 为高才认为数据传输完成不能假设 TREADY 一直为高。帧模式下还要处理 TX_SOF 和 TX_EOF在时序上一个帧的起始数据要同步拉高 SOF最后一个数据要拉高 EOF。接收端则通过 RX_SOF 和 RX_EOF 来识别帧边界。这两个信号和 TDATA 是严格对齐的用起来非常直接。4.4 复位和错误恢复机制Aurora 8B10B 的复位逻辑对初学者不太友好。这里我直接给出一套可用的复位流程系统上电后先给 Aurora IP 的复位信号拉高至少 1us然后释放复位接下来等链路初始化完成channel_up 拉高如果 channel_up 没有在预期时间内拉高需要重新复位再次等待。实际项目里我发现有些劣质 SFP 光模块或者光纤跳线质量不好会导致 channel_up 反复翻转这时候不要频繁复位要在代码里加一个状态机检测到 hard_err 或 channel_up 掉下来之后先等待 10us 稳定时间再决定是否触发重新初始化。接收端的错误处理更要重视Aurora 的 RX 接口有一个 tuser 信号表示当前数据是否有效或者是否发生了链路错误。当链路发生错误时接收端可能收到错误数据如果直接把错误数据送去显示可能瞬间出现花屏。我的做法是在接收端检测到 tuser 拉高或者硬错误标志置位时直接丢弃当前整帧数据并对外输出一个错误计数方便上位机统计。5. 四套工程源码的组织结构与移植指南标题里说了“提供 4 套工程源码”这里我把四套工程分别是什么、适用什么场景以及源码目录结构怎么组织讲清楚方便你拿到源码之后能快速找到自己要用的部分。5.1 四套工程分别覆盖哪些配置第一套工程是 Base 配置 CameraLink 转光纤这是最常用的一版。它针对大多数 Base 接口工业相机像素时钟范围 20MHz~85MHz线速率 3.125GbpsAurora 帧模式发送端接收端代码都在里面。你拿到这一套配合任何一片带 SFP 的 7 系列开发板改一下引脚约束就能跑起来。第二套工程是 Medium 配置 CameraLink 转光纤。Medium 配置用了 8 对 LVDS 数据线一个像素时钟周期传 56 位数据。这套工程在解码端用了两个 DS90CR288A 芯片的组合FPGA 侧数据位宽扩展到 64 位Aurora 线速率提升到 5Gbps才能装下更大吞吐的数据流。它适合分辨率更高、行频更快的面阵或者线阵相机。第三套工程是单模光纤远距离传输版本。前两套用的都是多模 SFP 模块850nm传输距离几百米这一套换成了单模模块1310nm,传输距离几十公里同时在前面带了一个可选的 8B10B 误码仪模块方便现场验收时做链路误码率测试。这套的整体逻辑和前两套一致主要是板级器件选型和 SFP 模块类型不同FPGA 代码只做了少量调整。第四套工程是接收端输出恢复 CameraLink 的完整实现。前面三套发送端的工程都会附带一个辅助接收端但第四套专门针对“把光信号恢复成 CameraLink 电信号”做了完整的端到端设计光纤进来 - Aurora 解码 - 像素数据重组 - DS90CR287 芯片驱动 CameraLink 输出。广播级或者试验室回放场景会用到这一套我也常建议客户在发送端调试时用这套 Recovery 模块做自环验证。5.2 源码目录结构和关键模块清单每个工程的目录结构尽量保持一致方便对照查看。我习惯的源码组织是src/ 存放所有 RTL 源文件按功能分子目录cameralink/ 存放 CameraLink 解码和时序解析模块aurora/ 存放 Aurora 顶层封装、复位管理、数据发送接收模块axi_stream/ 存放 FIFO、跨时钟域处理、AXI4-Stream 控制逻辑top/ 存放系统顶层模块包含时钟管理、引脚例化ip/ 存放生成的 IP 核 XCI 文件constr/ 存放 XDC 引脚约束和时序约束sim/ 存放仿真测试平台这里我把核心模块做一个列表说明方便你拿到工程后直接定位模块名功能说明位置cameralink_rx_topCameraLink 芯片解码后并行数据采样src/cameralink/frame_sync提取 FVAL/LVAL 并组织像素流src/cameralink/async_fifo_w32_r32跨时钟域像素缓存src/axi_stream/aurora_tx_controllerAXI4-Stream 主发送状态机src/aurora/aurora_rx_controllerAXI4-Stream 从接收解析状态机src/aurora/link_reset_manager链路复位与 channel_up 监视src/aurora/camera_info_header帧头信息打包/解析src/axi_stream/5.3 移植到不同 FPGA 型号的注意事项这套工程设计时尽量使用了通用原语但移植时还是有几个点要注意。GT 参考时钟引脚每个型号位置不同XDC 里的 package pin 必须查对应芯片的引脚表重新分配这个最容易错。不同的 FPGA 的 GT bank 数量不同多路 CameraLink 输入时要注意多个 GT 通道要尽量分配在同一个或相邻的 bank否则参考时钟布线会很麻烦。另外7 系列和 Ultrascale 的 GT 底层配置界面差异很大。我这四套工程全部基于 7 系列K7325T 为主如果你用的芯片是 UltraScale 或者 UltraScaleAurora IP 版本要求更新GT Wizard 的配置选项也不同。建议在创建工程前先用目标芯片版本配置一次 Aurora IP确认能生成出 IP 核再开始写顶层代码。避免写完全部逻辑之后发现该芯片的 GT Wizard 生成方式有变化返工成本很高。6. 上板调试流程和问题排查实录这一节是我最想写的因为硬件调试里的坑永远比理论多。我把从零到跑通的经验和踩坑记录整理出来每一行都是实打实花时间换的。6.1 上板第一步先测 Aurora 本底通信不要急着接 CameraLink我的调试顺序是先不接相机把 FPGA 发送端的 Aurora 接上一个固定测试图案的数据源比如一个计数器或者 PRBS 伪随机序列接收端把收到的数据回传给上位机比对先确认 Aurora 链路能稳定传输数据。这一步跑通了说明 GT 物理层、SFP 模块、光纤、Aurora 协议栈都没有问题。这里有一个特别重要的细节SFP 光模块的 TX_DISABLE 引脚。很多 SFP 模块出厂默认是使能发光的但一些模块在未配置时会处于禁用状态。如果上电后发现 channel_up 始终不拉高第一件事就是检查 SFP 模块的 TX_FAULT 和 TX_DISABLE 引脚是否正常。我遇到过模块本来是好端端的但对应的 FPGA IO 默认电平和模块的 ENABLE 逻辑不匹配导致一直没发光折腾了大半天才发现。6.2 常见问题排查表链路不 UP、误码率高、图像花屏现象可能原因排查手段解决方法channel_up 一直不拉高SFP 没发光、光纤接反、参考时钟没起来用光功率计测光功率检查 MGTREFCLK 引脚波形调整 SFP 使能重插光纤跳线检查时钟源channel_up 拉高后反复掉线线速率配置不匹配、光模块质量差、时钟源不稳定抓取 hard_err 信号波形替换光模块统一两端线速率和参考时钟频率保证 SFP 模块互相兼容链路 UP 但误码率很高SFP 发射功率异常、光纤弯曲半径太小用误码仪统计错误字节数检查光路更换光纤跳线调整光模块发射等级接收端图像花屏或缺行帧头信息解析错误、FIFO 溢出、像素格式不匹配加调试逻辑抓取 FVAL/LVAL比对发送端插入的帧头修正帧头字段定义检查 FIFO 深度和读写速率图像整体偏移或颜色错乱CameraLink 像素位映射错误用测试图案相机输出纯色观察像素字节顺序对照相机手册修正位映射和字节序6.3 双端对接时一定要统一的东西发送端和接收端必须统一三个参数否则会出现各种暗坑Aurora 线速率必须一致两端都是 3.125GbpsAurora 帧模式定义必须一致比如一帧图像是一幅完整图像还是图像的某一行像素数据位宽和字节序必须一致尤其是 10 位或 12 位像素格式高两位或低两位放在哪个字节一定要核对。我调试时还遇到过一个很隐蔽的问题CameraLink 解码输出的像素时钟不是恒定不变的相机会在行消隐期间插入一些时钟周期导致像素时钟频率有轻微跳变。如果注意不到这一点发送端 FIFO 设计时就可能按平均带宽计算深度结果在突发像素数据到来时 FIFO 溢出表现为随机丢行。解决办法是 FIFO 深度留至少两行图像的余量或者在发送端加一个简单的速率控制器当 FIFO 半满以上才开始整帧发送。我工程里默认采取整帧缓存再发送的策略FIFO 深度按最大分辨率的一帧图像来设计这样突发问题直接从根上解决了。6.4 Vivado 下板调试实录从波形定位到参数修复我拿一个实际的调试场景说一下第一次上板channel_up 正常拉高但是图像到接收端之后每隔几秒就出现一行花屏。我的排查思路是先确认是链路误码还是数据组织问题。在接收端用 ILA 抓 m_axi_rx_tdata 和 tuser发现 tuser 会有零星的高脉冲而且这些脉冲会导致误字节。继续追查发现是光模块和光纤连接处损耗偏大导致接收端光信号在灵敏度边缘时好时坏。把光纤接头清洁之后问题立刻消失。还有一个场景是发送端数据连续性和 tready 反压的问题。用 ILA 同时抓 tvalid 和 tready 的波形发现 tready 会周期性拉低一小段时间这是 Aurora 在做时钟补偿CC属于正常现象。但是我的发送状态机当时没有等 tready直接认为数据已经发出去了导致丢了一拍。修复方式就是严格按标准握手处理。提示调试阶段建议把 Aurora IP 的 hard_err 和 soft_err 两个信号引出来接到一个 LED 或者 ILA 上出现问题时第一时间看是硬错误还是软错误。硬错误一般代表链路物理层异常软错误可能是偶发噪声或者时序收敛问题两者的处理方向完全不同。7. 资源占用、性能测试和后续扩展方向系统整体跑通之后我做了资源占用统计和长期稳定性测试。在 Kintex-7 K325T 上CameraLink 解码加 Aurora 发送端的 LUT 占用大约 8000 左右FF 大约 10000BRAM 占 20 多个GT 用了 1 个。接收端比发送端略少一些。整套系统的资源开销很小一片 Artix-7 35T 级别的芯片也能放得下成本可以压得很低。长时间稳定性测试我跑了 72 小时不间断图像传输用测试图案统计误码。结果是零误码通道一次没掉过。这个稳定性成绩和光模块质量、电源纹波、FPGA 时序收敛都有关系。我强烈建议在板子电源设计上给 GT 的模拟电源单独加滤波不要和数字电源混在一起否则长期运行误码率可能莫名升高。后续扩展方向可以有几个选择如果相机的数据率特别高比如 5Gbps 以上Aurora 8B10B 就力不从心了这时可以切换到 Aurora 64B66B 协议线速率上到 6.6Gbps 甚至 10Gbps。如果想在 FPGA 里直接做图像处理比如白平衡、畸变校正、ROI 截取完全可以在发送端 FIFO 后面加一个处理管线不影响 Aurora 链路整个结构。更进阶的玩法是把整个链路接到 PCIe 或者 DMA 上光纤进来的图像数据直接写进电脑内存这也是很常见的高速图像采集方案。我个人在实际操作中的体会是做这套系统最耗时间的往往不是 Aurora 和 GT 配置而是把 CameraLink 的像素时序和帧组织弄清楚。很多相机厂商的协议文档写得并不算直观关键就是对照示波器波形和采集到的数据逐个字节核对。只要把数据映射关系弄对了后续的链路稳定性和图像质量都是水到渠成的事。最后再分享一个实用小技巧现场调试时不要相信任何一根光纤跳线“应该是好的”。用光功率计或者换一对已知没问题的跳线来排查往往能减少一半的无效劳动。这套系统我前前后后调试了两个多月遇到的坑基本都是这一类“相干物件都正常但实际上某个环节就是不正常”的问题。如果你正在复刻这个方案记住我开头说的那句话先把 Aurora 自环跑通再接相机。这两步之间的时间差就是很多人口中“FPGA 图像传输太难”的真正原因。链路本身并不复杂复杂的是把每个环节都做得可靠。这套架构我们团队已经交付给多个客户从工业现场到试验室验证都稳定运行算是打磨得比较透的方案了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进