100G光口/光模块的FPGA测试实例
这些年折腾FPGA高速接口,我踩过最多的坑基本都集中在光模块和光口这条链路上。尤其是刚到100G这个速率档位,很多人第一反应是“不就插个光模块跑个IBERT吗”,实际做起来才发现,链路两端各有一堆雷:光模块的误码率、无源线缆的损耗、FPGA收发器的CDR锁定能力、时钟抖动预算……任何一个环节出问题,最终都只表现为一个现象——误码。
这篇文章我把100G光口/光模块的FPGA测试完整捋一遍:从最基础的架构认知到IBERT快速验证,再到自研PRBS回环逻辑和误码统计实现,最后把高频故障排查思路一并分享出来。内容面向已经会用Vivado建工程、跑过GTX/GTY基础例程的工程师,也兼顾正准备入坑高速串行接口的新人。
1. 100G光口测试的核心架构与方案选型
1.1 100G光口到底测的是什么
100G光口不是一根线一根针就能搞定的,目前主流的100G以太网物理层方案是基于4×25G NRZ并行通道的CAUI-4接口,光模块端则对应QSFP28封装。MAC层(或逻辑层)通过一个40bit或者64bit的并行接口接到PCS层,再经过编码、加扰、串行化之后从FPGA的GTY/GTH收发器输出4对26.5625Gbaud的高速差分信号到光模块,最后由模块内部的电光转换电路把电信号变成4路25.78125Gbps的LANE WDMA光信号,在光纤上传输。
所以说,所谓“100G光口测试”,本质上测的就是FPGA侧收发器到光模块之间这4对高速电信号的完整性,再加上光模块本身电到光、光到电的转换链路质量。测试手段一般分三种:协议层误码测试(用Ethernet MAC + PCS4做全栈回环)、物理层误码测试(GTY直通PRBS回环)、以及眼图/浴盆曲线扫描(IBERT内建功能)。
很多工程师上手第一个遇到的问题是混淆了“链路误码”和“模块光功率劣化”。我见过测试报告里写“光模块接收光功率-8dBm,误码率1e-8”,实际上这往往是FPGA与模块之间的电连接质量不行,光模块自身反而没有责任。所以测试之前先定清楚:到底要测哪一段。
1.2 FPGA选型与GTY收发器的关键规格
做100G测试,FPGA的收发器必须支持至少25.78125Gbps的线速率,并且要有足够数量的高速收发通道。目前市面上能胜任的FPGA大致如下:
- Xilinx UltraScale+ VU9P/VU13P(GTY最高支持32.75Gbps)
- Xilinx Virtex UltraScale+ VU3P以上(GTY速率更高,可用于400G)
- Intel Agilex/Stratix 10(E-Tile可达58Gbps PAM4)
- 国产方面,复旦微、紫光同创高端器件目前主要在25G NRZ档位,50G PAM4还在追赶
这里要特别强调一个容易忽略的点:GTY收发器的参考时钟架构。100G CAUI-4要求4个收发器通道使用同一路156.25MHz参考时钟,而且这路时钟的抖动指标要求极其严格——一般要求不超过0.4ps RMS的随机抖动。FPGA开发板上的可编程时钟芯片(比如Si5345)能不能输出满足要求的超低抖动时钟,直接影响CDR锁定余量。
我建议的原理方案是:时钟芯片输出156.25MHz给GTY的REFCLK0,用Vivado的GT Wizard生成4通道收发器实例,线速率设定为25.78125G,编码为不编码直通模式(即RAW模式),接收端开启DFE(判决反馈均衡)和LPM(线性预加重),先用IBERT扫一遍眼图确认通道裕量,再进入自定义逻辑测试。
1.3 为什么要选用回环模式做首轮验证
光口的FPGA测试,第一步永远是回环模式,而不是对光。道理很简单:光模块一插上去,链路里新增了电-光-电转换、模块内的CDR/LA、光纤连接器等多个变数,一旦误码,你没法快速定位问题出在FPGA还是光器件。
回环分三层:
- 电层回环:光模块不插,在FPGA的GTY发送端直接内部回环到接收端(Near-End PCS Loopback),验证FPGA自身收发器和时钟
- 模块电回环:通过专用测试夹具或QSFP28回环模块,把TX端4路Lane的电信号从外部接到RX端,验证过孔、连接器焊盘这一段的信号完整性
- 全链路回环:两端都插上光模块,用一根短光纤(1m-3m)把TX和RX连起来,验证电+光的完整链路
我的习惯是三层逐级突破,每一层都跑到BER低于1e-15、至少跑10分钟以上无误码,才进入下一层。这能省下大量排查时间,而且后续出问题时能精确定位到是哪一段引入的劣化。
2. 100G光模块的接口与信号完整性要点
2.1 QSFP28引脚定义与低速管理接口
QSFP28模块的电气接口分为高速数据通道和低速管理通道。数据通道是4对TX差分对和4对RX差分对,每对速率25.78125Gbps,差分阻抗要求100Ω。管理通道包括I2C(SDA/SCL)、ModPrsL(模块在位指示)、ResetL(复位,低有效)、LPMode(低功耗模式使能)和IntL(中断输出)。
很多工程师第一次接QSFP28模块,容易漏掉ModPrsL的上拉电阻。这个信号是模块内部开漏输出,FPGA侧必须接1kΩ到2.2kΩ上拉到VCC(通常是3.3V),否则FPGA永远检测不到模块在位。I2C地址是A0h(0x50),用于读取模块的DOM信息(光功率、温度、电压等),同一总线上如果挂了多个模块,通过写A2h地址选择不同页。
实际项目中,通过I2C读取光模块的光功率来做链路预算分析几乎是必须的操作。我建议在FPGA逻辑里写一个简易I2C Master模块,不要依赖外部MCU,这样在系统上电后可以自动读取每个模块的RX光功率,实时监测链路状态。I2C速率用100kHz标准模式就足够,不需要走400kHz快速模式,长走线时慢速更稳。
2.2 高速差分走线的SI设计与连接器适配
100G光口的PCB走线是测试成败的关键基础。25Gbps信号的一个UI大约38.8ps,这个量级下,过孔残桩、连接器焊盘stub、走线阻抗不连续都会直接表现为接收眼图的开口缩小。几条关键经验供参考:
- 走线阻抗控制在85Ω±10%还是100Ω±10%,取决于光模块厂商和FPGA封装的联合仿真结果。QSFP28连接器本身是100Ω差分,但GTY引脚焊盘区域和AC耦合电容区域会产生阻抗跌落,建议该段走线按90Ω设计做折中
- AC耦合电容优先选择0402封装0.1uF,很多集成光模块或可插拔光模块规范里都要求靠近连接器端放置
- 内层走线要控制层叠,让差分走线参考面连续,跨分割绝对禁止
- 过孔尽量用背钻工艺,至少把Stub控制在10mil以内
- 连接器推荐选用Samtec QSFP-DD系列或者Molex的QSFP28,插拔寿命和RF性能都能保证
线缆和光模块测试时,尽量用短的光纤跳线(OS2单模,1米即可)。长纤会引入一定损耗,虽然是微量的,但在测FPGA本身工程裕量的时候没必要给自己增加变量。
2.3 时钟、复位与上电时序规划
100G测试工程对时钟和复位的依赖程度远超普通逻辑设计。系统上电之后,光模块的复位信号和FMC供电时序如果处理不好,模块可能处于异常状态,导致I2C无响应或者高速通道输出异常。
FPGA侧要主动控制时序:
- 上电后等待100ms让模块电源稳定,然后释放ResetL
- 释放ResetL后再等10ms,拉低LPMode进入正常功耗模式
- 通过ModPrsL确认模块在位后,再执行I2C读取操作
GTY收发器的复位时序则完全依赖Vivado生成的Reset IP。这里我建议不要自己拼复位逻辑,直接用GTTX_RX Reset Sequence IP,它内部有完整的TX/RX复位状态机,能保证先复位TX再复位RX,顺序不对会导致CDR锁定失败。
实际上最容易犯的错误是:把GTY的复位信号和用户的全局复位直接连在一起,结果每次系统复位的时候,GTY还没配置完,CDR还在锁定过程中,用户逻辑就开始收数据,导致大量CRC错误。正确的做法是:用户逻辑等待GTY的txresetdone和rxresetdone信号拉高后再启动数据收发。
3. 实操第一步:用IBERT快速评估收发通道裕量
3.1 创建IBERT工程的完整流程
Vivado自带的IBERT(Integrated Bit Error Ratio Tester)是评估100G光口物理层质量最高效的工具。创建步骤固定,不需要写一行逻辑代码,直接在Vivado里用IP Catalog生成即可。
具体操作路径:IP Catalog -> Search “IBERT” -> 选择“UltraScale+ GTY IBERT”,打开配置界面。关键配置项如下:
- Line Rate:设置为25.78125Gbps
- Reference Clock:选择156.25MHz,并确认是从哪个BANK的REFCLK引脚引入
- Quad选择:按实际PCB上QSFP28连接到的GTY Quad选择,比如Quad 129、Quad 130
- Protocol模板:选“Custom”而不是“Ethernet”,因为IBERT的PCS模板在100G下会引入额外开销
- 发射端TX Pre-Cursor/TX Post-Cursor:初始值给0,后续根据眼图扫描结果再调节
配置完成后直接Generate Bitstream,硬件上电后下载bit。打开Hardware Manager,点击IBERT链接图标,所有GTY通道会自动出现在扫描列表里。这里有个小技巧:Vivado的IBERT界面里可以直接看到每个通道的TX/RX Initialization状态,如果某个通道显示“Pending”或者“Lock Failed”,大概率是参考时钟或者复位时序问题。
3.2 眼图扫描与浴盆曲线怎么看
IBERT界面里进入“Scan”标签页,选择单通道或者全部通道执行眼图扫描(Horizontal/Vertical Bathub)。扫描完成后会生成一个矩形眼图,横轴是UI百分比,纵轴是电压mV,中心区域是干净的眼图开口。
判断通道裕量有几个经验性标准:
- 眼图开口水平方向不小于0.5UI,竖直方向不小于120mV,说明链路裕量充足
- 水平开口在0.3UI到0.5UI之间,需要优化TX预加重参数或检查FMC连接器接触情况
- 水平开口小于0.3UI,基本可以判定信号通路存在严重问题,优先排查PCB过孔、AC耦合电容焊盘、连接器引脚虚焊
浴盆曲线(BER Bathub Curve)更能直观反映误码率随采样点的变化趋势。曲线上1e-15误码位点左右两侧各有一段宽裕的平坦区间,如果平坦区间太小(比如小于0.2UI),说明抖动预算消耗殆尽,即使在短时测试中误码率为0,长期运行也很可能出现偶发误码。
在IBERT扫描的同时,同步调节发射端的pre-cursor和post-cursor值,观察眼图变化。25G NRZ信号通常pre-cursor取0到-2,post-cursor取2到5的范围内效果较好。注意每次修改完TX参数,RX端CDR需要重新锁定,IBERT工具会给几秒的等待时间,不要误判为死机。
3.3 关于PAM4的一个方向性提醒
100G速率也可以用PAM4调制方式(50Gbaud的PAM4信号承载100G流量),但标准QSFP28光模块几乎都走4×25G NRZ通道。如果你的项目目标是400G及以上,才需要接触PAM4和对应的FGPA收发器(比如UltraScale+的GTM或Agilex的E-Tile)。
我个人的建议是:在100G这个档位,先踏踏实实把NRZ链路的信号完整性做扎实。NRZ的调试经验和误码分析方法可以直接复用到PAM4,只是PAM4的误码平台从NRZ的1e-15会降低到1e-6左右,需要依赖RS-FEC(544,514)来纠错,这是后话了。
4. 实战进阶:自研PRBS回环误码统计逻辑
4.1 为什么IBERT不够用,还需要自定义逻辑
IBERT是物理层利器,但它最大的局限是只能测GTY收发器的裸通道,无法模拟真实业务流。实际应用中,光口上跑的往往是MAC帧、自定义协议包或者某种定长块,这些数据经过PCS层编解码后的比特形态和PRBS完全不同,误码特性也会有差异。
所以我在做完IBERT快速验证之后,习惯再写一套轻量级的PRBS逻辑,直接把GTY收发器接成直通模式,用PRBS31发生器灌数据,在接收端做同步和误码统计。这套逻辑虽然简单,但可以做到:
- 持续测试时间任意可控(跑几小时甚至几天)
- 实时记录误码总数、当前误码率、最近误码时间戳
- 和业务流量形成可对比的基线数据,方便后续业务性能分析
4.2 核心RTL结构拆解:PRBS31生成与校验
PRBS31多项式是X^31+X^28+1,生成方式是用31级移位寄存器做异或反馈。Vivado中可以直接调用LFSR IP核,但我更喜欢手写,因为手写便于控制位宽和并行化。
针对100G光口4通道,每个数据位宽64bit、时钟频率约402.8MHz(25.78125G / 64),这样一个高并行度的PRBS31逻辑不是简单的单比特移位寄存器能实现的,需要做并行展开。
并行PRBS31生成的核心原理是:根据当前时刻的31bit内部状态,通过组合逻辑计算出下一拍64bit输出后的新状态。这个过程本质上是矩阵乘法,可以用如下方式快速实现:
先独立维护一个31bit的PRBS状态寄存器(PRBS_State),每个时钟周期基于这个状态生成64bit的PRBS数据。实际做法是:把当前状态复制32份,每份做不同步数的串行递推,再拼成64bit输出。因为PRBS31的递推只涉及异或,32路并行递推的组合逻辑深度在4到5级以内,时序收敛完全没有压力。
接收端校验逻辑稍微复杂一点:需要先实现一个同步器,在连续的bit流中找到PRBS31的相位对齐点。常见策略是:检测到一个锁定码型(比如连续64个bit与预期PRBS值匹配)后,进入锁定状态,然后每个时钟周期比较当前64bit与本地重新生成的PRBS值,不匹配则误码计数加一。如果连续失配超过系统可容忍门限(比如64个周期),则认为失锁,重新进入搜索锁定状态。
需要高位宽并行PRBS生成代码,在Xilinx官网有application note(XAPP884)可以参考,那个是最权威的公开版本。如果不想费劲,也可以直接用Vivado里的PRBS Generator IP,但那个IP多用于IBERT,用户逻辑接口不友好。
4.3 实际测试流程与误码统计方法
硬件上电后,我习惯按以下顺序执行测试:
- 先下载IBERT bit,快速确认4个通道都能锁定、眼图裕量合格
- 下载自定义PRBS测试bit,用逻辑分析仪或者VIO确认PRBS同步状态机进入锁定态
- 开始自动误码统计,计数周期按1小时起步
- 每小时记录一次误码总数,通过VIO或UART上报上位机
误码统计的寄存器设计建议如下:
- 32bit误码计数器,累加到最大值后需要溢出标志,防止长时间测试数据无效
- 32bit总周期计数器,用于计算测试持续时间和误码率(BER = 误码数 / (总位数))
- 一组时间戳寄存器,记录最后一次误码发生的全局时间
- 一组错误间隔寄存器,记录连续两次误码之间的正常周期数量
如果实验条件允许,我强烈建议在这套逻辑里加一个温循测试:把板卡放进高低温箱,在-40℃和+85℃之间循环运行,同时监测误码率变化。100G光口的故障在常温下很难暴露,温度变化会放大焊点不良、时钟漂移和老化问题。以前我遇到过一个项目,常温跑48小时无误码,温度循环到75℃时误码率飙到1e-4,最后定位到是光模块座子相邻引脚间桥连导致的微小漏电,这是常温测试永远发现不了的问题。
5. 光模块的寄存器配置与常见“暗坑”
5.1 I2C寄存器读取与光功率诊断
QSFP28模块的I2C寄存器空间包含了上下电配置、中断标志、DOM监测数据等。在测试过程中,最有价值的是读取每个通道的RX光功率(单位0.1dBm)和温度、电压。
通用地址映射如下(各厂商稍有差异):
- 字节21: 温度(有符号定点数,8bit整数+8bit小数)
- 字节22: VCC电压(16bit固定点)
- 字节23: 通道1-4的RX光功率(每个通道2字节,单位0.1dBm)
- 字节26: TX偏置电流(每个通道2字节,单位2uA)
用FPGA实现一个I2C Master去轮询这些寄存器,每100ms采样一次,记录下来做曲线分析,就能快速判断光模块本身是否健康。有一个现象我印象特别深:某个通道RX光功率从-2.5dBm掉到-8dBm,全程比特误码率却没有显著变化,这是因为该通道的眼图裕量本来就很大。等到光功率跌破-12dBm,误码率才开始快速恶化。这说明“光模块功率下降到指标低谷但不一定立刻产生误码”是有预兆期的,趁这个窗口去检查光纤连接器、MPO端面污染,通常就能规避一次大批量返工。
5.2 模块误报在位的排查逻辑
ModPrsL信号误报在位的现象很隐蔽,我遇到过两种典型情况:
- 缺陷一:模块座子没插好,但ModPrsL被FPGA内部弱上拉误认为在位
- 缺陷二:两个模块共用一根I2C总线,A模块中断请求影响B模块的在位检测
排查方法很简单:打开Vivado Hardware Manager的I2C扫描功能,读取模块的0x00寄存器(模块标识符),如果读到的值不是0x0D(表示QSFP28),或者在位状态和实际不一致,直接检查连接器机械接触和PCB上拉电阻。
100G测试环境里,QSFP28连接器的机械寿命大约在200次插拔左右,超过这个次数会出现触点氧化、接触电阻增大,进而导致信号完整性和I2C通信的间歇性故障。备一套备用连接器和正常模块,是测试台架上的必要配置。
5.3 光模块测试中的静电与清洁注意事项
光模块的LC/MPO接口端面是高度精密的光学面,污染是信号质量最大的隐形杀手。测试前务必用光纤显微镜检查端面是否符合IEC 61300-3-35标准,等级至少B级。不用的时候要盖好防尘帽。
静电防护同样不能被忽视。QSFP28模块支持热插拔,但FPGA单板上靠近模块插座的位置最好设计ESD保护器件。多次在测试中遇到过模块在插拔瞬间损坏的情况,表面现象是模块无法初始化,实际原因是FPGA和模块之间I2C引脚上没有ESD保护导致的闩锁效应。后加的压敏电阻或TVS管后问题才彻底解决。
6. 典型故障排查实录与避坑心得
6.1 故障一:CDR锁定失败但IBERT扫描正常
一台测试设备上,4个100G光口中有一个通道始终无法完成CDR锁定,但用IBERT扫描这个通道时,眼图正常,TX/RX都能初始化成功。
排查过程:先检查GTY接收端的参考时钟——CDR需要参考时钟来锁定频率,而IBERT扫描使用的是同一个时钟,可以排除。再检查这个通道对应的光模块,在另一个QSFP28座子上测试完全相同的光模块,正常,排除模块故障。
最后发现问题在PCB走线:这个通道的差分对走线经过了一个测试点(PAD),而这个测试点在高速线上产生了约0.35pF的寄生电容,导致眼图在特定pattern下失真变大,CDR在特定码型序列下会阶段性失锁。IBERT扫描时如果刚好用了PRBS7这种简单pattern,可能无法触发这个罕见码型。
解决办法是去掉这个测试点,或者用0Ω电阻预留回路替代测试点,避免在差分对线上直接用焊盘裸露。这一条经验后来也被我们写进了DFM设计规范。
6.2 故障二:误码率稳定在1e-10但始终降不下去
误码率一直稳定在1e-10左右,无论怎么调TX预加重、无论怎么散热,就是降不到1e-12以下。这种现象非常典型,原因通常是接收端采样时刻的确定性抖动过大,而来源是GTY收发器的RX端CDR追踪能力不足,原始原因是参考时钟的相位噪声不够好。
用频谱仪测试参考时钟的相位噪声后发现,100Hz偏移处的噪声基底比规格书要求高出约15dB。把参考时钟源从普通锁相环芯片换成专用的低抖动时钟芯片后,误码率直接掉到0(48小时测试窗口下)。
另一个隐藏干扰源是FPGA内部其他高速逻辑从GTY的参考时钟域借用了同一路时钟做逻辑处理,引入了大量同步开关噪声。建议GTY参考时钟在PCB上独立电源域供电,与FPGA逻辑电源之间多加一个磁珠隔离。
6.3 故障三:两个通道交叉产生周期性误码
项目测试中遇到的另一个很有代表性的故障是:光模块1的TX2与光模块1的RX2之间不到1cm的走线间距,产生了严重的串扰,当两个通道同时处于满幅输出时,串扰导致的误码率是单通道工作时的8倍。
排查方式是用IBERT只开单通道,误码率极低;4通道同时打开,误码率恶化到1e-9。这个现象基本可以断定为串扰源。
解决思路:在FPGA逻辑里调整TX端的预加重参数,减小输出摆幅;PCB上给相邻通道之间加一排接地过孔屏蔽;布线时错开相邻通道的走线位置,避免平行走线过长。
6.4 故障四:短光纤连接却出现高误码率
用1m短光纤连接两端光模块,误码率却高达1e-4,完全不能接受。初期怀疑光模块本身,更换多只模块无法解决。
后来检查实际是光纤跳线的问题——用的是多模光纤跳线,而光模块是单模模块,波长1310nm的光在多模光纤里形成了高阶模干扰,导致接收端灵敏度急剧劣化。这是新手最容易犯的低级错误,但也是最容易忽视的。
另外一个可能的坑是光纤连接的插入损耗超标。用光功率计测量光纤跳线两端的损耗,如果超过1dB,就要检查光纤端面是否污染,用专用清洁工具擦拭后再测。
7. 关于100G光口FPGA测试的个人体会
从IBERT到自定义PRBS,从眼图扫描到温度循环,100G光口的FPGA测试这条路,走完一遍之后最大的体会是:高速信号没有侥幸。每一个误码背后都是一个真实的物理原因,只要花时间逐层剥离,一定能找到水落石出的位置。
最后再分享一个很实用的小技巧:在正式测试开始前,给每个光模块和每根光纤跳线编上唯一编号,测试记录里把所有链路信息(光模块序列号、光纤编号、PCB版本、FPGA温度)都记录下来。看似繁琐,真到定位问题时就能体会到作用了。尤其当你需要对比两个“看起来完全相同”的实验时,编号可以帮你排查到究竟是模块批次差异还是线缆损耗差异。
100G光口的测试能力,本质上是高速收发器、信号完整性、光电器件三个领域知识的总和。能把这三方面串起来,今后处理200G、400G乃至800G链路,方法路径也基本相通。这套底层能力是真实可迁移的,值得花时间打牢。