接手一块带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/GMII,MII接口时序裕量更大,调试难度低不少。同一套代码,如果先把百兆调通,后续再往千兆迁移,也能少走很多弯路。
另一个原因是百兆PHY的功耗和发热都更可控。对一块没有主动散热的小型采集板来说,这两个指标很关键。再加上咱们用的线缆和连接器都是现成的百兆方案,整体成本压缩得很明显。
2. 调试环境和工具准备
2.1 硬件环境清单
调试之前,先把环境盘一遍。我的调试台面包括:
- FPGA开发板,带一个百兆以太网口,PHY芯片的方案比较主流,MDIO地址默认是0,PHY地址可以通过外部引脚配置。
- 一个千兆交换机,兼容百兆速率,方便组网。
- 一台普通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_CLK:25MHz,由PHY提供,FPGA发送数据时要用它做同步时钟。
- TX_EN:发送数据有效信号,只有它为高时,TXD[3:0]上的数据才是真的。
- TXD[3:0]:发送数据总线。
- RX_CLK:25MHz,由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 ID | PHY地址配置错误或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提高到125MHz,DDR模式下上下沿都采样,相当于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,也能在长时间跑测时快速知道当前收发是否正常,以及是否有偶发错误帧。百兆以太网调试其实不复杂,但挨个层次做透,每一步都留下证据,后面就能睡个踏实觉。