做FPGA图像传输的同行应该都有过这种经历:相机端好不容易把CameraLink图像信号接到FPGA,结果板卡之间只能隔几十厘米,线一长就丢帧花屏,工业现场电磁干扰一来更是心力交瘁。我最近完成的一套方案就是专门解决这个问题的——用FPGA把CameraLink图像数据转成光纤收发,链路基于Xilinx GT Transceivers Wizard和Aurora 8B10B协议,一发一收两端都打通,光口用标准SFP模块。整套方案一共适配了4套工程源码,覆盖Artix-7、Kintex-7、Spartan-7和Zynq四种常见平台。这篇文章就把完整的设计思路、IP配置、代码结构、上板调试和踩坑记录全部分享出来,给正在做FPGA光口收发或者CameraLink采集传输的朋友一个可直接参考的范本。
1. 项目背景与方案选型:为什么CameraLink非要转光口
1.1 应用场景与痛点
CameraLink接口在工业相机里非常常见,尤其是线阵相机和高分辨率面阵相机,靠的是LVDS差分信号的高速传输能力。但它的一个天生短板就是传输距离——标准的CameraLink线缆,Base配置在85MHz像素时钟下,实际可靠传输距离通常只有3到10米,线缆越粗越硬,布线越费劲。到了产线上,相机和控制端往往隔着十几米甚至几十米,光靠CameraLink线根本拉不过去,更别说有些环境还有大功率电机、变频器之类的高干扰源。
把CameraLink转成光口,本质上是把电信号换成光信号,一次性解决距离和抗干扰两个问题。光模块和光纤的成本这几年降得很快,一个千兆SFP光模块加几米光纤的价格,已经和一根像样的CameraLink线差不多了,但传输距离直接拉到几百上千米,电隔离天然就有,不用再做额外的光电隔离电路。
1.2 方案架构对比与技术选型逻辑
做CameraLink转光口,市面上有两条路线。
第一条是用专用转换器,比如某些工业相机厂家的光口转接盒,买来即用,不用写代码。但这类产品的问题很明显:价格高、协议封闭、带宽固定,没法根据自己相机的像素时钟和图像格式做优化,更没法在FPGA里顺便做图像预处理。
第二条就是我采用的方案——用FPGA做协议转换。CameraLink解出来的并行图像数据进FPGA,FPGA内部做跨时钟域缓存,再用Xilinx的GT Transceivers Wizard生成高速串行收发通道,配合Aurora 8B10B协议把数据打包发到SFP光模块,光纤对端再用一块FPGA接收并还原成CameraLink信号。这个方案的灵活度最高,带宽可以自己定,后续想加图像处理、想做多路复用、想改成UDP协议走以太网,都在同一片FPGA里就能扩展。
选Aurora 8B10B而不是自己写高速串行协议,原因很简单:Aurora是Xilinx免费提供的点对点传输协议,自带8B10B编解码、时钟修正、通道对齐和链路初始化握手,用户只需要处理AXI4-Stream接口上的数据流。自己从零写一个GT的收发逻辑不是不行,但要处理K码对齐、误码统计、链路训练、跨时钟域恢复这些东西,工作量至少翻三倍,而且稳定性很难保证。Aurora在工业场合用得非常多,相关资料也全,出问题好排查。
2. 核心硬件设计:接口芯片、FPGA与SFP光模块
2.1 CameraLink信号接入与解串方案
CameraLink Base配置在物理层上是4对数据LVDS加1对像素时钟LVDS,每对数据线以像素时钟7倍的速度串行传输,解串之后得到28位并行信号,其中24位是图像数据,4位是FVAL、LVAL、DVAL、SPARE同步控制位。
解串这一步有两条路:
一是用专用解串芯片,最常见的是TI的DS90CR288A。芯片把4对LVDS数据直接解成28位并行TTL信号和像素时钟,FPGA这边只需要用普通IO接收并行数据和时钟即可,逻辑简单,时序压力小,上板几乎不用调。这也是目前绝大多数CameraLink采集卡的做法。
二是省掉解串芯片,把CameraLink LVDS直接接FPGA的差分IO,用内部的ISERDES原语做7:1解串。这个方案省了一颗芯片的钱,但时序约束和调试难度明显上升,特别是像素时钟超过60MHz之后,约束没做好就容易采样出错,而且不同型号FPGA的ISERDES级联方式还不一样,代码移植麻烦。
我的4套工程都用DS90CR288A方案,省心。如果你打算去掉解串芯片直接进FPGA,建议先把ISERDES的位同步和字同步逻辑调稳了再谈后续,否则图像出来就是花的,你还分不清是链路问题还是解串问题。
2.2 FPGA平台选择与GT通道分配
4套工程我分别基于Artix-7、Kintex-7、Spartan-7和Zynq-7000开发,覆盖了市面上最常见的几类平台。
选型逻辑是这样的:
- Artix-7(比如XC7A35T、XC7A200T)是主流选择,GT资源够用,性价比高,绝大多数项目选它没错。
- Kintex-7(XC7K325T)适合需要高线速率、多通道并用、或者在FPGA里同时做较大规模图像处理的场景,GTX的线速率上限更高。
- Spartan-7(XC7S25、XC7S50)适合成本敏感的产品,只要GT线速率要求不高、逻辑资源需求不大就够用,但要特别注意Spartan-7不是所有型号都有GT,选型前必须查Pinout文档确认。
- Zynq-7000则是把ARM核和FPGA资源放在一起,适合需要Linux、网络协议栈或者上层控制界面的场景,前端的采集逻辑跟纯FPGA一样写。
GT通道分配主要看SFP的位置。一般在7系列FPGA上,SFP光模块的TX和RX差分对建议直连同一个GT Quad的MGTY引脚,不要跨Quad绕线,否则PCB布线和引脚约束都会很难受。GT参考时钟也要就近选,通常每4个GT共用一个MGTREFCLK引脚,我习惯把125MHz参考时钟接在对应Quad的REFCLK0上。
2.3 SFP光模块驱动与电源设计要点
SFP模块的电气接口不复杂——高速数据线是LVPECL电平的差分对,需要与GT引脚之间加交流耦合电容,一般板上放100nF就行;控制信号包括TX_DIS(发送使能)、TX_FAULT(发送故障)、LOS(信号丢失)、MOD_DEF0/1/2(I2C管理接口)。
有几个实际中容易踩的坑:
- SFP的3.3V供电要干净,我吃过亏的地方是光模块电源纹波大导致GT误码率升高,后来在模块供电入口加了磁珠和去耦电容才稳定下来。
- TX_DIS这个脚一定要拉高或者拉低到一个确定状态,悬空会导致模块工作状态不稳定,链路时好时坏。
- 光模块的I2C管理接口(MOD_DEF1和MOD_DEF2)建议接到FPGA的普通IO上,上电后可以读SFP的型号、温度、光功率、供电电压等参数,调试光口的时候这些信息非常救命。
- 没有插光纤时LOS信号会拉高,这个信号在FPGA侧最好引到逻辑里做一个状态指示,不要只靠灯。
3. GT Transceivers Wizard与Aurora 8B10B协议详解
3.1 Aurora 8B10B协议基础与帧格式
Aurora 8B10B是Xilinx提供的免费轻量级高速串行链路协议,它解决的问题可以理解成:GT收发器只是给你一条能跑高速数据的“水管”,但水管里流的是什么、怎么判断数据从哪开始、时钟漂移怎么修正,这些规则都要你自己定,而Aurora把这套规则提前写好封装好了。
协议的核心是8B10B编解码:每8位用户数据编码成10位线上数据,多出来的2位用于保证直流平衡和提供足够的跳变沿,方便接收端恢复时钟。这意味着线速率和实际有效数据带宽之间有个固定比例关系,线速率除以1.25才是真实数据带宽。比如3.125G线速率,有效带宽约为2.5Gbps,再扣除Aurora协议本身的控制开销,用户可用带宽大约在2.3到2.4Gbps。
Aurora 8B10B还内置了链路初始化握手、通道对齐(多通道时)、时钟修正(CC序列)、热插拔检测等功能。关键是它提供两种用户接口模式:Framing和Streaming。Framing模式需要用户自己定义帧格式,接口是AXI4-Stream加帧起始信号;Streaming模式更简单,数据进去什么顺序,出来就是什么顺序,适合我们这种纯图像数据透传的场景。4套工程里我全部用的是Streaming模式。
3.2 GT Transceivers Wizard关键参数配置
GT Transceivers Wizard是Vivado里配置GTX/GTH收发器的IP核。很多人一打开这个界面就懵,里面参数一大堆。实际跟Aurora配合使用时,核心参数就几个:
- GT Selection选择对应的GT通道,7系列一般选GTXE2_CHANNEL。
- Protocol Selection选Aurora 8B10B,会自动切换到配套模式。
- Line Rate我用的3.125G,因为CameraLink Base在85MHz像素时钟下的数据量是24bit乘85MHz约2.04Gbps,Aurora有效带宽扣掉开销后略大于这个值,3.125G正好合适。如果你相机像素时钟不到62.5MHz,也就是数据量在1.5Gbps以下,可以降到2.5G线速率,功耗会低一些。
- Reference Clock选125MHz,这个是从板子上GT参考时钟引脚直接进来的。
- TX/RX的字节宽度和数据宽度保持默认即可,但要注意TXUSRCLK和RXUSRCLK的频率会自动按线速率和位宽计算出来,配置完成后在IP的时钟信息里能看到。
一个重要细节:GT的TX和RX最好都使能,即使你暂时只做单向传输。因为Aurora链路初始化和通道对齐需要双向握手,只开TX或者只开RX,链路是起不来的。
3.3 Aurora 8B10B IP配置与用户接口时序
Aurora 8B10B IP本身是独立于GT Wizard的另一个IP核,在Vivado的IP Catalog里搜Aurora就能找到。配置时需要注意:
- Lane Count选1,对应一个SFP光模块的单通道传输。
- Line Rate要和GT Wizard里保持一致,3.125G。
- Dataflow Mode选Duplex,理由上面说了,链路握手需要双向。
- Interface选Streaming,图像数据不需要帧结构的话这个最省事。
- 生成IP后,顶层模块会自动实例化一个GT Wrapper,你不需要直接面对GT Wizard的接口。
Aurora IP的用户侧接口是一套AXI4-Stream。发送方向是s_axi_tx_tvalid和s_axi_tx_tready握手,握手有效时s_axi_tx_tdata上的数据会被发送出去;接收方向是m_axi_rx_tvalid、m_axi_rx_tdata。熟练这套握手时序非常关键,我见过很多新手在这里犯错——不关心tready信号就往上丢数据,结果链路明明通了,画面却是碎块。
Aurora IP还输出channel_up和lane_up两个状态信号,channel_up拉高代表整个Aurora链路已经建立,这时候才能开始传用户数据;lane_up只代表物理通道就绪。上板调试时我习惯把channel_up引出来接LED,一眼就知道链路状态。
4. 工程源码架构设计与数据通路
4.1 4套工程的差异化设计
标题里说的4套工程源码,实际是针对不同平台和不同速率需求做的适配版本,模块核心逻辑完全通用,只是引脚约束、IP配置和部分时钟资源不同。
第一套基于Artix-7 XC7A200T,CameraLink Base输入,3.125G SFP光口,这是标准方案,适用面最广。第二套基于Kintex-7 XC7K325T,同样是Base输入,但把光口线速率提到5G,留足了带宽余量,给以后图像分辨率或帧率升级留后路。第三套基于Spartan-7 XC7S50,逻辑资源更少,适合只做简单透传、不看复杂图像处理的产品。第四套基于Zynq-7000 XC7Z020,把采集逻辑和ARM软硬件协同放在一起,适合需要网口、Linux界面或者远程配置的场景。
四套工程的顶层结构一致:CameraLink接收模块、异步FIFO、Aurora封装发送模块、Aurora接收解包模块、寄存器控制模块。这样设计的好处是,你要换平台时,只需要改引脚约束和重新生成GT/Aurora IP,业务逻辑代码一行不用动。
4.2 模块划分与代码结构
整个工程的核心模块我分为这四块:
CameraLink接收模块的功能是把DS90CR288A解出来的28位并行数据和像素时钟采样进来,从LVAL、FVAL、DVAL的时序里判断当前有没有有效图像数据,把24位图像数据拼接成自己定义的行格式,写入下游FIFO。
异步FIFO模块负责跨时钟域,写侧是CameraLink像素时钟域,读侧是Aurora用户时钟域。FIFO深度我一般做到4096乘32位,按85MHz像素时钟算,这能缓存几百行数据,足够应对突发流量和Aurora链路偶尔的背压。
Aurora发送封装模块把FIFO读出的图像数据组织成Aurora Streaming接口的数据流,同时加上包头包尾标记。因为要区分每一帧图像的开头和结尾,我在数据流里插入自定义的帧起始码和帧结束码,接收端根据这两个码来恢复行场同步信息。
Aurora接收解包模块在光口对端FPGA里,负责把Aurora接收接口的数据按帧起始码和结束码解出来,再生成CameraLink TX侧需要的LVAL/FVAL/DVAL信号,拼成28位并行数据送给DS90CR287串行芯片,最终驱动显示器或者图像采集卡。
4.3 跨时钟域处理与数据打包
这大概是整篇里最容易翻车的地方。CameraLink像素时钟是随机的,可能在28MHz到85MHz之间任意取值,而Aurora用户时钟是GT线速率反推出来的固定频率,两者完全异步。直接用同步设计去处理必然导致亚稳态,图像花屏、偶发丢行都是这么来的。
异步FIFO是最稳妥的方案。但要注意FIFO读侧的带宽必须大于写侧。工程里3.125G线速率配32位用户接口,Aurora用户时钟是78.125MHz,读带宽约2.5Gbps,大于CameraLink Base的最大数据量2.04Gbps,所以正常情况下FIFO不会因为读不赢而堆积。如果FIFO的ProgFull一直拉高,先查是不是Aurora用户时钟频率不对,再查是不是发送端握手时序不规范导致Aurora背压。
数据打包我用的是一行一包的方式:每来一行有效图像数据,先发一个32位的行头,包含行号和数据长度,然后连续发送这一行的像素数据,行尾加一个行尾标记。接收端按行头恢复行同步,按帧头恢复帧同步。这样做的好处是光链路哪怕偶尔丢一行,接收端还能按行头重新同步,不会整个画面乱掉。
5. 实操记录:约束、仿真与上板调试
5.1 引脚约束与时钟约束配置
工程里最容易卡住的就是引脚约束,尤其是GT引脚。
GT引脚不需要像普通IO那样用set_property PACKAGE_PIN指定,而是用locate约束,格式类似于set_property LOC GTP_CHANNEL_X0Y4。SFP的TX差分对接GT_TX引脚,RX差分对接GT_RX引脚。参考时钟引脚用set_property PACKAGE_PIN指定到MGTREFCLK引脚上,同时要加上set_property PULLUP和set_property PACKAGE_PIN等属性。
CPU侧容易漏掉的是差分对约束。SFP的TX和RX两对线要用set_property DIFF_TERM TRUE(如果板子上没有端接电阻)或者确保板上有端接。GTX引脚本身一般不需要DIFF_TERM,因为GT内部有端接,但旁路电阻要看板子设计。
时钟约束方面,除了50MHz板载晶振等常规时钟约束,还建议对GT参考时钟125MHz做一个主时钟约束。Vivado在综合AXI组和GT IP时通常会自动处理一部分,但手动加上约束能避免很多P&R阶段的时序告警。
5.2 联合仿真与时钟树测试
不要一上来就上板点灯。我在拿到新板子时有个固定流程:先用Vivado自带的仿真把所有业务逻辑跑一遍,重点看三组信号——解串后的像素时钟和LVAL/DVAL时序、异步FIFO的写读指针和ProgFull、Aurora发送接口的tvalid/tready握手。
仿真的麻烦在于GT和Aurora IP在仿真里也要跑初始化,耗时比较长。我一般把Aurora初始化时间跳过,直接在测试平台里手动拉高channel_up,只仿真后面的数据通路。当然这只适用于功能验证,上板之后还是要看真实的channel_up时序。
仿真没问题之后,上板第一步不是接相机,而是做回环测试。把一个SFP光模块的TX用一根光纤接到接收插槽(如果没有对端板卡,可以用SFP环回头),然后在FPGA里通过一个简单的计数器循环发送递增数,接收端检查数值是否连续。这一步确认了GT链路的数据完整性,也确认了Aurora握手正常。
5.3 上板调试流程与ILA观测
回环测试通过后,接上真实CameraLink相机,用ILA观察信号。我建议至少添加这几组探针:
FIFO的写侧有效信号和写数据、读侧有效信号和读数据、Aurora发送接口的tvalid和tready、Aurora接收接口的tvalid和channel_up。
最常见的调试现象是:ILA能看到CameraLink侧的行场信号正常,但Aurora发送接口的tvalid一直没拉高。这种问题八成是状态机卡住了,要么是等FIFO的ProgFull清零等不到,要么是Aurora的channel_up没拉高导致发送状态机不启动。对着ILA波形一步步跟状态机跳转,很快能定位。
另一种常见现象是接收端能收到数据,但图像偏移或者颜色通道互换。这种情况基本是DS90CR288输出的位序映射和DS90CR287输入端的位序没对齐。CameraLink的24位数据不是简单按RGB排列输出,每8位对应一个端口,端口内又有位序规则。处理办法是先发一幅纯色图像,比如全红、全绿、全蓝,然后逐个对照位映射关系修正过来的,这个坑我足足踩了一天才爬出来。
6. 常见问题与排查技巧实录
6.1 链路层问题速查
链路层的问题通常表现为channel_up不拉高或者灯闪烁。排查顺序我建议固定为:
先用IBERT测GT物理层。Vivado里有Integrated Bit Error Ratio Tester,把IBERT工程加载到板子上,它会自动扫描GT收发链路,直接显示出误码率。如果IBERT都报错,说明是硬件层问题——光模块没插好、光纤损坏、GT引脚接错、参考时钟不对。
如果IBERT正常但Aurora的channel_up起不来,重点查Aurora和GT Wizard两个IP的配置是否一致。线速率不一致是最容易犯的错误,GT Wizard里配了3.125G,Aurora IP里默认可能是2.5G,这对不上就永远握手不了。其次是GT参考时钟的来源要一致,IP里选的参考时钟是REFCLK0,实际板子上必须恰好接在那个Quad的REFCLK0引脚上。
6.2 数据层问题速查
链路起来之后图像不对,这是第二阶段的问题。
花屏和错行:先看接收端的行头恢复是否正确。如果行头对不上,说明光链路在传输中丢了不少数据,要么是Aurora用户时钟带宽不够导致FIFO溢出,要么是发送端没有严格按照tvalid/tready握手。还有一种情况是FIFO复位时序不对,读写侧复位释放的先后顺序有问题,导致FIFO状态混乱。
图像偏色:查CameraLink解串芯片的位映射,发纯色图逐个比对,确认RGB位顺序。
图像卡顿掉帧:观察接收端的帧头恢复逻辑。如果帧头偶尔丢失,十有八九是发送端把帧头写进了FIFO,而FIFO在跨时钟域过程中偶尔丢了一个数据。这种情况要查FIFO的复位和写使能逻辑,确保帧头和数据同进同出。
闪现错行:多半是Aurora链路在运行过程中发生过短暂重训练或者通道重对齐,导致个别周期数据异常。可以在接收端加一个CRC校验,一旦校验不过就重新同步帧头,避免错误蔓延到整个画面。
6.3 长稳运行的经验总结
最让我难受的一次是系统跑二十分钟后才开始丢帧,查了两天没头绪。最后发现问题是GT的TXUSRCLK时钟树在长时间运行后,由于电源电压轻微跌落导致眼图变差,偶发误码。处理办法是优化GT的TX差分电压摆幅和预加重参数,在GT Transceivers Wizard的TX选项卡里适当调高差分摆幅,再在板卡电源上补了几颗钽电容。从那以后我养成了一个习惯:每次上板之前先跑一个小时的Aurora回环误码统计再看结果,不要急着接相机。
长稳测试还有一个容易忽略的点:光模块温度。SFP模块在工作一段时间后会发热,如果PCB散热没做好,模块内部温度升高后发射光功率可能跌出范围,链路直接断开。建议代码里周期读取SFP管理接口的温度寄存器,记到日志里,如果温度超过85°C就要考虑加散热措施。
最后再分享一个调试小技巧:在Aurora空口数据里加一个固定的伪随机序列或者递增计数,接收端专门对这部分做比对和计数。这一块逻辑看起来多余,但在现场排查问题的时候,它能帮你一秒区分是“链路丢包”还是“图像逻辑丢帧”,省下来的时间远远超过写这块逻辑的时间。