
前阵子给一套数据采集设备升级上位机通信原来用USB 2.0采集端数据一上来就开始掉包忙活一下午也没找到瓶颈。后来把通信链路整体换到ZestET2-NJ这块千兆以太网FPGA模块上问题才真正解决。写这篇文章的时候这块模块已经在我手头跑了小半年从最初看板卡手册一头雾水到后来把千兆跑满线速中间踩了不少坑也积累了不少可复用的经验。这篇东西不打算写成说明书式的罗列而是按我实际开发的顺序把这块Gigabit Ethernet FPGA Module从硬件认知、时序调试、工程搭建到数据通路实测的完整过程记录下来希望对正在做或者准备做FPGA以太网方案的朋友有参考价值。1. ZestET2-NJ的硬件底子为什么先看板卡再写代码很多人拿到FPGA开发板第一件事就是开Vivado建工程这个习惯在纯粹做逻辑验证的时候没什么问题但一旦涉及千兆以太网这种模拟和数字混合的接口硬件底子没吃透后面代码写得再漂亮也跑不起来。所以我建议先从硬件层面把模块拆开看。1.1 板上关键器件与数据链路ZestET2-NJ模块的输入参数里最显眼的就是“Gigabit Ethernet”和“FPGA”但真正决定开发难度的不是FPGA芯片本身而是围绕以太网链路的那几个外围器件。以我这块板子为例链路大致是FPGA --- RGMII --- PHY芯片 --- 网络变压器 --- RJ45。FPGA端负责MAC层逻辑完成以太网帧的封装、解封装、CRC校验、地址过滤等工作PHY芯片则承担物理层编码把MAC送过来的并行数据变成双绞线上的差分信号同时负责自动协商、载波侦听、链路状态检测网络变压器做电平隔离和共模抑制保护PHY芯片免受外部浪涌干扰RJ45是标准插座带内部LED指示Link和Activity状态一目了然。开发之前一定要确认PHY的具体型号。我这块模块用的是瑞昱的RTL8211EG但市场上同类型模块也有用美满的88E1512、博通BCM5461的它们都支持千兆RGMII但寄存器地址、复位时序、功耗模式、LED配置差异很大。一个很实在的建议拿到板子先去看PHY芯片丝印确定型号后立刻把对应数据手册下载下来重点看三块内容——寄存器映射、RGMII时序参数、复位时序要求。这三样后面全是高频使用的。1.2 网口布局和供电、时钟部分网口这块我特别提醒注意RJ45内部的LED驱动方式。很多模块的LED是FPGA控制的但也有一部分RJ45自带灯驱由PHY芯片直接驱动。ZestET2-NJ的LED控制是通过FPGA侧引出的GPIO实现的你如果不在约束文件里正确配置这些引脚网口插上去灯不亮很容易误判成PHY没工作实际只是LED没驱动而已。供电和时钟同样值得提前看一眼。以太网PHY对供电纹波比较敏感模块上一般会单独安排一路1.0V或1.1V LDO给PHY核心供电FPGA内核电压和IO电压分开。我做的时候发现一个问题如果只给FPGA下载程序不额外初始化PHY的供电时序偶尔会出现PHY启动后自动协商不成功的情况。这类问题极难查最有效的办法是强制在FPGA配置完成后对PHY做一次硬复位把RST引脚拉低再释放确保PHY启动状态干净。时钟方面RGMII需要一个125MHz的参考时钟源ZestET2-NJ板载了一个50M晶振给FPGA然后用PLL内部倍频到125M再经由EMCCLK或专用时钟引脚送给PHY。这个方案能用但相位噪声和抖动表现不如独立125M有源晶振实测对时序收敛有余量要求后面在约束部分会细说。2. 千兆链路的第一道坎RGMII时序与PHY初始化我第一次在ZestET2-NJ上跑千兆以太网烧进去一个最简单的MAC环回逻辑结果上位机网卡一直显示“网络电缆被拔出”折腾了大半天。后来把所有问题归结成两类一是RGMII时序不满足二是PHY初始化寄存器没配对。这两类问题都极具隐蔽性且排查手段完全不同。2.1 RGMII的DDR传输机制RGMII全称是Reduced Gigabit Media Independent Interface工作频率125MHz4根数据线在时钟的上下沿各采一次因此等效传输速率是1000Mbps。之所以叫“Reduced”是因为相比传统GMII的8位数据线、独立发送/接收时钟RGMII把数据线砍到4位同时用DDR方式把发送时钟TXC和数据TXD之间的相位关系限定为“时钟边沿对齐数据跳变”。这里面最大的坑在于千兆模式要求TXC的边沿和TXD的跳变正好对齐但接收端需要的是用时钟的中心来采样数据也就是数据要稳定在时钟边沿两侧。所以PHY内部通常会把接收时钟做90度相移或者在MAC侧通过IDELAY把数据延迟2ns左右保证RGMII时序窗口落在采样点的安全区域内。ZestET2-NJ上FPGA和PHY之间的RGMII走线长度对时序也有影响板子一旦固定下来延迟值基本是确定的但不同批次模块可能存在1ns级别的差异。2.2 MDIO管理通道的初始化流程PHY芯片是一个模拟和数字混合体但管理接口极其简单——MDIO时钟MDC和双向数据线MDIO两根线通过读写寄存器配置PHY的工作模式。ZestET2-NJ模块在千兆模式下需要重点关注的寄存器配置包括寄存器地址功能典型配置项0x00控制寄存器软复位、1000M全双工使能、自动协商使能0x01状态寄存器读链路状态、协商结果0x04千兆控制寄存器1000M全双工能力广告0x09千兆状态寄存器确认1000M协商完成0x1FPHY特定控制寄存器调整LED模式、极性0x10-0x1F扩展寄存器用于RGMII延迟配置、SLEEP模式控制初始化顺序也很关键。建议先置位0x00的软复位位并等待复位完成再配置千兆能力的广告寄存器0x04最后使能自动协商。如果RGMII侧需要调整时钟/数据延迟可以在PHY的扩展寄存器里找到专门配置接收或发送延迟的字段。我用的RTL8211EG是在0x10和0x15寄存器里进行RGMII TX/RX delay配置具体位段要根据数据手册确认不要凭记忆写。2.3 IDELAY手动校准一次链路不通的完整排查我记得有一次ZestET2-NJ板子回环测试时PC网卡协商成了千兆ping包有去无回链路指示灯也正常。这个现象非常迷惑因为链路层看似通了MAC帧肯定有发出去但大概率是接收方向时序不对导致帧错误太多。排查思路先用Wireshark抓包发现PC端能发出ARP请求但收不到任何响应用Vivado ILA观察FPGA侧RX接口发现rx_ctl信号一直为低说明PHY认为线上没有有效数据再用MDIO寄存器读0x01和0x09链路状态显示已经协商到千兆全双工说明PHY没问题问题定位到FPGA侧RGMII接收时序改用IDELAY把RXD数据延迟从0逐步调到31级每级约78ps同时观察rx_ctl是否正确解析出帧起始。最终把RXD和RX_CTL的IDELAY调到大约26级左右ping包就能通了。这个经验很重要IDELAY的取值和FPGA速度等级、PCB走线长度、PHY驱动强度都有关系不要指望一个固定值通吃所有板子调参时应该做成可配置寄存器通过TCL命令动态调整。3. 在Vivado里把以太网MAC跑起来IP配置与工程约束当RGMII时序基本稳定后就可以正式在Vivado里搭工程了。ZestET2-NJ模块的FPGA芯片支持Vivado开发流程TCL脚本和IP核都是标准流程但有几个配置项非常容易踩坑单独拿出来说一说。3.1 三速以太网IP配置的几个关键点Vivado里可以直接使用三速以太网MAC IP它会帮我们把MAC层的大部分工作做完包括帧起始/结束检测、CRC校验、填充、流控帧处理、MII管理接口等等。配置时需要注意“Physical Interface”选择RGMII速率选择1Gbps全双工Phy Device地址要和实际PHY的地址一致。ZestET2-NJ的PHY地址通常由硬件跳线或引脚上下拉决定默认一般是0x04但不同版本模块可能不同。配置错了最典型现象是MDIO读写全返回0xFFFF且PHY没有任何反应“Management Interface”必须勾选MAC IP需要对外提供MDC/MDIO引脚也方便后续用ILA观测PHY寄存器值如果后续要把FPGA侧时钟和PHY的125M参考时钟解耦建议使用“clocking”选项中的独立时钟输入保证MAC核心时钟是连续可用的。IP里还有一项“Statistics”统计接口强烈建议打开。它提供发送/接收帧数、CRC错误数、超长帧数、丢包数等计数器调试网口时能大幅提高定位效率否则你只能靠ILA在高速信号上抓波形非常痛苦。3.2 引脚约束和System Planner协同ZestET2-NJ模块的引脚分配必须参考模块自带的原理图或BSP文件不要直接用通用LED灯那些引脚。RGMII相关的信号包括RXC、RXD[3:0]、RX_CTLTXC、TXD[3:0]、TX_CTLMDC、MDIOPHY_RST_N这些引脚在XDC约束里的IO标准要严格按原理图配置。很多模块默认使用LVCMOS33或LVCMOS18千万别只设成LVCMOS25IO标准不一致会导致信号电平不匹配轻则偶尔丢包重则PHY完全无法通信。同时RGMII的时钟信号要加到set_clock_groups约束里避免时序分析工具把它和用户逻辑时钟混在一起乱分析。具体到ZestET2-NJ我建议直接用Vivado的System Planner导入板级文件它能自动生成引脚约束和部分时钟约束省去手动查原理图的功夫。但也要注意System Planner生成的拓扑不一定覆盖所有电源稳定、复位层次、差分对等细节生成XDC之后还要人工核对RGMII引脚方向是否正确。3.3 时钟域划分从125MHz到用户逻辑时钟千兆以太网在FPGA内部会涉及多个时钟域。RGMII发送时钟是125MHz接收时钟由PHY恢复出来可能也是125MHz但和本地发送时钟不同源存在频偏问题。三速以太网IP内部有异步FIFO来处理跨时钟域但用户逻辑侧通常使用一个统一的axis_aclk比如125MHz或150MHz。有一个很常见的错误把axis_aclk和RGMII TXC直接连成同一个125MHz时钟表面上看频率一致实际相位和抖动特性完全不同。PC网卡的时钟和本地PHY时钟本来就不是一个源头接收侧RX时钟和用户逻辑时钟之间必然存在跨时钟域也就是所谓的异步时钟域。此时必须依赖IP内部的FIFO或者在用户逻辑里再插入异步FIFO。我实测下来如果省掉这层异步处理高速连续传输时偶尔会有几个帧后半个字节错位排查起来极其困难。固化程序的时候也要注意ZestET2-NJ把配置数据和PHY初始化逻辑一起固化到SPI Flash后上电顺序和JTAG调试时不完全一样。最好在FPGA配置完成后先对PHY做一次硬复位并延时至少100ms再做MDIO初始化否则容易出现“JTAG下载一切正常固化后网口不通”的诡异现象。4. 数据通路的搭建与验证从AXI-Stream到UDP回环硬件链路通了以后接下来就是真正的“FPGA开发”部分——把以太网数据从MAC层接到用户逻辑里并最终形成一个可用的数据通路。这一节我按自己实际调试的路径来写重点讲AXI-Stream接口的使用和UDP帧的发送逻辑。4.1 AXI-Stream接口和内部FIFO设计三速以太网IP的发送和接收侧对外暴露的都是AXI-Stream接口。对用惯了AXI4总线的朋友来说这个接口种类简单得多核心只有tvalid、tready、tdata、tlast、tkeep几个信号。发送方向上是用户逻辑主动推数据tvalid拉高且tready拉高时传输一拍接收方向上是MAC IP向用户逻辑推数据同样靠tvalid/tready握手。有个细节tkeep信号表示最后一拍的有效字节数。以太网帧最小是64字节如果用户数据不足64字节MAC IP内部会自动做填充但用户逻辑在组帧时最好直接构造完整帧减少IP侧填充带来的不确定性。我用的方案是在用户逻辑和MAC IP之间插入一个异步FIFO。发送侧用户先把要发的完整UDP帧写入FIFOFIFO非空时自动读出并写给MAC IP。接收侧MAC IP收到的帧写入FIFO用户逻辑从中读取并做解析。FIFO深度不要太小我最初设了512在一次连续收到大量广播包时直接溢出丢帧后来改成2048问题才消失。4.2 UDP发送逻辑状态机FPGA侧自行实现TCP几乎不现实状态复杂且占资源UDP才是FPGA应用里最常见的协议。UDP发送逻辑并不复杂关键在于组帧时各层的顺序和长度计算。以一个发往PC的UDP包为例完整帧结构是目的MAC地址6字节源MAC地址6字节以太网类型2字节0x0800表示IPv4IP头20字节UDP头8字节UDP负载FCS4字节由MAC IP自动生成在状态机里可以这样安排IDLE -- 发送MAC头 -- 发送IP头 -- 发送UDP头 -- 发送负载数据 -- LAST一个容易忽略的点IP头的总长度字段和UDP头的长度字段都要计算正确而且IP头校验和是16位反码和取反UDP校验和可选但建议开启计算范围覆盖UDP头负载IP头字段不用参与。用状态机写的时候每发送一个字节校验和累加器也要同步更新否则全部数据发完再单独算会多占很多硬件资源。4.3 回环验证先让模块自己跟自己通信第一次数据通路联调时不要直接连PC上位机先做回环。ZestET2-NJ模块上有的版本带双网口可以A口发B口收也可以把PHY配置成内部环回模式。用PHY寄存器环回验证的是MAC逻辑用外部网线双机互连验证的是PHY物理链路两种模式各有用处。我的调试顺序是先用RTL8211EG寄存器配好内部环回确认FPGA侧MAC收发通路完全正确再用一根直连网线把ZestET2-NJ的两个网口互连验证外部PHY之间的数据能跑通最后接PC用Wireshark抓包确认ARP、UDP帧结构、校验和全部正确。如果直接在第三步才开始抓包一旦出现问题你根本不知道是PHY还是MAC还是帧格式的问题排查效率会低得多。5. 实测带宽与丢包修复630Mbps到线速的调优记录ZestET2-NJ模块标称是千兆以太网但“千兆”意味着物理层线速实际应用能否到千兆取决于FPGA侧时钟、FIFO深度、协议栈开销、操作系统网络栈等多方面因素。这一节记录我在这块模块上将吞吐从630Mbps提升到接近线速的完整过程。5.1 用iperf看真实吞吐ZestET2-NJ和PC之间直接点对点连接PC上装好了iperf工具。FPGA侧写了一个简单的UDP回环程序把PC发过来的UDP包原样返回。如果PC发的是1Mbps的小包流回环没有任何问题但用UDP无脑打满带宽比如把包长设为1400字节连续发送PC发出的包FPGA收到后返回中间出现明显丢包。第一轮测试结果里面接收侧吞吐大约630Mbps发送侧只有400多Mbps。这个结果说明问题不在PHY物理层而在FPGA内部处理速度或FIFO深度上。我用ILA抓了一下MAC IP的接收接口发现接收侧tready在部分时间段会被拉低说明FIFO满了。原因很直接UDP回环逻辑的处理逻辑里每条指令之间有几个周期的空档导致回环逻辑跟不上MAC IP的接收速率。5.2 RX侧丢包缓冲区深度和突发长度定位到FIFO满了之后我做了两件事第一把异步FIFO深度从2048提到4096。单看一个UDP包突发2048个32位字足够缓冲几十包但千兆线速下MAC IP可以连续输出上百个帧的突发流量2048立即不够用。建议FIFO深度至少能覆盖IP内部突发缓存的两倍。第二优化回环逻辑的节拍。原来代码里每读出一个数据都要等几个周期判断一下改成流水线直通模式一旦FIFO有数据可读立刻转发给MAC IP的发送接口中间不插入任何多余状态。这样逻辑路径变成纯数据搬移发送速率就追上接收速率了。修改后再次测试UDP吞吐稳定在940Mbps左右。5.3 时序收敛和布线的最后冲刺FIFO深度和逻辑节拍问题解决后我又遇到一个更微妙的瓶颈——综合后时序报告显示WNS最差负余量只有0.1ns左右虽然没报错但稍微加点外部干扰就可能出现偶发丢包。把vivado的时序报告打开看关键路径全在UDP校验和的组合逻辑上一串16位加法器在125MHz下几乎把时序裕量用光。解决办法是优化校验和计算逻辑把校验和拆成两级流水每个时钟周期只算部分字节避免单个周期完成整个头部和负载的加法链。改完之后WNS提升到约0.8ns稳定了不少。另外一提如果ZestET2-NJ的FPGA型号带高速收发器调试GTX/GTH链路时可以使用IBERT核来测试物理通道质量。虽然千兆以太网走的是普通IO不涉及高速收发器但用IBERT测一下板子上其他高速通道的误码率可以帮助判断整板供电和串扰有没有问题这个习惯我一直保留。6. 往更高带宽方向扩展这块模块还能怎么玩ZestET2-NJ作为一块千兆以太网FPGA模块单纯把它当成一个网口转接板来用就浪费了。它的FPGA资源和外部接口足以支撑更多数据密集型应用。最后写几个我实际验证过或者正在开发的方向供参考。6.1 外挂DDR3做大容量缓存ZestET2-NJ模块一般会引出DDR3接口或直接板上焊了DDR3颗粒。以太网数据流的瞬时带宽很高但业务逻辑处理速率往往低于线速这时候中间必须挂一个大容量FIFO或DDR3缓存。一个很典型的方案PC通过千兆以太网向FPGA发送待处理数据FPGA把数据先写入DDR3等整包数据收齐后再启动DSP或图像算法处理处理完再通过以太网返回PC。用DDR3做缓存时要做二维乒乓设计第一块DDR3区域正在被写入第二块区域正在被读出两块区域交替使用。中间用AXI4接口桥接DDR3控制器用户逻辑只需要关心地址和数据底层刷新、bank切换全交给控制器。这里最容易踩的坑是DDR3读写时地址对齐和突发长度设置建议按16字节对齐访问突发长度设为8个128位字能显著提高DDR3带宽利用率。6.2 图像/数据采集场景的接入方案ZestET2-NJ经常被用作高速数据采集的上位机通信接口比如ADC采集卡、摄像头数据流、软件无线电前端。FPGA在采集端做实时处理处理后的压缩结果通过千兆以太网传给PC做显示或存储。在这个场景里以太网侧的UDP协议栈通常要支持突发大包建议PC端用专用的数据接收程序而不是Wireshark直接抓包否则软件协议栈开销很容易丢数据。如果你玩的是软件无线电可能会用FPGA实现CIC滤波器来抗混叠和抽取。CIC滤波器在抽取之后数据率下降正好能匹配以太网的带宽上限。比如ADC以100MSPS采样每个样点16位原始数据率是200MByte/s远超千兆以太网的125MByte/s极限。可以先在FPGA里做CIC抽取低通把数据率降到100MByte/s以下再经以太网上传。这个处理链完全可以在ZestET2-NJ这类板上完成不需要额外的高端平台。6.3 和JESD204B、PCIe等高速链路协同有些更高端的项目会同时用到Gigabit Ethernet和JESD204B这种高速串行接口比如射频采样ADC前端。JESD204B负责把ADC采样数据高速搬运进FPGA千兆以太网负责把数据分发到外界或接收控制指令。这两套链路同时工作时时钟和同步设计一定要提前规划JESD204B的SYSREF和以太网报文定时之间可能存在周期性的相位抖动需要在用户逻辑里做缓冲对齐。另外如果后期性能不够用ZestET2-NJ模块的板型通常也会兼容PCIe金手指版本。届时可以考虑把以太网和PCIe同时跑PCIe用于大数据块搬移以太网用于控制面和管理面。这样分工明确实际运维时也方便分别排障。我个人在实际操作中的体会是ZestET2-NJ这类千兆以太网FPGA模块真正的价值不在那块网口而在“FPGA可编程”这个自由度上。当你把RGMII时序调明白、把MAC和PHY跑通之后后续不管接DDR3还是接ADC核心思路是一样的——先把数据稳定地搬到缓存再让业务逻辑以自己能接受的速度去处理。希望这篇记录能帮你少走一点弯路。