1. 先把底层的分工搞清楚:MAC和PHY到底各管哪一段
很多嵌入式工程师第一次接触以太网时,最困惑的不是那堆协议栈代码,而是硬件上那一堆引脚到底谁管谁。我以前带过好几个新人,给他们扔一块带网口的核心板,问“MAC在哪、PHY在哪”,十有八九会指着RJ45座子说“这不就是网卡吗”。其实这个理解不能说全错,但如果你想搞明白MII、RGMII、SGMII这些接口,就非得先把MAC和PHY的职责边界掰扯清楚不可。
1.1 从OSI分层说起:MAC管“交通规则”,PHY管“路况信号”
以太网链路从逻辑上分好几层,但硬件上真正干活的就两块芯片:MAC(Media Access Control,介质访问控制层)和PHY(Physical Layer,物理层)。MAC工作在OSI模型的数据链路层,负责组帧、拆帧、地址过滤、CRC校验、冲突检测、流量控制这些“上层规矩”;PHY工作在物理层,负责把MAC送过来的并行数据变成真正能在网线上跑的电平信号,或者反过来把网线上的模拟信号恢复成并行数据。
用一个生活化的类比:MAC和PHY的关系就像调度中心和外勤司机。MAC是调度中心,它关心“车该什么时候发、车牌号对不对、货单要怎么写、验货怎么验”;PHY是跑在路上的司机,它只关心“眼前的这条路是柏油路还是土路、油门该给多大、怎么把车稳稳开到终点”。调度中心要求所有司机都照同一套单据交接,这套单据就是MAC和PHY之间的接口标准。
所以你要记住一个关键点:PHY不解析以太网帧的内容,它只负责把MAC给它的每一个字节变成物理信号扔到线上去,再把线上收到的物理信号变成字节回给MAC。MAC才是真正会“读帧”的那个人。
1.2 为什么中间非要插一个标准接口
既然MAC和PHY分工明确,那问题来了:为什么不能让MAC直接把数据发给PHY,非得定义一套MII、RMII、SGMII之类的接口标准?
最直接的原因是解耦。以太网标准发展了快五十年,速度从10M一路涨到100M、1G、10G,PHY的实现方式早就变了好几代。如果每换一颗PHY芯片,主控里的MAC也要跟着改,那系统厂商就彻底被某一家芯片供应商绑死了。于是IEEE在802.3系列标准里定义了一套MAC与PHY之间的独立接口规范,让MAC一侧只需要关心“往接口上写数据”,PHY一侧只需要关心“从接口上拿数据”,两端互不绑架。
另一个原因是成本。早期的10M以太网,MAC和PHY是可以做到同一颗芯片里的,比如经典的NE2000。但到了100M特别是千兆时代,PHY前端要处理的模拟电路(比如时钟恢复、均衡、编码解码)和MAC的数字逻辑(缓存、DMA、协议状态机)工艺要求差异越来越大——数字逻辑用先进工艺划算,模拟前端用成熟工艺更稳。强行集成在一颗SoC里,面积成本和良率都受不了。于是业界的普遍做法是:MCU/SoC里集成MAC,外接一颗独立的PHY芯片,两者通过标准化接口连接。你用STM32接LAN8720、用ESP32接IP101,本质上都是在干这件事。
除了数据接口,MAC和PHY之间还有一条管理通道,叫MDIO/MDC。MDIO是串行管理总线,MAC通过它去读PHY的寄存器,比如看link状态、协商结果、配置PHY的工作模式、读PHY芯片的ID。后面讲问题排查时会专门提到,很多“连不上网”的坑,都是先从MDIO读ID这一关开始的。
2. 一张表看懂MII、RMII、GMII、RGMII、SGMII的区别
接口的本质就三件事:数据位宽、时钟频率、引脚数量。把这三个参数记住,任何接口你都能快速看懂。我不喜欢一上来堆一堆术语,先用一个类比:接口协议就像两座城市之间的公路,数据就是路上跑的车。有的路修得宽,一次能并排跑8辆车;有的路窄,一次只能跑4辆甚至2辆,但可以靠把车速提上来弥补。
下面这张表是我常用的对照表,建议直接收藏。它把几种主流MAC-PHY接口的关键参数列全了,后面所有分析都围绕这张表展开。
| 接口 | 数据位宽 | 时钟频率 | 10M/100M/1000M是否支持 | 典型引脚数(不含MDIO) | 电平标准 | 适用场景 |
|---|---|---|---|---|---|---|
| MII | 4位 | 25MHz / 2.5MHz | 10M / 100M | 16根左右 | 3.3V CMOS | 老式ARM9、部分MCU,现在用得少 |
| RMII | 2位 | 50MHz | 10M / 100M | 9根左右 | 3.3V CMOS | STM32、ESP32等MCU做百兆联网的主流选择 |
| GMII | 8位 | 125MHz | 1G | 24根左右 | 3.3V / 2.5V HSTL | 早期千兆设计,引脚太多已经被替代 |
| RGMII | 4位(DDR) | 125MHz | 10M / 100M / 1000M | 12根左右 | 2.5V / 3.3V | 千兆嵌入式主力,性价比最高 |
| SGMII | 1位串行 | 1.25Gbps | 10M / 100M / 1000M | 4根差分线(收发各一对) | CML差分 | 高性能交换、多端口路由器、背板互联 |
2.1 MII:以太网接口的元祖
MII(Media Independent Interface),直译是“介质无关接口”,名字里的“介质无关”意思是它不关心底层是双绞线还是光纤,只要PHY支持就行。这是IEEE 802.3u标准里最早定义的MAC-PHY接口,和100BASE-TX对应的Fast Ethernet是同一代产物。
MII接口用4位数据线(TXD[3:0]和RXD[3:0])在时钟的上升沿采样,100M时时钟25MHz,10M时降到2.5MHz。它还有收发控制信号(TX_EN、TX_ER、RX_DV等)和独立的收发时钟(TX_CLK、RX_CLK)。引脚数量算下来相当可观:不算MDIO,光数据相关的信号就接近16根。
MII的问题非常直观——太占引脚。稍微算一下:4位发送数据、4位接收数据、两个时钟、四五根控制线,这还只是百兆。如果要做千兆,位宽翻倍到8位,时钟升到125MHz,引脚数接近25根甚至更多,这在现代高密度PCB设计里完全不可接受。所以MII现在更多是作为教学概念存在,你在实际产品里见到它的概率,比见到ISA总线还低。但理解MII很有价值,它是后面所有接口演化的基础。
2.2 RMII:引脚减半的“省脚方案”
RMII(Reduced Media Independent Interface,精简介质无关接口)就是冲着省引脚去的。它的思路很直接:把数据位宽从4位砍到2位,让收发共用时钟,把控制信号合并精简。代价是时钟频率从25MHz提高到50MHz,靠频率翻倍补回位宽减半损失的带宽。
RMII收发数据各只用2根线,加上一条50MHz参考时钟、TX_EN发送使能、CRS_DV载波侦听/数据有效复用信号,总数压到9根左右。相比MII,板子上省出来的GPIO可以拿去干很多别的事,对STM32、ESP32这种引脚不算宽裕的MCU来说非常友好。
RMII有一个特别容易踩的坑是参考时钟的归属。MII的收发时钟分别由PHY或MAC提供,RMII却把收发时钟合并成了一个50MHz的REF_CLK,这个时钟到底由谁产生,协议里并没有强制规定,于是不同PHY芯片的接法差异很大。有的PHY要求MAC提供50MHz(比如LAN8720在“从晶振/外部时钟输入”模式下),有的PHY可以自己从25MHz晶振内部倍频出50MHz然后输出给MAC。我最开始调STM32F407+DP83848的时候,就是在这个REF_CLK上卡了两天——不是没接,是时钟源方向搞反了,导致RMII链路数据全乱。
2.3 GMII与RGMII:千兆接口的“重型”与“轻量”
GMII(Gigabit Media Independent Interface)是千兆时代的MII:数据位宽8位,时钟125MHz,收发分别有独立时钟和八位数据线,全套信号加起来24根往上。数据吞吐量算下来正好是8bit×125MHz=1Gbps,和千兆以太网的线速率对齐。GMII在设计上没有任何问题,问题还是那句话:引脚太多,而且125MHz的8位并行总线对PCB走线等长要求比较苛刻,做起来非常痛苦。所以GMII在真实产品里很快就被RGMII取代了。
RGMII(Reduced Gigabit Media Independent Interface)的思想和RMII一脉相承:靠提高时钟利用率来压低引脚数。它的核心招数是DDR(Double Data Rate,双沿采样)——在125MHz时钟的上升沿和下降沿都采样数据,用4位数据线实现等效8位位宽。换句话说,一组TXD[3:0]在时钟上升沿送低4位、下降沿送高4位,就相当于原来的8位数据线;接收方向RXD[3:0]同理。这个设计把千兆接口的引脚数砍到12根左右,在性能和引脚压力之间取得了极好的平衡,所以RGMII成了目前千兆以太网最重要的板级接口。
RGMII同时向下兼容10M/100M:低速模式下时钟相应降低,DDR采样依然生效。这里要提醒一句,很多平台的RGMII是“降级复用”的,如果你的主控手册写的是“支持RGMII”,基本就不需要再考虑GMII了,直接按RGMII布线就行。
2.4 SGMII:串行差分,走向高速的基础
SGMII(Serial Gigabit Media Independent Interface)把并行数据彻底抛弃,改用一对发送差分线、一对接收差分线,以1.25Gbps的串行速率传输数据。因为多了8B/10B编码开销,1.25Gbps的实际有效带宽正好是1Gbps,和千兆以太网的流量对应。
SGMII为什么能成为更高端平台的主流?根本原因在于串行差分信号的扩展性。并行总线上到一定速率就压不动了(信号偏移、串扰、等长布线都头疼),而差分串行可以轻松跑到几个G甚至几十个G。把MAC和PHY之间的接口做成串行之后,PCB布线从“12根线等长”变成“两对差分线严格控制差分阻抗”,面积、层数、调试难度都大幅下降。而且同一组SerDes物理通道,只要配置不一样,既可以当SGMII接以太网PHY,也可以当PCIe或者其它协议用,这对交换机、路由器这类多端口设备特别友好——所以很多交换芯片的MAC侧都是清一色的SGMII甚至QSGMII(一个串行通道承载4路SGMII)。
SGMII有一个容易混淆的点:它和链路另一端的网络接口(比如RJ45那个口)是完全独立的两码事。SGMII只负责主控MAC到PHY芯片之间的板级传输,PHY芯片到了线缆侧还是要按1000BASE-T的物理层规范做编码和调制。有人以为用了SGMII就相当于“光口”或者“不用PHY了”,这是不对的。只有在MAC自带SerDes并直接外接光模块的场景下,才不走标准PHY,那又属于1000BASE-X的范畴了,不是今天讨论的SGMII接法。
3. 实际选接口时到底看什么
很多教程把每个接口的引脚画了一遍就结束了,看得人头晕,真到自己画板子、写设备树的时候还是不会选。其实选接口的决策链路非常清晰,就是一条链:主控支持什么 → 速率目标是什么 → 引脚预算和PCB条件允许什么 → PHY芯片有什么。下面把每个环节的实际考量拆开讲。
3.1 主控和PHY的“菜单”决定了你的选择范围
选接口的第一步不是看接口协议本身,而是先打开主控芯片的数据手册,看SoC集成的MAC到底支持哪几种接口模式。比如STM32F407的以太网MAC支持MII和RMII,板上通过一个引脚(ETH_MII_RMII_SEL)切换;ESP32内置MAC支持RMII;很多Linux网关上的i.MX6ULL支持MII/RMII,而到了i.MX8M系列、瑞萨RZ/G系列,往往直接支持RGMII;Xilinx的Zynq-7000内部MAC可以引出RGMII,也可以通过软件配置走SGMII到PS侧的SerDes。
反过来也要查PHY芯片支持的接口模式。LAN8720A只支持RMII,IP101GRI支持MII/RMII,DP83848支持MII/RMII,RTL8211F支持RGMII(还有SGMII版本),88E1512同时支持RGMII和SGMII。极限情况下,一颗PHY的接口模式和速率如果和MAC对不上,后面再怎么调软件都没用。所以选型第一步就是把两端的手册并排打开,画出交集,交集里的接口才是你的可行集。
这里有一个很现实的建议:如果MCU同时支持MII和RMII,直接选RMII。省下的那六七个引脚,在你后续增加传感器、串口、LCD或者调试接口时,价值远大于MII带来的那一点点理论优势。MII只在某些特殊场景才值得选——比如MCU的RMII和某个外设产生了引脚复用冲突,退回去用MII反而省事,但这种场景太少见了。
3.2 引脚预算与PCB布局的现实压力
选接口就是一场引脚交换游戏。MII大约16根信号,RMII大约9根,RGMII大约12根,SGMII只要4根差分线。在PCB上,每多一根信号线,就意味着多一份布线面积、多一些过孔、多一些等长约束,有时候甚至直接决定你的板子能不能从4层降到2层。
百兆RMII流行的一个很大原因就是它对PCB要求非常宽容。RMII是50MHz单端信号,只要走线不要拉得太夸张,基本不用做等长控制,2层板也能跑得很稳。我之前做过一块用ESP32+LAN8720的最小板,RJ45的差分线做了10mil间距差分对,RMII信号线随意走,实测跑TCP下载能到90Mbps以上,非常稳定。RGMII就不一样了,125MHz的DDR信号,收发数据和时钟之间必须做等长控制,一般要求走线偏差控制在±50mil以内,4层板起步,否则时序窗口一紧张就会出现随机丢包——这类问题软件解决不了,只能改板。
SGMII对PCB的要求是另一种思路:它不管等长,但差分阻抗必须控制好。100Ω差分对,走线按层叠计算好线宽线距,收发两对尽量靠近PHY和MAC侧的SerDes引脚。SGMII对信号质量的容忍度比并行总线高一个量级,板子设计得好,走线长一点问题也不大。
3.3 DDR时序与参考时钟:RGMII和RMII最麻烦的两个坑
如果说引脚数量是选接口的“面子”,那时序和时钟就是“里子”,绝大多数工程师踩的坑都在这里。
RGMII的核心是DDR双沿采样,它对CTS(时钟到数据的建立保持时间)非常敏感。IEEE规定RGMII的发送侧时钟由MAC和PHY各自沿PCB走线传输,“源同步”的意味很强。说白了就是,时钟沿着PCB走,数据也沿着PCB走,接收端在时钟沿采样数据,双方必须在时序窗口内稳定。这就引出了PCB等长约束和FPGA里那一堆set_input_delay、set_output_delay约束——如果你在Zynq或者其它FPGA上做RGMII,绕不开IDDR原语和IO时序约束。这部分在第四章的FPGA案例里我再详细展开。
RMII的坑则主要集中在50MHz REF_CLK的方向上。我碰到过的典型情况有两种:一是某些PHY(比如LAN8720)的REF_CLK既可以由MAC提供也可以由外部晶振提供,如果模块上已经焊了50M晶振,MAC侧就不能再输出时钟,否则两路时钟打架,链路起不来;二是STM32F407做RMII时,推荐方案是从MCO1引脚输出50MHz给PHY的REF_CLK,但MCO1复用的是PA8引脚,如果你把PA8当普通GPIO用了,PHY就没时钟,整个RMII直接瘫痪。这两种情况在调试的时候都极其隐蔽——因为软件配置看起来全对,波形却不对。
4. 几个典型项目的接口搭配思路
光讲理论不给实战方案等于没讲。我挑了三类最常见的硬件组合,把接口选择和关键注意事项写清楚,你可以直接当设计参考用。
4.1 ESP32 + LAN8720:低成本入门首选,RMII
ESP32内置MAC只支持RMII,LAN8720A是市面上最便宜的百兆PHY之一,只支持RMII,这俩天生一对。接线非常简单:RMII的TXD[1:0]接LAN8720的TXD[1:0],RXD[1:0]接RXD,REF_CLK用50MHz,TX_EN、CRS_DV各自对上,再加上MDIO/MDC管理总线,总共没几根线。
这里的重点有三个。第一,ESP32的REF_CLK可以自己产生也可以让LAN8720模块从外部晶振取时钟,但如果你买的是常见的“ESP32-LAN8720模块”,它默认方案是ESP32输出50M REF_CLK给LAN8720。第二,PHY地址LAN8720默认是0或1取决于PHYAD0引脚,很多模块把PHYAD0接地(地址0)或接3.3V(地址1),买回来先确认一下,否则驱动里配不对地址,MDIO读不到ID,以太网起不来。第三,LAN8720的复位引脚最好用ESP32的GPIO控制,不能悬空,上电后要保证一段时间的低电平复位,再拉高进入工作状态。后面的避坑章节会把这三个问题再展开成完整的排查清单。
4.2 STM32F407 + DP83848:工业联网经典,RMII
STM32F407的MAC在STM32家族里算是很强的一代,支持MII和RMII,靠一个专用引脚切换。我最早做联网数据采集时用的就是F407+DP83848,当时选RMII是看中省引脚。F407做RMII的典型接法是:MCO1引脚(PA8)输出50MHz时钟给DP83848的XI脚,RMII的TXD[1:0]、RXD[1:0]、TX_EN、CRS_DV分别接到PHY对应引脚,配置好复用功能。
折腾过的人都知道,F407的RMII有两大坑:一是MCO1初始化和时钟树配置必须正确,否则50M时钟出不来,PHY直接摆烂;二是RMII模式下ETH外设的引脚复用要全部配置成AF11,漏配一个RXD引脚,帧接收就断断续续。别问我怎么知道的——这两件事我在带新人时见过太多次了。还有一点,DP83848的X1脚接时钟,X2脚不接晶振时通常要悬空,这个细节手册上写了,但很多人画原理图时习惯性地给X2串个电容接地,反而引入了不稳定的寄生参数。
4.3 FPGA + RGMII PHY:时序约束是关键
FPGA场景下,MAC往往是你自己用逻辑写的,或者用Xilinx的Tri-Mode Ethernet MAC IP核。PHY侧用RGMII几乎是默认选择,比如Zynq-7000接RTL8211。RGMII对FPGA工程师最大的挑战不是逻辑,而是IO时序约束。
在Zynq的PL端做RGMII,接收方向要用IDDR原语把DDR数据拆成两路单沿数据,同时要给RXC设置合理的input delay。Xilinx的官方做法一般是在约束文件里用set_input_delay -clock [get_clocks rx_clk] -max 2.0之类的命令,把Tsu/Th关系约束清楚。如果你不做这些约束,综合实现后的时序报告大概率一片红,跑起来随机丢包甚至完全不通。这个问题的典型症状是:100M模式没问题、千兆模式下ping不通,因为低速时时序裕量充足,DDR采样根本不会错;到了千兆125MHz,时序窗口收紧,没有约束的路径全暴露出来。我还碰到过一种更隐蔽的情况,同一套RGMII设计,在不同批次PHY芯片上表现不一样,有些能跑千兆、有些只能跑百兆,最后查下来就是RXC采样沿选错了——RGMII标准里建议把RXC反相后再采样数据,但反过来用的PHY也有。建议在调试时先用示波器看RXC和RXD的相对相位,再决定要不要加IDELAY。做RGMII的调试,示波器不是可选项,是必选项。
4.4 高性能平台 + SGMII:走向更高端口密度
在多网口网关、核心交换机这类设备里,接口选择的逻辑完全不同。这类平台的主控或交换芯片往往自带SerDes通道,比如Zynq UltraScale+的PS-GTR、各种网络处理器的SerDes,片上MAC通过SGMII接口直接和PHY芯片对接。比如88E1512支持SGMII和RGMII两种模式,实际项目里如果主控在PCB布局上离PHY比较远,就优选SGMII——4根差分线比12根并行线好拉得多,而且差分线抗干扰能力也强。
SGMII模式下还有一个优势:不同PHY之间的“背靠背”测试很方便。什么意思呢?当你想在没有主控的情况下验证两颗PHY是否正常,可以把PHY1的SGMII(或1000BASE-X)侧接到PHY2的相同接口,两边都配置成SGMII或自适应模式,然后从PHY1的RJ45口发送数据,走内部回环验证链路质量。这在产线测试和故障定位中都很有用。
还要提一下车载以太网。车载上用100BASE-T1或1000BASE-T1,物理层编码方向跟传统RJ45完全不同,但MAC和PHY之间的接口并没有变——依然是从MAC侧引出RMII/RGMII到车载PHY芯片。所以你在车载ECU的原理图里看到的TJA1101、MARVELL 88Q2112这些车载PHY,数据侧接口和普通PHY基本兼容,反而是线缆侧的共模电感和ESD防护需要额外关注。理解了MAC-PHY接口,车载以太网对你来说就只有物理层需要重新学。
5. 避坑实录:我踩过的那些PHY接口的坑
调试经验这种东西,文档里经常只有一句话,实际踩一遍就是大半天。我把这几年遇到的典型问题整理成一个排查列表,每个问题都写了症状和解决思路,希望能帮你少走弯路。
5.1 LAN8720的三板斧:时钟、复位、PHY地址
ESP32+LAN8720这个组合是开源社区用得最多的低成本以太网方案,但网上每天都有新人卡在同样三个地方。
第一是REF_CLK时钟方向。LAN8720的第2脚(PHYAD0/CLKOUT)和第5脚(REF_CLK)在不同接地/配置下,时钟来源会变。常见模板电路是外部25M晶振接LAN8720,PHY内部倍频/输出50M给MAC;也有模块把50M REF_CLK输入给LAN8720,由MAC提供时钟。如果你买的模块没有原理图,先测一下REF_CLK引脚上到底有没有50MHz波形,再决定驱动里怎么配置。
第二是复位时序。LAN8720的复位低电平时间不能太短,我习惯用ESP32的GPIO控制复位脚,上电后保持低电平10ms以上,再拉高并延时一段时间等PHY稳定。如果复位时间不够,PHY内部寄存器状态不确定,MDIO可能能读但就是link不上。
第三是PHY地址。LAN8720的SMI地址由PHYAD0脚决定,接地是0,接高是1。驱动里配置的PHY_ADDR必须和硬件一致,如果配错了,MDIO读ID会返回全1或者0xFFFFFFFF,初始化直接失败。很多模块为了兼容性,默认把PHYAD0接地(地址0),你从ESP-IDF示例程序里复制过来的代码如果写成1,就要改回来。这个检查只需要一个电表测引脚电平,比乱改软件快多了。
5.2 千兆RGMII回环测试不过:问题出在时序约束
我做一块Zynq+88E1512的板卡时,遇到了一个特别磨人的问题:单板静态测试一切正常,两台设备对跑千兆吞吐,过一段时间必掉一次包;降到百兆,跑一晚上都稳如老狗。开始怀疑是PHY芯片个体差异,换了三片都一样。最后开着Vivado的时序报告一点点看,发现问题出在RGMII RX路径上——软件里我只做了set_input_delay,但没有把RXC时钟约束成与数据同源的“源同步时钟”,导致实现工具对时钟树做了激进优化,布线到了临界状态。这属于典型的“功能验证全过、长时间运行翻车”。
给大家一个经验值:RGMII的收发时钟和数据等长,在FR4板材上按1 inch≈170ps延迟估算,差分阻抗控制好之后,PCB走线差50mil以内基本不用加IDELAY;超过100mil或者跨层换过孔,就必须在约束里显式补偿了。如果你用的是Xilinx器件,直接用set_input_delay配好-min和-max,再用set_clock_groups把RXC和其他时钟隔离,不要省这一步。也不要迷信“板子画得好可以靠运气”,时序约束必须写,因为综合实现工具的算法不是为“刚好能跑”设计的,它需要明确的目标来优化。
5.3 MDIO读不到ID:不是接口配对错误,是引脚复用表骗了你
MDIO读不到PHY的ID,这个坑我在STM32上掉过一次,也在Linux设备树调试时见过别人掉过一次。现象很统一:ETH外设初始化失败,ethtool或者裸机调试打印读回来的寄存器全是0xFFFF。第一反应都会去查PHY地址,结果地址没错,MDIO的引脚电平也正常。真正的问题在引脚复用表。
STM32F407的MDIO在PA2,MDC在PC1,但如果板子上一开始把PA2复用成了USART2_TX,MDIO引脚就不能正常输出802.3帧格式的MDC/MDIO波形了。这种问题最坑的地方在于,你用万用表量PA2电平,看起来有波形,但波形完全不是MDIO的协议格式。排查方法很简单:用示波器同时抓MDC和MDIO,对照802.3协议看MDIO命令帧是否完整,MDC频率是否在2.5MHz以内。频率太高是另一个常见原因——PHY的MDIO接口一般最高支持2.5MHz,但有的MDC端口在软件里没有做分频,直接跑在了几十MHz,读寄存器当然一片乱码。
5.4 RGMII千兆模式下随机丢包:先查时钟相位,再查电源纹波
RGMII的另一个经典隐性故障就是“千兆随机丢包”。如果你已经把PCB等长、IO时序约束都做过了,还是丢包,那就把怀疑对象从信号完整性转到电源完整性上。PHY的数字电源和模拟电源如果共用一组LDO,或者去耦电容放得太远,在高负载时电源纹波会串进时钟频率综合器,造成时钟抖动加大,RGMII的建立保持时间裕量就会被吃掉。
之前有个项目,PHY芯片的AVDDH和DVDDH都是3.3V,设计时图省事直接接在一起,结果千兆大流量下RGMII偶发CRC错误包,抓包根本看不出来,因为错在PHY内部就已经恢复了。最后在AVDDH处加了一个磁珠和10μF+100nF去耦电容才解决。所以画原理图的时候,即使数据手册允许模拟和数字电源直连,也建议按“模拟电源经过磁珠或小电阻再去耦”来做,成本几乎为零,却能避开一类特别难查的问题。
5.5 时钟源、帧间隔和链路稳定性:测试时容易被忽视的细节
最后聊一个不那么起眼但很实际的点:帧间隔。以太网标准规定两个帧之间要有最小帧间隔(IFG),百兆下是0.96μs。这个参数看起来是MAC侧的事,但当你调RMII接口时,如果MAC侧配置的IFG过小(比如某些IP核默认配置成40bit而不是96bit),PHY的发送FIFO在高负载下会处理不过来,表现出来的症状就是吞吐上不去、偶发丢包。用iperf测试时如果发现“单线程上不去,多线程正常”,除了排查中断绑定之外,也回头看一眼MAC IP核的IFG配置。
还有一个和时钟源有关的细节:很多RMII方案里,REF_CLK的占空比要求比较严(45%~55%),如果你用了MCU内部时钟直接分频输出,占空比可能达不到PHY的要求。实测下来,用主控的PLL输出基本都能满足,但用简单计数器分频出来的时钟就危险。所以RMII接法能用PLL输出或者外部有源晶振就尽量用,省一个时钟芯片的钱,后面可能要在调试上花十倍的精力。
6. 接口学习的“钱花在刀刃上”思路
把前面这些接口全看一遍之后,很多人会纠结:我到底要不要把所有接口都学会?我的答案是,对90%的应用场景,认真玩透RMII和RGMII就够了。MII了解原理即可,GMII基本可以跳过,SGMII等真要做交换机、多网口网关时再深入也来得及。
我个人的学习习惯是:先拿一块ESP32+LAN8720的板子,把百兆RMII调通,通过MDIO把PHY的所有关键寄存器读一遍,搞清楚link状态位、协商结果、中断标志都在哪里;然后再拿一块Zynq或者带RGMII的ARM板,把RGMII时序约束写出来,用示波器实测TXC和TXD的相对时序。这两步做完,你对“MAC和PHY是怎么配合的”会有一种很难忘的体感——比看十篇文章都管用。
实际去翻芯片手册的时候,也别把几百页的datasheet从头看到尾。重点只看三部分:接口时序图、寄存器表、参考原理图。时序图告诉你信号怎么对齐,寄存器表告诉你状态从哪里读,参考原理图告诉你电容电阻怎么摆放。三个都过一遍,再难调的PHY也就那么回事。
最后说个我一直以来的习惯:画原理图之前,先把MAC侧和PHY侧的每个接口信号列成一张Excel表格,标清楚方向、电平、时钟域、复位归属。这看起来多花了半小时,但真正到了打样回来调试的时候,你会发现这张表能帮你省好几个通宵。选接口选到最后,选的不只是协议,更是你对整条链路可控度的把握。