做FPGA通信这么多年,以太网在我印象里一直是“FPGA加PHY芯片”的组合:外挂一颗RTL8211或者88E1512,走RGMII或者GMII,再接网口变压器和RJ45,一套下来PCB面积、物料成本都不小。最近在项目里把PHY芯片整个拿掉,直接用Xilinx的高速收发器(GTX/GTH)配合SFP光模块,用AXI 1G/2.5G Ethernet Subsystem来做MAC、PCS和PMA,跑千兆和2.5G光口UDP通信,效果出乎意料地好。这篇文章把整个方案从原理到配置、从UDP协议栈到调试方法完整梳理了一遍,21套工程源码也整理好了,适合正在做FPGA网络通信、想省PHY成本,或者准备上SFP光口做高速数据采集的工程师朋友。
这个方案的价值在于:不再需要外置PHY芯片和网络变压器,FPGA的GT高速收发器直接驱动SFP光模块,一套链路从MAC层到光口都是FPGA内部搞定。省掉的不仅是一颗芯片的成本,还有PCB差分走线的复杂度、上电时序的调试量,以及PHY寄存器配置这一堆麻烦事。我做下来最大的感受是,只要GT参考时钟稳定、IP核配置正确、UDP协议栈逻辑清晰,光口通信的稳定性和调试效率比传统电口方案高了不少。
1. 告别外部PHY:SFP光口直连FPGA的方案逻辑
1.1 传统以太网接口方案的组成与痛点
传统FPGA以太网方案里,FPGA负责MAC层,PHY芯片负责物理层,两者之间用GMII、RGMII或者SGMII连接。PHY芯片内部承担了PCS(物理编码子层)和PMA(物理介质附着层)的工作,比如8B/10B编码、时钟恢复、串并转换、线路驱动等。FPGA侧只要把MAC数据通过接口给PHY,PHY再通过网口变压器和RJ45插座发送到网线,或者在光口方案里通过PHY的LVPECL或CML接口驱动光模块。
这套方案的痛点非常明显。第一,PHY芯片本身不便宜,千兆PHY即便用量大也很难压到个位数人民币成本,工业级PHY更是价格感人。第二,PHY芯片需要独立的时钟、电源和配置接口,MDIO管理时序、复位时序、各个寄存器配置都是调试工作量。第三,GMII/RGMII是大量并行或双沿信号,PCB布局布线要严格控制等长和串扰,特别是RGMII在DDR模式下时序裕量很紧张。第四,有些PHY芯片的功耗和发热也不小,对产品整体散热有压力。
所以只要条件允许,直接在FPGA内部把物理层也做掉,是一件非常划算的事情。而Xilinx的高速收发器本来就支持各种串行协议,从PCIe到SATA再到以太网,无非是物理层的PMA部分已经有了,PCS部分用IP核来实现即可。
1.2 1000BASE-X模式下PCS/PMA在哪里
要搞清楚怎么告别PHY芯片,先要理解以太网的不同物理接口标准。大家熟悉的GMII/RGMII是MAC到PHY之间的接口标准,PHY芯片还要继续处理线路侧的编码。而SGMII同样是MAC到外部PHY的接口,区别只是串行化。但在1000BASE-X标准里,MAC、PCS、PMA是完全集成在一颗器件内的,可以直接驱动光模块或者屏蔽双绞线。
Xilinx的AXI 1G/2.5G Ethernet Subsystem支持两种关键模式:SGMII模式和1000BASE-X模式。SGMII模式仍然假定外部有一颗SGMII PHY,适合扩展电口。而1000BASE-X模式下,整个PCS(包括8B/10B编解码、自动协商、扰码)和PMA(串并转换、时钟数据恢复、线路驱动)全部在FPGA内部完成,GT收发器直接输出差分信号给SFP光模块。这就是我们说的无PHY方案。
这里要强调一点:很多人看到“AXI Ethernet Subsystem”就以为它只是MAC核,其实它内部集成了完整的以太网MAC加上可选的PCS/PMA。选择1000BASE-X模式后,用户需要做的只是把GT的TX/RX差分对连到SFP座子上,然后通过光模块把电信号转成光信号。SFP光模块的电气接口标准是MSA定义的,低频管理引脚如Mod_Def0/1/2、TX_Disable、LOS都是LVTTL电平,但高频数据引脚TX+/TX-和RX+/RX-是CML电平,和FPGA GT收发器的接口电平完全兼容,不需要额外的电平转换芯片。
1.3 为什么这种场景一定要选UDP
既然已经打通了MAC和物理层,那么上层协议选型就摆在面前。在FPGA上做TCP/IP协议栈不是不行,但代价实在太高。TCP的状态机复杂,要处理三次握手、四次挥手、超时重传、滑动窗口、拥塞控制,纯逻辑实现占用资源大、调试周期长,而且性能和可靠性很难两全。如果只是做数据采集、图像传输、控制指令下发,UDP完全够用,而且延迟低、逻辑少、吞吐高。
我不是说FPGA上永远不用TCP。如果需求确实是可靠文件传输或者远程登录管理,那还是要考虑TCP,可以购买商业IP核或者用软核CPU跑协议栈。但绝大多数工业数据采集、实时控制系统、高速图像传输场景,UDP加应用层重传或者数据校验就能满足需求,CPU侧的软件协议栈本身也能处理一定程度的丢包。我们的工程就是在AXI Ethernet Subsystem之上自己实现了轻量级ARP、ICMP和UDP协议栈,支持上位机ping通、UDP收发、千兆和2.5G线速传输,这就够产品用的了。
2. AXI 1G/2.5G Ethernet Subsystem配置与接口设计
2.1 IP核关键参数配置:模式、速率、GT位置
在Vivado里创建AXI 1G/2.5G Ethernet Subsystem时,有好多配置项需要注意。先说最关键的几个。组件名称可以自己定义,但建议保持默认风格。物理接口那里,要选择1000BASE-X模式,这个选项会决定后面PCS/PMA是否集成在IP核里。如果误选了SGMII模式,IP核会输出SGMII信号给外部PHY,恰恰是我们想省掉的芯片。所以这一步特别关键。
速率配置,可以选择支持1G或者1G/2.5G自适应。如果你的板子是千兆光模块,选1G就行,资源会省一点;如果SFP座子后面可能插2.5G模块,就选1G/2.5G。数据接口选择AXI4-Stream,这是IP核推荐的接口,配合DMA或者自定义逻辑都很顺手。管理接口选择AXI4-Lite,用来读写IP核内部的寄存器,比如统计计数、链接状态、MDIO等。GT位置要手动指定,选择实际连接SFP座子的GT Quad和通道,这个必须和你的XDC约束一致。
IP核生成之后,顶层例化会有一个gt_ref_clk引脚,这个必须接125MHz的GT参考时钟(2.5G模式为156.25MHz)。这个时钟必须是高质量差分时钟,连接到专用的GTREFCLK引脚,不能用普通IO引脚或者内部PLL生成。GT参考时钟的抖动直接影响光口误码率,这一点在实物调试时深有体会,后面调试部分会详细讲。
2.2 125MHz参考时钟与SFP差分对接线
SFP座子到FPGA的接线比较简单,数据信号就4根:SFP_TX_P、SFP_TX_N接到GT的TXP/TXN,SFP_RX_P、SFP_RX_N接到GT的RXP/RXN。这里要注意,有些SFP座子的引脚命名是TD+/TD-、RD+/RD-,看板卡原理图时要仔细对照,不要接反。
控制信号方面,SFP的LOS(Loss of Signal)是输出脚,FPGA侧可以通过GPIO读取,LOS为高表示光信号丢失,链路无法建立,调试时非常有用。TX_Disable是输入脚,必须拉低,否则光模块发射端被禁用,链路建立不起来。Mod_Det是模块检测脚,一般通过上拉电阻接电源,插入模块后该脚电平变化,可以用于检测光模块是否在位。这些信号虽然不影响数据通路,但调试时能提供很多有效信息,强烈建议都接出来,做成状态指示灯或者寄存器位。
GT参考时钟的布线是重中之重。参考时钟必须连接在专用的GTREFCLK引脚上,如果板卡上SFP旁边的GT Quad没有对应的参考时钟输入,需要调整GT位置。我遇到过一块自研板,两个SFP分别接到了不同的GT Quad,结果有一个Quad没有125MHz差分时钟,只能从另一个Quad引过来,走了不少弯路。建议在设计阶段就把GT Quad和参考时钟规划清楚,一组GT Quad共享一个125MHz差分时钟,四通道完全够用。
2.3 用户侧AXI4-Stream总线怎么对接
AXI Ethernet Subsystem用户侧有两组AXI4-Stream接口:一组是发送(s_axi_tx),一组是接收(m_axi_rx)。每组接口基本上就是TVALID、TREADY、TDATA、TKEEP、TLAST、TUSER这几个信号。其中TDATA宽度默认是64位,同时也支持32位和8位配置。TLAST表示一帧的最后一个beat,TUSER[0]表示帧开始,这两个信号在帧处理逻辑中非常关键。
接收侧时序是IP核主动输出数据:m_axi_rx_tvalid拉高时,如果m_axi_rx_tready同时拉高,一个beat有效传输;TLAST拉高表示整帧结束。如果用户逻辑来不及接收,拉低TREADY即可,IP核会把数据停在总线上,不会丢数据。发送侧刚好反过来,用户逻辑拉高TVALID、准备好数据,等TREADY拉高时完成传输。
我建议在AXI Stream和自定义UDP协议栈之间加一级异步FIFO,一方面做跨时钟域,另一方面做速率匹配。因为UDP协议栈处理每一帧需要几个周期的判断时间,如果后级逻辑正在处理上一帧,而来的帧源源不断,没有FIFO就会丢。接收侧用Xilinx的axis_data_fifo,深度根据最大帧长配置,至少1024深,宽度64位,足够缓存几个1500字节大帧。
3. 轻量级UDP协议栈的实现细节
3.1 协议栈整体架构与数据流
在FPGA里实现UDP协议栈,我的做法是接收通路和发送通路完全分离,各自使用独立的状态机,互不阻塞。接收通路从AXI Ethernet的m_axi_rx接口读到以太网帧,按帧类型分发。发送通路从用户应用接口拿到待发送数据,组装成以太网帧后写入s_axi_tx。中间公用一个ARP缓存表和一个MAC地址配置寄存器。
接收数据流是这样的:以太网帧到达后,先判断目的MAC是不是本机MAC或者广播地址,不是就整帧丢弃。通过MAC判断后,解析以太网类型字段:0x0806是ARP,0x0800是IP,0x86DD是IPv6,我们的方案暂不处理IPv6。ARP请求的话,检查目标IP是否为本机IP,是则自动回复ARP应答。IP包继续解析协议字段:1是ICMP,回ping应答;17是UDP,按目的端口分发到应用FIFO。
发送数据流稍微复杂一点。应用程序把要发送的数据写入发送FIFO,附带目的IP和目的端口。协议栈检查目的IP对应的MAC地址是否在ARP缓存里,如果命中就直接组帧发送;如果没有命中,则需要先发送ARP请求并等待应答,应答收到后再发送数据。这种模式下,第一次发送会有几个毫秒的延迟,但后续发送都是线速。在工程里,我额外提供了一个静态MAC配置接口,可以手动指定目的MAC,适合点对点直连场景,省去ARP过程,延迟更低。
3.2 ARP模块与MAC地址缓存表
ARP模块是整个协议栈里容易被忽视但其实很重要的部分。FPGA作为嵌入式设备,和上位机通信时,上位机发UDP包之前会先发ARP请求询问FPGA的MAC地址,如果FPGA不处理ARP,上位机的UDP数据根本发不出来。所以ARP应答是必须做的。
ARP请求的帧结构是固定的:以太网头14字节,ARP头28字节。解析时判断ARP头的操作码,请求是1,应答是2。收到请求后,先把发送方IP和MAC存入ARP缓存表,然后用本机MAC构造应答帧:目的MAC是请求方的源MAC,目的IP是请求方的源IP,操作码置2,目标MAC就是请求发的源MAC。这里注意源和目的要完全互换,很多人第一次写都会写反。
ARP缓存表我做了8个表项,按写入时间覆盖,表项记录三元组:IP地址、MAC地址、有效标志。因为是嵌入式局域网,8个表项完全够用。如果你对端设备很多,可以扩展到16或32个表项。缓存表的读端口供发送通路查询,写端口由ARP解析逻辑写入。当ARP缓存未命中时,发送通路会把请求挂起,同时启动ARP请求发送,等待应答期间不再接受新发送任务,避免状态机混乱。
3.3 IP/ICMP/UDP解析与校验和算法
IP包解析的核心是定位IP头、提取关键字段和校验和计算。IP头固定20字节(不含选项),其中总长度字段是整包IP数据报的长度,包括IP头和UDP头。接收时要用这个长度来截帧,而不是直接用以太网帧长度。因为以太网帧可能有填充字节,如果直接按帧长把填充也交给应用层,数据就多了。我踩过这个坑,最开始收到的UDP数据尾部总是多几个字节,排查半天发现是填充没去掉。
IP首部校验和的计算方法是:把IP头按16位为单位累加,如果超过16位则回卷,最后取反。这个算法在FPGA里可以用组合逻辑或者多周期流水实现。发送时同样要生成正确的IP校验和,接收时也要校验一遍,防止坏包污染应用数据。
ICMP只实现了ping应答,也就是type=8的echo request,回type=0的echo reply。回包时要把ICMP头里的identifier和sequence number原样返回,校验和重新计算。这样上位机ping的时候才能正常显示时间。ICMP回包的IP头校验和、UDP头校验和都要正确,否则ping不通或者ping通也不稳定。
UDP头校验和比较特别,计算范围包括了IP伪首部、UDP头和数据。伪首部是源IP、目的IP、协议号、UDP长度。如果计算结果是0,按协议要求要填0xFFFF,因为0表示无校验。自己做协议栈时,为了简单,也可以把UDP校验和置为0,表示不校验,很多嵌入式设备都这么干,但最好还是实现正确,避免在某些严格网络环境里出问题。
3.4 发送通路封装与接收通路裁剪
发送通路的帧封装顺序是固定的:6字节目的MAC、6字节源MAC、2字节类型0x0800、20字节IP头、8字节UDP头、N字节数据。目的MAC来自ARP缓存表,源MAC是本机MAC寄存器,源IP是本机IP寄存器,目的IP和目的端口来自应用接口。
数据发送时,有一个小技巧:IP总长度字段和UDP长度字段要在组装帧时就算好,一旦确定,整帧长度就固定了。校验和计算可以在发送的同时流水完成,用两个周期的组合逻辑,第一周期累加,第二周期取反,不影响整帧发送速率。我测试过,这套协议栈在千兆速率下发送1400字节包,能达到接近线速的吞吐,完全没有瓶颈。
接收通路的裁剪主要针对两种异常帧:小于64字节的短帧和带填充的帧。IP总长度字段是裁帧的唯一依据,从IP头取出总长度后,如果总长度小于实际收到的数据长度,就从UDP头往后取该长度字段对应的数据。UDP头里的长度字段是UDP数据报长度,也就是UDP头加数据,再用它减去8就是应用层有效数据长度。应用数据按这个长度写入FIFO,整帧干净利落。
4. 21套工程源码的构成与移植方法
4.1 工程清单与硬件平台对应关系
这21套工程源码是这些年做项目、给客户适配板卡过程中沉淀下来的,每种组合都经过实测,不是那种“生成一个空壳工程”的凑数代码。工程覆盖了常见的Artix-7、Kintex-7和Zynq-7000系列,速率涵盖了1G和2.5G,接口封装上有原生AXI4-Stream版本,也有简化成32位/64位自定义接口版本。
| 序号 | FPGA型号 | 开发板/平台 | 速率 | 数据接口 |
|---|---|---|---|---|
| 01-04 | Artix-7 35T/75T/100T | 黑金AX7A035/075/100 | 1G | AXI4-Stream 64bit |
| 05-08 | Artix-7 35T/75T/100T | 黑金系列 | 2.5G | AXI4-Stream 64bit |
| 09-12 | Kintex-7 325T/410T | 米联客、ZC706 | 1G/2.5G | AXI4-Stream |
| 13-16 | Zynq-7020/7035 | 正点原子Z7系列 | 1G/2.5G | AXI4-Stream |
| 17-19 | Artix-7 100T | 自研板卡 | 1G/2.5G | 简化接口 64bit |
| 20-21 | Kintex-7 325T | 自研板卡 | 1G/2.5G | 简化接口 32bit |
每套工程都包含完整的Vivado工程文件(Tcl脚本生成或者完整工程目录)、XDC约束、IP核配置导出文件和RTL源码。协议栈代码在各工程之间是通用的,只有顶层例化和约束文件不同。如果你手里的板卡不在这个清单里也没关系,最通用的做法是打开最接近的工程,把XDC里的引脚约束和GT位置改一改就能用。
4.2 移植到自研板卡的标准步骤
移植到自己的板卡,我总结了一套固定流程,基本不会出错。第一步,确认FPGA型号和封装,选一个规格最接近的参考工程,比如同为Artix-7 100T就选17号工程。第二步,用Vivado打开工程,查看顶层例化,把IP核的GT位置改成自己板卡上SFP对应的GT Quad和通道。第三步,改XDC约束,包括GT参考时钟引脚、SFP差分数据引脚、LOS和TX_Disable控制引脚。
第四步,检查时钟资源。GT参考时钟必须是专用引脚,如果板卡上的125MHz时钟没有接到GTREFCLK而是接到普通MRCC/SRCC,IP核无法正常工作。第五步,修改本机MAC地址和IP地址参数。我的代码里把这两个参数做成了顶层Parameter,直接改参数即可,避免每次改寄存器或源码。第六步,综合、布线、生成bitstream,下载后先用ping验证,再用UDP调试工具收发数据。
整个移植过程顺利的话半天能完成,遇到问题基本集中在GT位置约束和参考时钟引脚绑定这两个地方。Vivado报错如果出现GT参考时钟相关的DRC错误,先检查是不是GT位置和参考时钟引脚不在同一个Quad里。这个坑比较常见,只要记住“GT Quad内的所有通道共享该Quad的参考时钟”这个原则,大部分问题都能解决。
4.3 用仿真和板级打流验证功能
功能验证我建议分两步走。第一步是纯RTL仿真,用Vivado Simulator或者ModelSim跑协议栈的testbench。testbench里模拟了AXI Ethernet侧的发送行为:先发一个ARP请求,检查是否回应答;再发一个ICMP echo request,检查是否回echo reply;最后发一个UDP包,检查应用FIFO里的数据是否和发送端一致。发送通路则反向验证:给发送FIFO写入数据,检查TLAST、TKEEP和帧头字段是否正确。
第二步是板级联调。先用一根光纤把FPGA和电脑的网卡直连,电脑端需要光口或者光转电模块。IP地址设置成和FPGA同一网段,比如FPGA是192.168.1.10,电脑设192.168.1.20。第一步先ping FPGA,如果能ping通,说明MAC、PCS、PMA和ARP、ICMP都正常。第二步用网络调试助手或自写的C#/Python上位机发送UDP数据,检查FPGA收到的数据是否一致。发送方向则反过来,FPGA定时发送数据,电脑端用Wireshark抓包验证。
板级验证如果一次通过,说明整个链路非常健康。如果ping不通,不要急着怀疑协议栈,优先检查链路物理层状态:SFP的LOS信号是否正常,GT参考时钟是否锁存,光模块的TX_Disable是否拉低,光纤收发是否接反。这些物理层问题在仿真里看不到,只能靠仪器和眼睛排查。
5. 实测数据:带宽、延迟与稳定性
5.1 千兆与2.5G光口的线速压测结果
我最关心的是这套无PHY方案的性能到底能不能打。在自研板卡上,用iperf3的UDP模式做过完整的带宽测试。测试环境是FPGA通过SFP光模块连接电脑的光口网卡,中间用短光纤直连,没有经过交换机。
千兆1G模式下,发送端用iperf3 -u -b 950M -l 1400打流,FPGA侧应用FIFO持续收到数据,接收速率稳定在118MB/s左右,换算成以太网有效速率大约是947Mbps。丢包率为0,连续跑10分钟无异常。这个数据说明AXI Ethernet Subsystem在1000BASE-X模式下完全没有性能短板,8B/10B编码带来的20%开销之外的有效带宽基本拿满了。
2.5G模式下的表现同样让人满意。线速率2.5Gbps,有效数据率理论上限是2.5G/8B编码后约2.27Gbps,实测iperf3发送UDP流量到2.2Gbps时,FPGA侧接收速率达到约280MB/s,丢包率在万分之一以下。这个丢包主要来自上位机网卡驱动和DMA缓冲的抖动,FPGA侧逻辑没有瓶颈。如果上位机换成更稳定的Linux系统并调大socket缓冲,丢包可以压到更低。
5.2 延迟实测与端到端链路预算
延迟是工业控制和实时数据采集场景非常关心的指标。我做了两种延迟测试。一种是FPGA逻辑自环:在FPGA内部把接收到的UDP数据通过发送通路原样发回,上位机统计从发送到接收的往返时间RTT。实测最小RTT约为18微秒,平均在20-25微秒之间,其中上位机协议栈和网卡驱动的开销占了大部分。
另一种是FPGA到FPGA的直连延迟。两块FPGA通过光纤直连,一块发送带时间戳的UDP包,另一块收到后立刻回包,第一块计算收到回包的时间差。这个测试排除了上位机干扰,实测单向延迟约为8-12微秒,其中AXI Ethernet Subsystem的MAC加PCS/PMA物理层贡献了大约4-6微秒,UDP协议栈解析和重封装占用2-3微秒,剩余的GT收发器串并转换和弹性缓冲占1-2微秒。
这个延迟水平对于绝大多数工业应用完全够用。如果对延迟极度敏感,比如要做亚毫秒级的同步控制,可以考虑缩短UDP数据包长度、关闭IP核的统计功能、把接收FIFO深度调小来降低流水延迟。这些都是可以按需调优的,我在源码注释里也写明了各参数的调整方法。
6. 调试实录:链路起不来、ARP不通、UDP丢包怎么办
6.1 光路不通与GT起不来怎么排查
碰到光口链路起不来的情况,按下面这个顺序排查能省很多时间。第一步,检查光模块有没有发光。拔下光纤,用肉眼对着SFP的发射窗口看,如果完全黑暗,检查TX_Disable引脚是不是被拉高了,很多板卡默认上拉,必须用GPIO拉低。第二步,检查SFP的LOS信号。如果LOS引脚为高,说明接收端没有光信号,问题可能是对端没发光、光纤接错方向、光纤断裂或者光模块损坏。
第三步,检查GT参考时钟。用示波器测GTREFCLK引脚上的波形,必须是125MHz或156.25MHz的低抖动差分时钟,幅度满足GT的输入要求。我曾经遇到过板卡上用了普通晶振而不是差分可编程振荡器,GT无论如何都锁不住,换成正品125MHz差分晶振后一切正常。第四步,检查GT复位时序。AXI Ethernet Subsystem的复位信号需要满足特定时序,参考时钟稳定后至少等待500us再释放复位,否则GT内部状态机可能初始化失败。
最后一步是用ILA抓IP核内部状态寄存器。AXI Ethernet Subsystem提供了丰富的统计寄存器,比如链路状态、接收错误计数、CRC错误计数。通过AXI4-Lite接口读取这些寄存器,能快速定位问题在物理层还是MAC层。我在每个工程里都加了VIO和ILA,方便调试时在线查看链路状态。
6.2 ARP不通和UDP收包异常的细节
ARP不通是最常见的调试问题,表象是上位机ping不通FPGA。先确认上位机网卡的IP地址和FPGA的IP是否在同一网段,子网掩码是否匹配。然后是检查FPGA的ARP应答逻辑,最常见的问题是应答帧的源和目的地址写反了。我在协议栈代码里写了详细的注释,特别标注了ARP应答时操作码为2、目标MAC填请求方的源MAC、目标IP填请求方的源IP。
如果ARP通了但UDP收不到数据,先检查UDP目的端口和FPGA协议栈里配置的端口是否一致。上位机发送的UDP包目的MAC、IP、端口必须全部匹配,协议栈才会把数据送到应用FIFO。其次是检查Wireshark抓包里的IP头和UDP头校验和是否正确,有些上位机软件发送时不会自动计算校验和,导致FPGA侧校验失败丢包。
UDP收到数据但内容不对,重点检查接收通路是否把以太网帧的填充字节误传给了应用层。前面提到过,IP总长度字段是截帧的依据,不要用帧的实际长度。还有一个常见情况是上位机发送的数据正好是跨beat边界,收到后数据顺序没问题但最后多了几个字节,基本就是填充没去掉,按UDP头的长度字段裁剪即可解决。
6.3 高速收发器调试的隐形大坑
最折磨人的往往是GT相关的“玄学”问题。比如链路偶尔断开、误码率偏高、2.5G速率下数据偶发错位。这些问题很多不是逻辑错误,而是硬件和工程约束问题。电平方面,SFP光模块的差分信号直连GT,PCB走线必须按照差分100欧姆阻抗设计,长度尽量短,避免过孔和换层,这些约束要在PCB设计阶段就做好。
GT参考时钟的抖动对链路稳定性的影响,很多人体会不到,直到亲眼看到波形才能理解。我实测过,用同一个GT Quad跑2.5G,参考时钟抖动从4ps增加到10ps时,误码率上升了两个数量级。解决方法是参考时钟尽量靠近GT Quad布置,不要跨过其他高速信号区域,如果板卡空间允许,使用专用的时钟buffer芯片给GT提供低抖动时钟。
最后一个大坑是FPGA的电源纹波。GT收发器对电源质量很敏感,特别是PMA的模拟电源引脚。如果板卡上用DC-DC供电而滤波没做好,纹波过大时GT的时钟恢复环路会抖动,导致链路不稳定。排查方法是测量FPGA电源引脚上的高频纹波,最好控制在20mV以内,必要时增加LDO和磁珠滤波,这个工夫不能省。
工程源码里的所有板级约束、参数配置和调试脚本都是基于这些经验反复打磨过的。我自己第一次做无PHY光口方案时,光是在GT参考时钟上就折腾了将近两天,后来把电源滤波重新做了一版,问题彻底消失。这种问题用Vivado和ILA很难抓到,只能靠示波器和经验,写出来希望大家少走弯路。目前这套方案我已经在好几个量产项目里稳定运行,光口UDP通信从千兆到2.5G都没有出过幺蛾子。如果你手里正好有带SFP座子的FPGA板卡,强烈建议试一试这个无PHY方案,成本、面积和调试工作量都能明显降下来。