
接手一块带FPGA的百兆以太网板卡前前后后折腾了好几轮从一片茫然到最终能稳定跑起来。上周某团队做视频采集板采购的网口模块死活不通最后也是走了一遍类似的排查流程。这里把完整的“FPGA 百兆网”上板调试记录整理出来包括整体思路、每一步的具体操作、踩过的坑以及排查问题的手法。如果你正好在调MAC、PHY、MII接口这一类东西这篇应该能省你不少时间。1. 先把项目背景和整体方案说清楚1.1 这块板子到底要干什么这块板子的核心需求很简单FPGA通过百兆以太网接口和上位机通信传输图像数据和少量控制命令。上下行数据量都不大百兆带宽完全够用没必要上千兆成本和布线复杂度都能压下来。板子上FPGA作为MAC控制器外接一颗百兆PHY芯片PHY再通过RJ45网口连到交换机或PC。FPGA内部实现一套简化的MAC逻辑发送侧把上层数据封装成以太网帧接收侧解析以太网帧并提取有效负载同时通过MDIO接口读写PHY寄存器完成配置和链路状态监控。刚开始上板调试时最常见的问题是链路建立失败、数据收发不通、或者能通但丢包严重。这些问题往往不是单点故障而是RTL逻辑、PHY配置、物理链路、时钟时序、上位机软件等多方面因素交织在一起。1.2 为什么选百兆而不是千兆百兆以太网用的是MII接口数据宽度只有4bit时钟25MHz。相比千兆的RGMII/GMIIMII接口时序裕量更大调试难度低不少。同一套代码如果先把百兆调通后续再往千兆迁移也能少走很多弯路。另一个原因是百兆PHY的功耗和发热都更可控。对一块没有主动散热的小型采集板来说这两个指标很关键。再加上咱们用的线缆和连接器都是现成的百兆方案整体成本压缩得很明显。2. 调试环境和工具准备2.1 硬件环境清单调试之前先把环境盘一遍。我的调试台面包括FPGA开发板带一个百兆以太网口PHY芯片的方案比较主流MDIO地址默认是0PHY地址可以通过外部引脚配置。一个千兆交换机兼容百兆速率方便组网。一台普通PC安装抓包分析工具用于验证FPGA发出和收到的数据。一根标准超五类网线尽量短的避免网线自身引入过多信号衰减。示波器和逻辑分析仪不过大部分信号级测量其实用FPGA内部的ILA就能搞定。注意选网线时别拿那种又细又软的扁线实测过几次差线在长距离传输时很容易出现高频信号劣化百兆偶尔还能跑但误码率和重传率会明显上升。调试阶段用短线高品质网线能少很多麻烦。2.2 软件和固件工具链FPGA侧采用厂商提供的主流开发环境工程里包含了MAC层逻辑、MDIO控制器、一些调试用的FIFO以及ILA抓包IP核。上位机侧抓包工具选择的是基础网络分析软件不依赖额外硬件就能直接看网卡收到的原始帧。调试中我用得最多的其实是ILA。百兆MII接口一共就那么十几根信号把MII接口的TX、RX、控制信号全部送进ILA做实时采样协议层的问题基本都能在FPGA内部看清。逻辑分析仪和示波器更多用于检查物理层的电平、上升沿、和时钟质量。提示工程里一定要预留ILA调试入口哪怕正式代码不需要也建议用条件编译留出来。否则调试中想抓信号就得改RTL重新综合一次十几分钟效率极低。3. 上板之前先自查逻辑和配置3.1 PHY寄存器配置的初始套路PHY芯片上电后默认是自主协商模式但FPGA侧的MAC逻辑和PHY的工作模式必须保持一致。比如PHY协商成百兆全双工MAC侧就不能按半双工跑。MDIO控制器的初始配置流程我一般这样走上电后延时至少100ms等PHY内部上电复位完成。读PHY的ID寄存器确认MDIO通路是通的能读到预期的厂商ID和型号ID。读基本状态寄存器查看当前是否有链路、是否支持需要的模式。写基本控制寄存器关闭PHY内部环回开启自动协商或者强制百兆全双工。设置广告寄存器把支持的速率和双工模式广播出去。重新触发自动协商然后轮询状态寄存器直到协商完成。这里有个细节有些PHY进入自动协商后需要等待一小段时间才能稳定轮询间隔别太短建议100ms一次连续读几次结果一致再往下走。3.2 MII接口时序自查百兆MII接口信号不多但每一根都有讲究TX_CLK25MHz由PHY提供FPGA发送数据时要用它做同步时钟。TX_EN发送数据有效信号只有它为高时TXD[3:0]上的数据才是真的。TXD[3:0]发送数据总线。RX_CLK25MHz由PHY从接收数据中恢复出来FPGA接收侧用它采样。RX_DV接收数据有效信号高电平时RXD[3:0]有效。RXD[3:0]接收数据总线。MDC/MDIO管理接口用来读PHY内部状态和写配置。上板前在RTL仿真里确认这些信号的时序关系相对容易但上板后最容易出问题的反倒是各个信号的有效宽度和时钟沿对齐。比如TX_EN必须严格在TX_CLK上升沿之前建立稳定如果逻辑里用了组合逻辑去产生TX_EN就很容易出现建立时间违例。注意PHY的TX_CLK和RX_CLK通常不是同一个时钟源而且RX_CLK是从线上恢复的频率和相位都可能存在抖动。FPGA内部做跨时钟域处理时千万不要直接把两个时钟域的信号混用。4. 上板调试实战记录4.1 第一步确认PHY链路建立上电后打开ILA抓MDIO读操作确认能正确读到PHY ID。如果MDIO总线没反应先用示波器看MDC和MDIO是否有正常翻转再看PHY地址配置是否正确。这里我踩过一个坑开发板上PHY地址靠外部上下拉电阻配置结果有一个电阻虚焊导致PHY地址变成了一个不对的值MDIO怎么读都读不到。后来量引脚电平才定位到是硬件问题。MDIO能读通后无论PHY是自动协商还是强制模式link状态寄存器都必须变成“已建立”。如果链路一直起不来最典型的几个原因PHY芯片没有正确复位。网线另一头没接设备或者接的设备没上电。PHY的晶振没有工作量一下PHY的25MHz时钟输出。网络变压器或RJ45的差分线有问题。链路建立之后PHY通常会点亮Link指示灯。如果硬件设计上把link状态引到LED这个灯比读寄存器直观得多。调试初期我习惯一边看LED一边读寄存器能快速区分是PHY物理链路问题还是MDIO读配置问题。4.2 第二步用PHY内部回环验证MAC发送链路链路建立只是第一步接下来要确认FPGA的MAC发送逻辑是否工作正常。最简单的方法是启用PHY的内部环回功能这样FPGA发出的数据经过PHY后不经过外部网线直接由PHY的物理层返回接收路径。这样能绕开网络变压器、RJ45、网线、对端设备等一堆外部因素把问题圈定在FPGA和PHY以内。操作流程通过MDIO设置PHY工作在环回模式有些PHY还把环回模式从半双工强制改成了全双工需要同时把双工模式配好。在FPGA内部构造一个固定的数据帧帧内容包括前导码、帧起始符、目的MAC地址、源MAC地址、长度/类型字段、有效负载、FCS校验字段。持续发送同一帧数据。同时用ILA抓取MII接口的发送信号确认FPGA确实在产生合法的数据帧。再用ILA抓取接收侧信号确认PHY返回的数据帧是否正确进入FPGA接收逻辑。这个测试跑通后基本能确定FPGA的MAC收发侧逻辑、FIFO读写时序、PHY的MII接口时序都没有大问题。回环测试里最容易出问题的地方是FCS。很多简化版MAC逻辑在发送时故意不填FCS把全零或者随机值放在那里导致接收侧校验过不了。我在某项目里就见过这种问题发送数据和接收数据完全一致但接收校验模块一直报错后来才发现发送侧根本没计算CRC32。4.3 第三步真实线上抓包验证内部环回验证完成后关闭环回模式把网线接到交换机或PC上。此时FPGA向外部发送数据上位机用抓包工具监控。我习惯先发一个固定内容的UDP包源MAC地址、目的MAC地址、IP、端口全部手填固定值。目的MAC如果填了广播地址FF:FF:FF:FF:FF:FF整网都能收到也方便验证组网是否正常。如果目的MAC填上位机的具体MAC地址需要先查一下目标网卡的MAC地址。抓包结果分析如果抓到了FPGA发送的帧但内容是乱的比如前导码不完整、帧长度错误、有效负载错位那问题大概率在MAC逻辑本身。如果完全抓不到帧需要回到ILA看MII接口是否有数据输出同时检查PHY的配置是否关闭了发送路径。如果帧内容是好的但上位机网卡显示大量CRC错误往往是MII关键信号建立保持时间不够或者时钟有毛刺。发出去验证成功后再测试接收方向。最简单的方法是用PC发ARP请求来确认物理链路。FPGA侧抓ARP请求如果能正确解析出源MAC地址和IP说明PHY、MII接收路径、MAC逻辑已经打通。4.4 第四步跑通Ping测试收发都能抓到数据之后做一次端到端的Ping测试。FPGA内部实现一个简单的ICMP应答逻辑收到Ping请求后自动回复。这一步比较隐匿的坑是IP校验和和ICMP校验和的计算。很多新手容易算错导致Ping通一次失败一次。另一个坑是回包的时候如果FPGA内部有缓存或FIFO处理太慢会造成Ping延迟漂移甚至超过对端超时时间。实际测下来只要FPGA在收到请求后10ms以内发出回复一般不会丢包。Ping测试建议连续跑1000个包长ping的时候顺带观察有没有偶发丢包。如果丢包率极低但偶尔出现一次优先怀疑接收侧FIFO溢出或MAC地址过滤模块误删了数据帧。5. 上板之后经常会遇到的硬件和逻辑问题5.1 时钟问题最让人头大的部分百兆MII里MAC发送侧工作时钟和MAC接收侧工作时钟都来自PHY。奇怪的是很多时候这两个时钟频率还算稳定但相位关系会有微妙的漂移特别是在长时间运行后温度变化时具体表现为千次测试偶然一次收发错位。调试中我建议用ILA同时抓TX_CLK和RX_CLK观察跨时钟域的数据是否有亚稳态迹象。如果抓到的数据边缘出现毛刺优先检查PHY供电是否干净。供电噪声会在时钟上耦合出毛刺这个问题很难在早期发现因为示波器带宽不够的话根本测不出来。另外一个大坑是FPGA内部PLL使用不当。有人习惯把外部25MHz晶振倍频成125MHz再分频后给MAC逻辑用这种做法在时序上容易出问题。官方推荐的做法是MAC逻辑直接使用PHY提供的TX_CLK和RX_CLK不在内部额外生成和转移链路时钟。5.2 MDIO读写不稳定MDIO协议本身很简单但坑在时序。MDC时钟最高可以到2.5MHz有些PHY只支持到这个频率。如果FPGA侧把MDC分频得太高超过PHY支持范围读写结果就间歇性出错。正确做法是把MDC配置在1.5MHz上下留足裕量。另一个经验是每次MDIO读写之前先发一个空闲状态确保PHY内部的状态机复位到正确位置。MDIO读数据不稳定还有一个常见原因FPGA侧用了一个很宽的总线宽度去并行读多个PHY寄存器结果每个寄存器之间没有加入充分的等待时间。解决办法是每次读一个寄存器读完立刻停下来等一个MDC周期再发起下一次操作。5.3 网络变压器和RJ45的接线问题物理层的问题上板阶段同样容易翻车。网络变压器的抽头连接方式、共模电感的位置、RJ45引脚定义每一项都要和PHY芯片手册逐一核对。调试某块板子时遇到过这样的现象PHY芯片能link上但必须用力按压RJ45接口才稳定。后来检查发现是RJ45的焊接盘有一处虚焊信号链路阻抗不连续导致反射过大误码率高到无法通信。正常百兆链路只需要用到RJ45的1、2、3、6四根脚另外四根脚虽然不用但也要做合理的端接处理。不要悬空最好按照PHY参考设计来接。5.4 FIFO和流控导致的丢包FPGA作为MAC时内部通常有发送FIFO和接收FIFO用来跨时钟域和缓冲数据。这两个FIFO的深度和读写策略直接决定了会不会丢包。发送方向如果上层模块写入速率大于PHY发送速率FIFO就会溢出。解决方案是加入反压信号让上层模块知道当前不能继续写数据。这块的逻辑一定要做充分验证否则在高负载下就会莫名其妙丢帧。接收方向如果PHY收到的数据包速率高于FPGA内部处理速率接收FIFO同样会溢出。百兆全双工下对端可能连续发来大量广播包处理不过来时就会出现“接收FIFO清零丢帧”的情况。我曾经在某项目里遇到一个隐蔽问题接收FIFO的读侧时钟是用RX_CLK驱动的但顶层又给了FRAME_DONE信号这个信号来自另一个时钟域偶尔晚一个周期才来。结果就是FIFO经常多读一个数数据流错位之后整帧内容全错。最终的解决办法是同步器确认真空信号后再判断FIFO状态机避免跨时钟竞态。6. 常见问题与排查技巧实录6.1 整理一张问题速查表症状可能的根因排查手段解决方向MDIO读不到PHY IDPHY地址配置错误或MDIO时序不对示波器量MDC/MDIO、检查上下拉引脚修正地址配置、降低MDC频率链路一直不建立PHY没复位、网线有问题、对端设备未就绪观察link LED、读PHY状态寄存器检查硬件复位信号、检查对端连接发送无数据TX_EN逻辑错误、时钟不对ILA抓MII接口信号修正发送状态机、检查TX_CLK抓到帧内容乱MII时序违例、FCS没计算抓包对比帧内容、ILA查看逐bit信号修正MAC逻辑、补全CRC计算接收偶发丢包接收FIFO溢出、跨时钟域处理不当观察fifo状态寄存器、ILA抓frame_done时序增加FIFO深度、处理反压、同步器修跨时钟域长ping偶发丢包PHY供电噪声、时钟抖动、网线质量差示波器测PHY供电、换优质短网线优化供电滤波、换线并检查接头回环正常但线上不通网络变压器或RJ45接法问题测量网络变压器各引脚连线、检查RJ45焊接按参考设计修正物理层接线重启后才正常上电复位时序不满足PHY要求延时检查复位时序、观察上电波形增加复位延时、确保PHY先上电6.2 调试心得在真实调试过程中经验往往比工具更重要但排在经验前面的永远是观察。出问题不要急着改代码先把能抓的信息都抓下来。包括PHY寄存器的值、ILA的波形、抓包工具的数据、LED状态这几类信息组合在一起基本能定位到根因。排查顺序我一般是这样链路状态是否正常看PHY寄存器这一步能排除掉物理层的很多问题。MDIO通路是否正常必须先能读PHY寄存器否则后面没得谈。发送方向是否正常从FPGA发出到抓包软件能看到每一级都排查。接收方向是否正常从对端发出到FPGA内部解析出数据每一级都排查。最后才是协议层联调比如ARP、ICMP、UDP。每做一步确认一步不要同时改多个模块然后赌它一次能通。我见过某工程师一次改了物理层配置、MAC逻辑和FIFO深度三处结果出问题后根本无法定位是哪一处引入的。其实每次只动一小块验证完再动下一块反而更省时间。注意调试现场一定要记录每一步的操作内容和现象。很多时候你觉得这次改动没问题但几天后再看记录才发现是另外某个关联改动导致的问题。一个简单的Excel或者Markdown表格都行原始状态、修改了什么、现象如何如实记录就好。7. 调试过程中值得说明的细节以及留给后续的扩展思考7.1 规范上电流程上电流程这个点很多介绍性的资料只是一句话带过但对上板调试影响非常大。FPGA和PHY如果同时上电复位时序很可能不满足PHY的要求导致PHY内部状态机没有完全初始化。规范的上电流程应该是系统上电后先让FPGA完成配置并释放内部复位之后延迟至少200ms再释放PHY的复位信号。这样PHY上电后所有内部寄存器和状态都能被正确初始化。如果硬件设计上PHY的复位引脚被直接接到了系统复位上在FPGA配置期间PHY已经完成上电但FPGA还没开始工作那就有可能出现PHY在上电瞬间尝试自协商但MAC侧不回应后续链路状态就始终不对。遇到这种情况最好在FPGA代码里增加一个复位输出引脚用逻辑来控制PHY的复位时机。7.2 百兆调试完后面向千兆的扩展思路百兆MII这块调通后后续如果要升级到千兆一些调试手法可以顺手迁移但也要重新认识接口差异。千兆常用的RGMII接口数据宽度仍然只有4bit但时钟从25MHz提高到125MHzDDR模式下上下沿都采样相当于8bit数据总线。这个变化最大的挑战是时钟裕量变小对PCB布线、时钟偏移的要求都更高。调百兆时积累的MDIO配置经验、回环测试思路、抓包分析方法在千兆调试中照样适用。差异最大的地方在于时钟约束和引脚间偏移。当时调百兆MII时只要信号不出现明显毛刺基本都能过但千兆RGMII如果出现时序问题很可能表现为固定几类数据字段错误排查起来要比百兆复杂好几倍。7.3 回环测试的变体外环回前面提到的是PHY内部回环还有一种更接近真实情况的外环回把RJ45口通过一根短网线直接连接到同板或者另外一块板的RJ45口形成物理回路。这种外环回可以覆盖网络变压器、RJ45插座、网线、两个PHY的所有物理层环节。做外环回测试时链路建立时间通常比接交换机更长因为两端PHY都不能立刻稳定下自协商状态。收到数据后再做一次数据比较确认透传过程中有没有翻转、丢位、CRC错误。7.4 尝试故障注入法当某一帧数据总在特定位置出错时可以尝试在接收链路中故意做一点小改动比如在MII接口的RXD上强制拉高或拉低某一根线观察错误现象是否跟着变化。这个方法虽然听起来粗糙但快速区分硬件接反和逻辑取反很有效。某个板子上RJ45到网络变压器的差分走线绕了很远阻抗不匹配长包尤其容易出错。后来把故障注入点放在PHY环回测试上确认是PHY直连没问题推给外部物理链路时开始出错这才锁定了PCB走线长度问题。故障注入不是替代正规测试的手段但它能快速缩小排查范围。8. 最终实战建议和几个值得养成的习惯调试FPGA百兆以太网是个系统工程涉及RTL逻辑、PHY芯片、物理链路、上位机协议栈各个层次。真正踩坑之后才发现大多数问题不是因为某一个模块复杂而是模块之间的关系出问题。MII接口作为MAC和PHY之间的交界点往往是最容易暴露问题的地方。根据个人经验有以下几个建议调试顺序永远从底层到上层。先把MDIO打通再看链路然后回环最后才做线上收发。跳级调试很容易被各种表象误导。尽量在RTL里保留更多的可观测性。像FIFO水位、PHY状态寄存器快照、收发帧计数这类信号有条件都接到调试总线上。故障是“一次性偶发”的时候这些内部信号比外部仪器更能定位问题。学会利用抓包工具的双向统计。如果抓包软件统计出大量CRC错误或短帧可以快速判断是对端MAC逻辑问题还是物理链路问题。日志记录比想象中重要。上板调试时脑子会装太多中间状态时间一长很容易忘记某个关键现象。用一个简单的文档记录每次操作、结果、改动效果会好得惊人。最后分享一个小技巧在FPGA内部实现一个简单的“帧计数器”和“错误计数器”并通过串口或MDIO可读寄存器的方式暴露出来。这样即使不接ILA也能在长时间跑测时快速知道当前收发是否正常以及是否有偶发错误帧。百兆以太网调试其实不复杂但挨个层次做透每一步都留下证据后面就能睡个踏实觉。