
先说结论CameraLink转SFP光口说白了就是把相机那根又粗又贵又短的CameraLink线换成一颗SFP光模块让图像能跑到几百米甚至几十公里外。FPGA在这条链路里就干两件事一是把CameraLink的并行视频时序准确接住二是用GT Transceivers Wizard加上Aurora 8B10B协议把数据打包成高速串行光流送出去。这套方案在产线视觉检测、医疗影像采集、科研实验系统中都非常常见。很多项目卡住不是因为FPGA本身多难而是CameraLink时序、光口参数、复位顺序这几个环节没人讲透。这篇文章就围绕我整理好的4套工程源码展开把发送端、接收端、回环测试以及CameraLink Full扩展的做法一次说清楚。如果你正在做FPGA图像传输又不想花几万块买专用光纤转换器那这套思路可以直接照着搭。1. 方案定位为什么是“CameraLink SFP光口”这个组合1.1 什么场景需要把CameraLink送进光口我实测过的典型场景是工业相机挂在产线A点工控机或者图像处理主机在三十米外的控制柜里。相机接口是CameraLink Base常规线缆厂家建议5米到10米以内就算你硬买15米长的成品线信号眼图也会劣化跑满像素时钟时偶发花屏。有人第一反应是买CameraLink中继器一个无源中继器几千块两个一串联比很多二手机还贵。还有人买专用光纤转换器一套上万固定协议、固定速率后面想加点图像预处理逻辑基本没门。我的做法是拿一块带SFP座的FPGA开发板前端用CameraLink解串芯片把LVDS转成并行数据FPGA里做时序解析和缓存再用GT高速收发器配合Aurora 8B10B把数据送进SFP光口。换成单模双纤模块传输距离直接到10公里以上而且光纤链路天然抗干扰厂房里有大功率伺服电机、变频器也不怕。带宽方面要算一笔账。CameraLink Base模式下3组数据差分对加1对像素时钟内部是7:1串行。当像素时钟85MHz时每组数据对速率是85乘以7约595Mbps3组数据物理总速率约1.785Gbps但对应的并行数据是24bit图像加4bit控制信号也就是28bit乘以85MHz约2.38Gbps的有效数据率。注意这里算的是并行域数据量放到Aurora链路里还要经过8B10B编码线速率需要再预留25%开销。所以选光口速率时很多人踩坑Aurora单通道2.5Gbps线速率时8B10B编码后有效净荷只有2Gbps跑满85MHz的Base相机其实不够FIFO会慢慢被撑爆。我的工程里默认把GT线速率配成3.125Gbps单通道净荷约2.5Gbps留出余量如果相机像素时钟只有60MHz或者更低用2.5Gbps也能跑。拿到工程后建议先算一下自己相机的实际像素时钟再决定线速率不要照抄。1.2 成品转换器、中继器和FPGA方案的取舍这里放一张对比表把市面上几个常见路线的差别讲清楚。方案成本区间最大传输距离灵活性扩展性CameraLink中继器单个3000到8000受铜缆限制十几米一级固定CameraLink格式无专用光纤转换器一套上万几十公里CameraLink格式固定想加图像处理没门FPGA SFP光口板子加SFP模块几百到两千取决于光模块多模500米单模几十公里链路可变数据格式可改可加ISP、ROI裁剪、压缩、多路复用FPGA方案最大的价值不是“省钱”这么简单而是链路完全可控。今天Base相机明天换Full相机改FPGA代码就能适配后天想顺带做灰度校正、坏点修复也是在同一个设计里加IP的事。专用转换器买回来是什么样就永远是什么样想动一点逻辑都动不了。再说协议选择。FPGA点对点光口可以自己写8B10B也可以用Aurora 8B10B、Aurora 64B66B这些现成协议。对普通图像传输场景我推荐Aurora 8B10B。它替你解决三件最容易翻车的事字节对齐、时钟修正、多通道绑定。这些模块如果自己从零写GT至少多折腾两周还未必稳定。Aurora 8B10B把链路层固化了用户侧就是干净的AXI4-Stream总线接口简单直接。2. 核心链路拆解CameraLink、GT与Aurora各司其职2.1 CameraLink接口在FPGA这边到底长什么样一套完整的CameraLink接口不只是LVDS数据线它至少包含三部分主链路Channel Link、串行通信控制线SerTC/SerTFG、以及可选的Power over CameraLink。主链路的物理层是LVDS差分对。Base配置下发送端芯片把28位并行数据24bit图像数据加上LVAL、FVAL、DVAL、SPARE四个控制位按7:1串行化通过3对数据线和1对像素时钟线发出去。FPGA这边有两种常见接收方式第一种外接DS90CR288A这类解串芯片它把LVDS转成28位并行数据FPGA收到后直接按像素时钟采集时序压力小。第二种完全用FPGA内部逻辑解串用LVDS差分缓冲器加移位寄存器把7个串行bit还原出来这要求管脚约束、延迟约束都很严格Base模式还能勉强做Full模式下要处理三组并行链路复杂度很高新手容易在这里卡死。我自己做项目除非目标就是验证FPGA解串能力否则都首选解串芯片。Cameralink接口芯片处理了差分转单端、7:1解串、时钟恢复这些脏活FPGA只接并行数据和PIXCLK工程量小很多上板成功率也高。解码之后的关键就是时序信号LVAL拉高表示一行有效FVAL拉高表示一帧有效DVAL是数据有效标志。后面所有FIFO写逻辑都要根据这三个信号判断“这一拍数据到底算不算数”不能无脑全写进去。2.2 SFP光模块和GT Transceivers Wizard怎么配合SFP光模块的电气接口其实很标准TX_P/TX_N和RX_P/RX_N两对高速差分线再加上Mod_Def、LOS、TX_Disable这些管理信号。SFP模块本身只负责光电转换CDR时钟恢复和8B10B编解码都在FPGA的GT收发器里完成。模块供电3.3V板上最好把SDA和SCL引出来接IIC上电后能读模块厂商、速率、波长调试时省去很多猜疑。GT Transceivers Wizard这里我认为最重要的三个配置项是线速率、参考时钟、参考时钟源。线速率直接决定了光模块的工作速率档位参考时钟频率要跟线速率匹配。比如3.125Gbps线速率通常配156.25MHz参考时钟2.5Gbps配125MHz参考时钟必须接在MGT Bank的专用参考时钟引脚上不能拿普通IO或者MMCM分频出来的时钟去驱动GT否则抖动太差误码率会高到怀疑人生。如果工程里用多通道GT参考时钟源建议用QPLL而不是CPLL。QPLL可以被多个通道共享CPLL严格要求每通道独立参考时钟配置更麻烦频率范围也窄。在Vivado里创建GT Wizard并且选择Aurora 8B10B协议模板时工具会自动把8B10B编码、RX CDR这些参数填好剩下的主要是确认线速率和参考时钟数字没有填错。2.3 Aurora 8B10B协议到底帮你把活干到了什么程度Aurora 8B10B是Xilinx官方提供的一种轻量级、点对点串行链路协议最直接的优点是开箱即用。协议内部自己维护了字节对齐、通道绑定、时钟修正。通道绑定解决的是多通道传输时各通道恢复数据的对齐问题如果不做绑定两路GT恢复出来的数据会在字节边界上差几个cycle图像花到没法看。时钟修正解决的问题更隐蔽发送端和接收端的参考时钟不可能完全同频时间长了收发缓冲会漂移Aurora在数据流中周期性插入修正序列接收端根据缓存水位删除或者重复这些序列把两个时钟域的误差抵消掉。使用Aurora IP时状态机链路是固定套路复位控制器先释放GT的PMAReset然后PCS复位接着进行lane初始化、通道对齐最终lane_up和channel_up拉高。看到channel_up为高才能认为这条光链路是可用的。如果channel_up不拉高所有上层逻辑都不要指望工作。Aurora用户接口是AXI4-Stream。从图像传输角度我推荐用Stream模式因为Frame模式要求发送端精确控制TLAST和帧长度而图像的尺寸本来就不固定。Stream模式本质是一长串连续数据流接收端只要自己做帧头检测就能恢复画面。Aurora IP还会生成USER_CLK给用户逻辑用这个时钟是从GT TXOUTCLK链路衍生的和GT收发器同源上层FIFO读写时钟要以它为准不要自己随便再生成一个频率差不多的时钟去替代。3. 4套工程源码结构与职责3.1 发送端工程CameraLink相机接入转光口输出第一套是发送端整体数据流如下CameraLink LVDS进来到DS90CR288A解串转成28位并行数据和像素时钟进入FPGAFPGA内先做FVAL/LVAL/DVAL解析把图像数据按32位拼接拼好的数据写入异步FIFO写时钟是像素时钟读时钟是Aurora的USER_CLK读出后在数据前面加一帧自定义帧头然后进Aurora TX发送端最后进入SFP光口。这中间有两个细节非常关键。第一像素时钟可能只有20到85MHz而Aurora USER_CLK取决于线速率和数据位宽例如3.125G线速率配32bit通路时USER_CLK约78.125MHz两个时钟域之间必须用异步FIFO隔离。第二图像数据需要自己加帧头因为Aurora只保证字节流可靠传输不负责识别“这是新的一帧”。我习惯在帧头放魔术字0xAA55加帧计数加图像宽度高度接收端检测到帧头就重新同步就算偶尔丢了几拍数据也不会导致整个画面永久错位。发送端还需要处理的坑是消隐区。很多相机在FVAL拉高期间LVAL会出现短暂的拉低这是正常的行消隐如果逻辑没有处理好会把消隐当成有效数据写进FIFO接收端画面出现多余的杂点。我的做法是只有当FVAL、LVAL、DVAL同时有效时才写FIFO否则不写这样可以保证FIFO里全是有效像素。3.2 接收端工程光口数据恢复为CameraLink信号第二套是接收端完成发送端的逆过程。SFP光口进来的数据进GT接收侧Aurora IP恢复出AXI4-Stream数据流FPGA内检测帧头然后写入异步FIFO写时钟是Aurora USER_CLK读时钟是本地生成的像素时钟。从FIFO读出的数据再组合成28位并行CameraLink信号送给DS90CR287并转串芯片输出LVDS差分对给采集卡或者显示器。接收端的关键是像素时钟的生成。因为接收端不接相机没有外部像素时钟必须自己用MMCM从板上时钟或者GT recovered clock派生一个稳定像素时钟比如85MHz。这个时钟要保证与相机输出的像素频率高度接近否则会出现FIFO漂移。如果本地时钟比相机发送快FIFO会逐渐读空反过来会逐渐写满。工程里我会在FIFO空和满两个方向做保护空的时候补0满的时候丢帧至少保证画面不花。接收端生成CameraLink输出时要严格还原LVAL、FVAL和DVAL的时序。采集卡判断图像帧结构完全依赖这三个信号时序不对采集卡要么抓不到图像要么抓到的图有明显的错行错列。SPARE位一般拉低。如果是双tap或者多tap相机需要在发送端就按采集卡能接受的方式重排像素顺序接收端不做额外处理。3.3 回环测试工程先别接相机把光链路调稳第三套是回环测试工程。这个工程的价值是不接相机、不接主机先把GT光链路和Aurora协议调到完全稳定再接到真实数据通路里。否则相机和代码两头一起出问题时排查起来非常痛苦。回环工程支持两种自环模式。第一种是GT内部近端PCS环回数据从TX出去后在GT内部直接回到RX这个模式测的是FPGA内部的GT配置、时钟、复位逻辑是否正常。第二种是外部SFP光纤环回用一根光纤跳线把SFP模块的TX和RX连起来或者用双纤模块把两根纤对插数据经过真实的光电转换和光传输再回来这样才能验证SFP模块、光纤、连接器这些硬件链路。我在回环工程里加了一套简单的伪随机码发生器用PRBS31做发送接收端做相同的PRBS序列比对并统计错误bit数。上板后只要观察ILA里的错误计数器在长时间运行下不增长就说明光链路是干净的。这套工程建议所有用户拿到板子后第一天就跑跑一整夜零误码再继续下一步。3.4 Full配置变体工程从Base相机扩展到大带宽第四套是Full配置变体工程。CameraLink Full模式用了3个Channel Link组并行传输图像数据位宽更高物理上需要27对LVDS线像素带宽远大于Base。如果相机是Full配置单通道Aurora线速率再高也可能不够这时候就要把Aurora扩展成双通道或者四通道用户接口位宽变成64bit或128bitUSER_CLK相应调整。多通道Aurora的优势是链路层本身支持通道绑定数据在发送端按字节轮流分发到各通道接收端再按初始化序列把多路数据重新拼成完整序列。用户侧看起来还是一路AXI4-Stream数据不用自己为多通道做复杂的负载均衡。所以Full相机工程里我要改的主要是Aurora Lane Count、数据位宽、GT配置以及上位机侧对应的解包逻辑图像拼接部分复用Base工程的结构。第四套工程还预留了一个图像处理挂载点。在FIFO之后、Aurora发送之前设计里留出一段寄存器配置好的数据通路可以把灰度校正、坏点修复、ROI裁剪这类简单图像处理IP挂上去。这个思路很重要因为实际项目中光口上一旦有了FPGA用户总会在某个阶段提出“能不能顺便做点处理”没预留位置就只能推倒重来。4. 工程实操流程与关键参数4.1 Vivado搭建步骤与IP配置以Xilinx 7系列GTX为例完整步骤大概是新建Vivado工程选对FPGA型号例如XC7K325T-2FFG900。添加Aurora 8B10B IP。Vivado会同时把GT Transceivers Wizard实例化管理到Aurora IP内部Aurora配置界面里选择协议模板、线速率、Lane Count、数据宽度、Stream/Frame模式。确认参考时钟频率。Aurora IP页面会要求填参考时钟比如线速率2.5G就填125MHz3.125G就填156.25MHz这个值和板子原理图要完全一致。先在IP的Example Design上跑通。Aurora IP自带一个example工程里面有复位模块、回环控制、ILA调试核先综合实现下载到板子上确认channel_up能拉高。在example设计基础上把自己的数据通路接进去。删掉example自带的loopback连接外部SFP管脚约束加上自己写的FIFO、帧头检测、DS90CR288A接口逻辑。这里有个忠告不要一上来就自己搭顶层。Aurora IP的example design已经有完整复位时序和GT hard macro封装直接改它是最稳的。自己从零搭GT顶层复位时序差一点就可能出现“上电第一次能跑重新复位就开不起来”这种玄学问题。4.2 上板调试顺序我每次调试这套链路都按固定顺序来这样节省了大量时间第一步确认FPGA加载成功板上指示灯正常SFP光模块插好光模块的LOS状态正常。有光时LOS应该为低很多板子上面会直接带LOS指示灯。第二步用示波器或者频率计测MGT_REFCLK确认参考时钟频率和配置一致。这一步很基础但屡次救我命因为不少问题是板子焊接虚焊或晶振没起振导致的。第三步下载最小的Aurora example设计观察lane_up和channel_up。如果这两个信号拉不高先解决复位时序、参考时钟、GT配置这三大件不要继续往下接业务逻辑。第四步跑回环PRBS测试观察误码计数。一次跑至少一小时累积零误码才说明物理链路可靠。第五步再接CameraLink相机先看像素时钟、LVAL、FVAL有没有正常翻转然后看发送端FIFO深度是否稳定有没有一直涨满。第六步最后接接收端看显示或采集画面。调试最忌讳同时调发送端和接收端。两端一起出问题时你根本定位不了是光模块问题、GT配置问题、相机时序问题还是接收端重建问题。先固定一端把另一端用测试码流调通再调另一端。4.3 核心约束与硬件连接注意GT和普通逻辑在约束上的关键差异首先是引脚位置。GT收发器只能放在FPGA高速Bank的专用引脚上不能随便分配到任意普通IO。SFP模块的TX_P/TX_N、RX_P/RX_N必须对接GTX/Y的MGT引脚参考时钟必须接MGT Bank的MGTREFCLK专用引脚。差分对的正负方向也不能接反接反了会导致通道完全不通或者必须打开极性翻转。SFP管理信号里TX_DISABLE上电默认状态很关键。很多模块把TX_DISABLE设计成高电平关断输出如果板子上没有下拉电阻FPGA输出高阻光模块可能默认不发光链路自然起不来。调试时如果LOS一直无光先查TX_DISABLE有没有被拉低。如果板子上的SFP支持IIC管理建议在逻辑里写一个简单的IIC主读模块上电后读一下SFP A0地址里的模块信息确认模块类型、标称速率、厂商。这个操作本身不难但能在链路异常时快速排除“模块不支持这个速率”的问题。5. 调试中的坑常见问题与排查实录5.1 链路起不来五个检查点一个都不能少很多人debug第一步就乱了我这里给一套固定的检查顺序。检查项现象排查方法光模块供电与插接SFP的LOS一直高确认SFP座子焊接、供电换一个模块交叉测试TX_DISABLE电平光模块无光输出量TX_DISABLE引脚的FPGA输出电平正常应拉低MGT参考时钟channel_up不拉高GT复位信号反复触发示波器测MGTREFCLK频率和波形确认不是普通IO乱拉复位时序上电跑得通复位后跑不通使用Aurora自带的复位控制器确保pma_init完成后再释放resetGT线速率与模块不匹配有光但hard_err很多确认SFP模块速率档位模块标称速率要和GT线速率一致如果我只能给一条经验那就是先测参考时钟再看复位时序最后怀疑光模块。GT这东西对参考时钟的抖动非常敏感用普通CMOS时钟代替差分MGT时钟基本必挂。5.2 图像花屏、错位的定位方法花屏大概可以分成两类。一类是整帧乱掉、画面边缘错位大概率是帧不同步或者数据位映射反了。可以在接收端ILA里抓FIFO读侧数据先抓几个固定的测试图片比对数据值是24bit像素里的哪几位如果发现R和G通道互换或者高低字节颠倒去检查发送端的拼接顺序改一次就好。另一类是偶发花屏几帧正常然后跳一帧这类问题很多和FIFO溢出有关。发送端像素时钟85MHz时数据速率高而Aurora侧净荷不够FIFO写满就会丢数据。排查时在发送端加一个满标志计数器看它是不是在花屏瞬间被拉高。如果溢出了解决方向就是提高GT线速率、增加Aurora通道数或者降低相机像素时钟。没有其他捷径。还有一类隐蔽的问题来自跨时钟域。异步FIFO的读写时钟频率接近但不完全相等时FIFO空满标志在临界点会出现毛刺导致读写使能误动作。所以FIFO深度不能只留几十个bit至少要给两行图像数据深度的余量并且对空满信号做格雷码同步处理这样即使瞬时波动也可以靠深度吸收掉。5.3 SFP光模块兼容性经验最后聊光模块。做这个项目的人很容易忽略光模块本身的兼容性但实际上很多“FPGA代码没问题但链路不稳定”的案例都出在模块和光纤上。第一个经验850nm多模模块必须配多模光纤1310nm单模模块必须配单模光纤。混用的结果是链路指示灯可能亮但误码率极高跑一会儿就出现大量soft_err。第二双纤模块的TX和RX必须交叉连接用一根跳线做回环时要把模块的TX接到自己的RX具体看SFP型号有的模块是单芯双向有的不带光口回环功能只能通过外部光纤环回。第三工业级模块比商业级稳定得多工作温度范围宽在工业现场环境里长期跑多花几十块很值得。另外SFP的LOS信号极性在不同厂商模块之间是有差别的。有的模块LOS高表示光信号丢失有的模块低表示丢失。如果LOS接到了FPGA普通GPIO逻辑里直接判断可能会得到反逻辑的结果。建议读模块规格书或者用万用表量模块引脚再写逻辑或者直接在代码里做成可配置的极性等现场调。这个细节看着小实际现场调试时很容易让人绕弯路。最后再分享一点我个人的体会这套项目最有价值的地方其实不是光口本身而是CameraLink数据流经过FPGA后拥有了“可编程”的能力。别人用专用线缆看到的是一根固定的图像管道你用FPGA加光口等于在这条管道里装了一个可重构处理器。以后想加坏点校正、想裁ROI、想多相机时分复用都是在这个骨架上加代码的事。我自己的调试习惯是拿到新板子第一天先把回环工程跑通第二天再碰CameraLink前面链路稳了后面所有问题都有明确的排查边界。如果你是从零开始做类似的方案我也建议你按这个节奏来不要急着一口气把相机、光纤、主机全部怼上。链路一层一层打通整个工程就不会有玄学问题。