
做机器视觉项目的人应该都有同感项目推进到图像采集这一环最容易卡住的就是高速接口。前阵子我们接了一个500万像素、80fps黑白相机的采集需求粗略一算像素数据量要到3.2GbpsGigE和USB3都不踏实Camera Link Full自然成了首选方案。这篇文章就把这个项目从方案评估、硬件接口设计、FPGA逻辑实现到上板调试的完整过程记录下来重点讲透XILINX 7系列FPGA上LVDS解串、三路端口对齐、像素重组和DDR3写入这几个关键环节。如果你正在做或准备做工业相机采集板这篇文章应该能帮你少走不少弯路。1. 方案决策Full模式的带宽账到底怎么算1.1 Base、Medium、Full到底差在哪Camera Link是工业相机领域的老牌接口由AIA标准组织定义底层基于National Semiconductor现TI的Channel Link技术。很多人对Camera Link的印象停留在接口线多、连接器大但对于做采集板的人来说必须清楚它分为Base、Medium、Full三种配置本质区别就是用了几个Channel Link通道。每个Channel Link通道由4对LVDS数据线和1对LVDS时钟线组成。发送端把28位并行数据24位图像数据加FVAL、LVAL、DVALID、Spare这4个控制信号串行化在4对数据线上按7:1比例发出。Base模式用1个Channel LinkMedium模式用2个Full模式用3个。所以Full模式在连接器上看到的是12对LVDS数据线加3对LVDS时钟线总共15对差分线带宽天然是Base的三倍。1.2 先算清带宽再决定方案很多项目在接口选型时容易犯一个错误只看了相机的分辨率和帧率没有算像素时钟与接口有效带宽的匹配关系。Camera Link的LVDS时钟上限常见为85MHzFull模式3个端口同时传输时每个端口每时钟周期传24位有效图像数据单端口有效带宽85MHz × 24bit 2.04Gbps 255MB/sFull模式三端口总计3 × 255MB/s 765MB/s这是一个非常有用的参考数字。我们当时的相机是500万像素、80fps、8bit黑白像素数据量是500万 × 80 400MB/sFull模式的765MB/s刚好能盖住还留了接近一倍的余量。如果换成同样的参数但用Base模式255MB/s显然不够用Medium模式510MB/s也很紧张因为还要考虑行场消隐期间虽然不传有效数据但链路开销和DDR3读写切换都会吃掉一部分余量。这里给个简单的判断表格模式Channel Link数量LVDS数据线对85MHz下有效带宽典型适用场景Base14255MB/s200万以下、中低速Medium28510MB/s200万~400万、较高帧率Full312765MB/s500万以上、高帧率、高位深1.3 两条接收路线FPGA直接解串还是外置芯片确定Full模式之后接收端有两条路线。一条是用TI的DS90CR288A这类专用解串芯片把LVDS先变成28位并行TTL信号再进FPGA优点是时序压力小、调试简单缺点是板级多了一颗芯片、物料成本高、灵活性差。另一条是直接利用FPGA的ISERDESE2原语做7:1解串省掉解串芯片所有控制权都在FPGA内部。我最终选了FPGA内部解串。原因有三个一是这个项目后续要兼容不同相机的像素映射FPGA内部解串改逻辑就能应对二是XILINX 7系列的ISERDESE2本身就支持SDR模式7:1解串属于官方支持的能力三是省掉两颗芯片后板卡面积和成本都明显下降。缺点也很明显就是调试初期要跟位对齐、眼图较劲这部分下文会详细讲。2. 硬件接口设计连接器、引脚规划与信号完整性2.1 资源评估与引脚规划Full模式要求FPGA提供15对LVDS差分输入12对数据加3对时钟这个数量对于Artix-7或Zynq-7000系列来说完全不是问题。但真正的坑在于引脚所在的Bank必须支持LVDS_25电平标准并且尽可能放在同一个Bank或者同一个时钟区域内。我当时用的是XC7A200T把三组Channel Link的15对差分信号全部安排在一个HP Bank的相邻引脚上。这样做的目的是让各lane的输入延迟尽量一致减少后续在FPGA内部做deskew的压力。规划引脚时建议打开Vivado的Device视图逐个Bank查看LVDS引脚对是否合法。7系列FPGA的引脚手册里会标注某些Bank的某些引脚不支持LVDS或者只能支持LVDS_25而不能支持LVDS_15这些信息在布板前必须确认好。还有一个容易忽略的问题Camera Link的LVDS差分时钟对也要走IBUFDS而IBUFDS输出的时钟要进入BUFIO或者BUFG才能驱动ISERDESE2。如果你把三组时钟放在同一个Bank但不同时钟区域逻辑设计阶段的时钟约束会比较难受。我的建议是尽量让三组时钟对落在同一个时钟区域哪怕牺牲一点布线空间也值得。2.2 高速LVDS布局布线与端接Camera Link Full模式的数据率并不算特别高。以85MHz像素时钟为例每条LVDS数据线上的串行速率为85MHz × 7 595Mbps这个速率对PCB布线来说处于需要注意但还没到不择手段的程度。关键点是同一端口内部的4对数据线加1对时钟线要做到严格等长。按照TI和AIA规范的建议同一Channel Link端口内部的差分对长度差控制在±5mil以内差分对内部的P/N两线长度差控制在±2mil以内。三个端口之间的长度差控制在±250ps以内换算成PCB走线大概是±25mil左右。如果端口间长度差太大后面做三路端口对齐时会增加不少额外逻辑。LVDS差分信号必须有100Ω终端电阻。在XILINX 7系列里IBUFDS本身可以打开内部差分终端也就是设置DIFF_TERM TRUE这样外部可以省掉端接电阻。我建议在原理图上预留外部端接电阻的位置前期调试先用内部端接如果发现信号质量不理想再焊上外部电阻对比测试。2.3 连接器选型和复位信号处理Camera Link物理连接器最常见的三种MDR26老式D型、SDR26小型高密度、HDR26更小型。Base模式只需要1个Medium需要2个Full需要3个。很多工业相机使用SDR26接口采集板这边我选了MDR26的插座配合标准Camera Link线缆。选连接器时注意确认线缆是标准Camera Link线缆还是PoCL供电线缆如果相机是PoCL供电采集板还需要提供12V电源到连接器别让供电问题卡住整个调试。还有一个基础但重要的问题复位信号。FPGA上电后LVDS解串状态机、FIFO读写指针、DDR3控制器都用各自的复位逻辑。如果复位信号直接由按键或者外部电平产生很容易出现复位释放时刻与LVDS时钟沿对齐不佳导致的亚稳态问题。我的做法是把外部复位先做两级同步后再统一生成各模块的同步复位释放信号。这个细节看起来不起眼实际调试中因为复位时序不对导致解串偶发错位的情况并不少见。3. FPGA内部解串ISERDESE2的7倍频采样与位对齐3.1 用MMCM生成7倍频采样时钟Camera Link接收端解串的关键是要为每个Channel Link端口生成一个7倍频的采样时钟。以85MHz像素时钟为例需要把85MHz倍频到595MHz用这个高频时钟去采样4条LVDS数据线每采7个bit拼成一位并行数据。具体实现上LVDS时钟线进来之后经过IBUFDS转成单端时钟然后进入MMCM/PLL。MMCM的CLKOUT0配置成7倍频即595MHzCLKOUT1保留85MHz作为并行数据输出时钟。这里的细节是595MHz采样时钟建议通过BUFG进入全局时钟网络再连接到每个ISERDESE2的CLK端口85MHz作为CLKDIV连接到ISERDESE2的CLKDIV端口。这样ISERDESE2内部会每隔7个采样时钟锁存一次并行数据。使用MMCM时还要注意595MHz对于Artix-7的全局时钟网络来说属于偏高频率但实际项目中跑下来没有问题。如果条件允许把像素时钟和7倍频时钟的相位关系用约束固定一下Vivado综合时会更听话。3.2 ISERDESE2配置的实战细节7系列FPGA的ISERDESE2是一个通用串行解串器官方文档UG471里明确支持SDR模式下的7:1解串这正好匹配Channel Link的物理格式。我自己的例化核心参数大概是这样的ISERDESE2 #( .DATA_RATE (SDR), .DATA_WIDTH (7), .INTERFACE_TYPE (NETWORKING), .NUM_CE (1), .INIT_Q1 (1b0), .INIT_Q2 (1b0), .INIT_Q3 (1b0), .INIT_Q4 (1b0) ) u_iserdes_lane ( .CLK (clk_595m), .CLKB (~clk_595m), .CLKDIV (clk_85m), .RST (rst_serdes), .CE1 (1b1), .CE2 (1b1), .D (data_from_ibufds), .BITSLIP (bitslip_lane), .Q1 (q_out[0]), .Q2 (q_out[1]), .Q3 (q_out[2]), .Q4 (q_out[3]), .Q5 (q_out[4]), .Q6 (q_out[5]), .Q7 (q_out[6]) );DATA_WIDTH设为7是关键这也是很多第一次接触Camera Link的人容易卡住的地方。因为ISERDESE2还支持4、6、8等宽度如果按习惯填了8解出来的数据位完全对不上。每个端口有4条数据lane就需要例化4个ISERDESE2。三组端口加起来就是12个ISERDESE2每个输出7位总共84位解串数据其中72位是图像数据12位是控制信号和保留位。3.3 位对齐的两种校准手段IDELAY和BITSLIP解串器把串行bit流变成并行数据后最头疼的问题就是字边界不确定。Channel Link协议没有像PCIe那样完善的内建训练序列所以必须自己做位对齐。我的做法分两步。第一步用IDELAYE2原语调节每条lane的输入延迟把采样点挪到眼图中心。IDELAYE2在7系列HP Bank下大约每级78ps一共32级。调试时写一个简单的状态机把IDELAY的tap值从0扫描到31观察每个tap下ILA抓到的数据是否稳定、是否有误码找出一段比较宽的稳定区间后取中间值。第二步用ISERDESE2的BITSLIP功能做字对齐。BITSLIP每拉一个高脉冲解串输出会循环移位一位。通过bitslip把FVALU/LVAL/DVALID这三个信号的位置调整到正确组合。典型现象是如果字边界错了一位LVAL可能出现在FVAL下降沿之后或者DVALID在LVAL低电平期间一直为高这都说明需要继续slipt。4. 三路端口对齐与像素重组从72bit总线到DDR34.1 三个端口为什么会失步理论上Full模式下三个端口在相机端由同一个像素时钟驱动到达FPGA时频率完全一致。但由于线缆长度、PCB走线、连接器接触等因素三个端口的LVDS时钟和数据到达FPGA的相位会有几百皮秒到几纳秒的差异。这个固定相位差如果不处理三个端口解出来的像素数据会错开一个或几个像素周期图像就会出现严重的横向撕裂。处理思路是先用每个端口自己的LVDS时钟完成解串得到各自时钟域的并行数据。然后以一个端口为基准检测三端口FVAL和LVAL的上升沿相位差通过打拍的方式把另外两个端口的有效数据延迟到与基准端口对齐。最简单有效的实现是每一级打一个像素时钟的延迟配合状态机比较三路LVAL的相对关系最多延迟几个周期就能对齐。注意不要试图用全局时钟重新采样已解串的数据那样会引入额外的跨时钟域问题。4.2 Pixel Mapping重组规则对齐之后输入到重组逻辑的是一份72位的多端口像素数据。以我们项目里8bit 3 tap的相机为例一个像素时钟周期内三个端口分别输出3个8bit像素。也就是说一个时钟周期里共输出9个像素。相机手册里的Pixel Mapping表会明确指出A口、B口、C口分别对应图像中哪一列、哪一个tap。重组逻辑要做的就是把这三个端口的数据按相机定义的顺序拼接成连续的像素流。这段逻辑本身不难难在相机的映射规则偶尔会和你默认的拼接顺序不一样。我建议在调试阶段先让相机输出test pattern然后在ILA里和相机的测试图像逐一对照确认重组顺序正确后再对接真实图像。如果不先做这一步后面FIFO里存进去的像素顺序就是错的到时候查问题会非常痛苦。4.3 DDR3写入通道与FIFO深度估算像素流重组完成之后需要写入DDR3做帧缓存。这个项目用的是XILINX MIG生成的DDR3控制器数据带宽完全不是瓶颈。以400MB/s的写入速率为例DDR3 16bit 800MHz的理论带宽约3.2GB/s写入占比不到13%。真正的坑在跨时钟域和FIFO深度。像素流时钟域是85MHz或重组后的等效时钟DDR3控制器的UI时钟通常是400MHz或更高之间必须要用异步FIFO衔接。FIFO深度的经验值建议至少能容纳两行像素再加一些突发切换余量。比如一行按4096像素计算8bit下就是4KB两行8KB为了避免频繁出现空满导致DDR3带宽利用率下降至少留16KB深度。我当时用了16384的FIFO深度实际占用率大概在60%左右没有出现溢出。5. 相机控制链路SerTC/SerTF下发参数背后的逻辑5.1 SerTC/SerTF的电气与协议Camera Link除了图像数据通道还定义了两条串行通信线SerTC板卡发给相机和SerTF相机返回板卡。电气上是LVDS差分对但协议本质就是通用UART。默认波特率常见的是9600也有相机支持115200或更高具体要看相机手册。FPGA里实现起来不复杂用IBUFDS把SerTF的差分信号转成单端接一个UART_RX模块用OBUFDS把UART_TX模块的输出转成差分接到SerTC。很多采集板还会把这两条线通过PL侧的UART桥接到Zynq的PS端这样Linux应用层可以直接用串口工具下发命令省去在PL里写一堆寄存器。相机侧通常有一个固定的设备地址比如常见的0x0C。命令帧格式一般包含起始符、长度、地址、命令码、数据和校验和具体字段每个厂商定义不同。第一次做的时候务必找厂商要Camera Link串行命令手册不要凭空猜测帧格式。5.2 相机参数下发的工程实践实际项目中通过SerTC下发最多的命令就是设置曝光时间、触发模式、增益和test pattern。调试阶段尤其离不开test pattern在确保图像通道还没完全调通之前先让相机输出内部彩条或渐变图可以隔离相机端和接收端的问题。工程上我会把UART_TX、UART_RX、命令解析器打包成一个自定义AXI-Lite IP。这样做的好处是上层软件或者Zynq的PS端通过读写寄存器就能控制相机参数不用每次改逻辑都重新综合。把UART做成AXI-Lite IP完全是常规操作XILINX的Vivado里用Create and Package IP向导就能封装关键是把寄存器地址定义得清晰一些比如寄存器0配置曝光低16位寄存器1配置曝光高16位寄存器2配置触发模式。提一个实际调试中的坑SerTC/SerTF两条差分线的极性问题。Camera Link连接器的引脚定义里SerTC是有极性的线序接反会导致通信完全不通。上板前用万用表量一下连接器引脚定义比调半天代码发现是线序问题要划算得多。6. 上板调试全流程从单端口验证到全链路联调6.1 推荐的调通顺序很多工程师拿到板子习惯一口气把所有逻辑全跑起来然后对着花屏发愁。我的建议是严格按以下顺序分步验证第一步示波器看LVDS时钟和数据眼图。确认差分信号幅度、共模电压、终端电阻都正常。如果眼图张不开后面所有调试都是在碰运气。第二步只解串A端口其他端口先屏蔽。在ILA里抓A端口解出的7位数据和LVAL、FVAL、DVALID。先确认控制信号逻辑正确再切换相机test pattern看数据是不是固定的彩条序列。这一步能排除大部分解串配置问题。第三步A端口稳定之后再依次打开B端口、C端口每个端口单独做IDELAY扫描和BITSLIP对齐。不要试图三个端口一起调出问题根本分不清是哪一路的问题。第四步三路端口都单独稳定之后做LVAL上升沿对齐和像素重组。第五步把重组后的像素流写入DDR3然后从DDR3读回来在ILA里比对写入数据和读取数据是否一致。确认无误后再接HDMI显示或者上位机采集。6.2 实测结果与资源占用这个项目完整跑通后我用ILA和示波器做了一轮实测。500万黑白相机稳定工作在80fps采集链路无丢帧DDR3写入带宽占用约45%FPGA整体资源占用LUT约35%、FF约28%、BRAM约40%余量很大。解串链路在常温下连续跑了48小时没有出现偶发误码。资源上最大的开销是像素FIFO和DDR3的读写缓存如果用高分辨率高帧率相机BRAM占用会明显上升。建议在设计早期就把这些FIFO深度定好后期加缓存比换芯片痛苦得多。6.3 常见故障排查表故障现象可能原因处理方式DVALID一直为0BITSLIP未对齐字边界错误对该lane做BITSLIP扫描图像左右错位像素重组顺序错误对照相机Pixel Mapping表图像上下滚屏FVAL/LVAL对齐异常检查三路端口的帧同步延迟偶发性花点IDELAY tap不在眼图中心重新做tap扫描三路端口数据不对齐端口间走线长度差过大代码中增加多拍延迟对齐SerTC下发命令无响应串口极性接反或波特率错误检查差分极性核对相机手册最后分享一个从这次项目里沉淀下来的经验千万不要迷信先调试后优化对于Camera Link这种有多个串行通道的接口一定要把基础的对齐验证流程固化下来每次上板第一件事就是跑一遍单端口对齐。这个动作看起来费时间但能帮你从一团乱麻里快速确认问题边界反而比直接全链路联调省出好几天的调试时间。