ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

以太网调试不再瞎忙:MAC与PHY的分工、接口与实战排查

以太网调试不再瞎忙:MAC与PHY的分工、接口与实战排查 做嵌入式或者FPGA开发多少都会跟以太网打交道。我见过不少同事第一次调以太网拿着示波器到处戳抓不到数据就怀疑自己代码写错了最后发现是PHY芯片的strap引脚配置不对或者MAC和PHY之间的接口模式没对上。说白了以太网看起来是一个整体实际从芯片内部到协议栈被严格切成了MAC和PHY两个世界。你只有搞清楚这两个模块各自干什么、中间怎么握手调试的时候才能把锅分清楚否则就是瞎忙活。这篇文章主要面向三类人一是用STM32这类MCU内置MAC、外接PHY芯片做网络功能的嵌入式工程师二是用FPGA做三速以太网、SGMII接口的硬件工程师三是刚接触车载以太网、需要理解100BASE-T1物理层差异的同学。我会把MAC和PHY的分工、工程上的选型组合、接口匹配、寄存器调试、常见坑一次性讲透全程用实际项目说话。1. 别把MAC和PHY混为一谈它们真的不是同一个东西很多人看原理图的时候有个误解一颗以太网PHY芯片上印着厂家Logo就以为PHY就是“网卡”MAC是CPU内部一个看不见摸不着的东西。其实从半导体工艺到协议分工这俩完全不是一个物种。1.1 从一颗网卡芯片拆起理解两层架构拿一颗常见的PCIe网卡芯片来说晶圆上实际封着至少两个大功能块一边是做数据帧处理的数字逻辑也就是MAC另一边是处理差分信号、编解码、线路驱动的模拟电路也就是PHY。有些芯片甚至真的是两个die封装在一起的中间靠MII/RMII这类接口互联。为什么不做成一个die因为数字逻辑和模拟电路在工艺上天然冲突——PHY需要模拟技术来驱动双绞线上的高压差分信号MAC需要大量标准单元来做时序逻辑硬凑在一起良率和性能都很差分开设计再封装集成反而是成本最低的方案。理解了这一点你就明白为什么协议栈里总说“链路层往下是物理层”。MAC负责的是链路层的帧逻辑PHY负责的是物理层的bit搬运。数据流向是CPU把IP包交给MACMAC封装成以太网帧加上前导码和CRC然后把帧的每一个bit通过MII接口送给PHYPHY把这些数字bit编码成适合铜线传输的电平通过网线发出去。接收方向则完全反过来PHY从线缆上恢复时钟和数据解码后交给MACMAC做CRC校验、去前导码、查MAC地址再交给上层协议。1.2 MAC层到底管哪些事MAC的全称是Media Access Control介质访问控制层。它管的事你可以理解成“收发室的主任”帧封装与解封装加上前导码、SFD、目的MAC、源MAC、长度/类型字段最后算一个CRC32校验值写在FCS字段里。接收方向负责剥掉这些附加信息只把净荷交给上层。地址过滤根据目的MAC地址判断这个帧是不是发给自己的不是就丢。网卡可以工作在正常模式、混杂模式、多播过滤模式这些过滤逻辑全在MAC里做。冲突检测与退避半双工古代以太网用同轴电缆大家都在一条线上发数据同时发就冲突。CSMA/CD协议规定发之前先监听冲突了要退避随机时间重传。现代交换网络几乎全双工这个机制用得少了但MAC内部仍然保留这个逻辑。流控Pause帧全双工模式下如果接收端缓存快满了MAC可以主动发一个Pause帧让对方暂停发送。这一点在工业以太网里很关键丢一帧数据可能就意味着一台伺服电机抖动。1.3 PHY层到底管哪些事PHY的全称是Physical Layer物理层收发器。它管的事更偏向“信号翻译官”编码转换不同速率的以太网用了完全不同的线路编码。10M以太网用曼彻斯特编码100M用4B/5B编码加MLT-3电平调制千兆用8B/10B编码万兆用64B/66B。这些编码的目的都是为了保证直流平衡、提供足够的电平跳变来恢复时钟顺便还能检测部分物理错误。自协商上电或插入网线后PHY会在MDI引脚上发出一串快速链路脉冲FLP里面携带自己支持的速率、双工模式、流控能力等信息。两端PHY交换能力后自动选择双方都支持的最高速率。这个过程就是自协商寄存器里能看到结果。信号驱动与接收把编码后的并行数据转成串行差分信号驱动变压器和网线接收方向要从线缆噪声中恢复出发送端的时钟和数据这需要模拟前端技术这也是PHY芯片里最值钱的部分。MAC和PHY的分界线就是MII、RMII、GMII、RGMII、SGMII这些接口。接口左边是数字世界代码可以控制接口右边是模拟世界你只能用示波器看眼图。记住一句话MAC是制度PHY是执行两者必须配套否则链路永远起不来。2. 实际项目里MAC和PHY怎么搭才靠谱工程上选型不是考协议而是看手里有什么。电表、驱动器、车载网关、开发板应用场景不同MAC和PHY的组合方式天差地别。我个人做过的项目基本跑不出四种搭法。2.1 方案一MCU内置MAC 外置PHYSTM32F40x、F407、F429、F7、H7这些芯片内部都集成了以太网MAC支持10/100M部分支持千兆。这种方案最典型MAC自动处理帧、CRC、地址过滤CPU只需要配置寄存器、维护DMA描述符然后收发缓冲区里的数据就行。外接一颗PHY比如LAN8720A、DP83848或者现在国产方案里很常见的裕太微YT8512系列通过RMII或MII接口连接。这个方案的优点是BOM简单、成本低、驱动成熟Linux内核和STM32标准库都有现成的驱动。缺点是MAC侧挂在CPU总线上CPU负载高的时候可能丢包而且MAC和CPU共用内存DMA描述符写错了很容易死机。我用F407做过一个电力采集终端最开始DMA描述符的环形缓冲区长度配错了跑几个小时就卡死一次查了两天才定位到是描述符的OWN位没有正确交接。选PHY的时候要注意如果MCU只有RMII接口时钟最好选50MHz有源晶振直接给PHY而不是选25MHz晶振再让PHY输出50MHz给MCU后者对PCB布线要求高容易把时钟搞脏。现在国产百兆PHY芯片已经相当成熟了引脚基本兼容常见封装调试时对照手册确认strap脚电平就行。2.2 方案二FPGA内置MAC 外置PHYFPGA做以太网设备很常见尤其是需要做协议转换、多端口交换、实时抓包处理的场合。Xilinx有Tri-Mode Ethernet MACTEMACIP核Intel有TSE IP核都支持10/100/1000M三速自适应。外面再接一颗千兆PHY比如RTL8211、88E1512或国产裕太微YTH853接口可以选RGMII或SGMII。FPGA方案的好处是MAC侧逻辑完全自己控制可以做到极低的确定性延迟非常适合做工业实时以太网。但也意味着MAC的每个细节都要自己配置。比如三速MAC的时钟方案10M和100M、1000M下接口时钟根本不一样RGMII的PCB走线延迟补偿、PHY的RX delay配置这些都逃不掉。后续我会专门讲接口配置的坑。2.3 方案三MAC和PHY集成在同一颗芯片里有些芯片把MAC和PHY都做进去甚至还把TCP/IP协议栈硬件化。最典型的就是WIZnet的W5500内部有硬核TCP/IP栈、MAC和PHYMCU只需要用SPI读写它的寄存器就能直接收发TCP/UDP包。非常适合MCU没有MAC、也不想用复杂以太网栈的场景比如温度传感器、灯光控制、一些简单的物联网设备。这个方案的缺点是灵活性低没法做底层自定义速率一般也就10/100M。但调试极其省心只要SPI通信正常socket配好基本就能通。我拿W5500做过一个小型温湿度采集器从画板到联网跑通只花了一个下午这种集成方案在简单产品里依旧有很强的生命力。2.4 方案四独立MAC 独立PHY以及车载以太网的特殊性在一些对性能要求高的场合比如工业通讯网关、车载中央计算单元会采用独立的MAC控制器芯片或FPGA里的MAC核加外置PHY。这里MAC和PHY都是独立物料接口走GMII、RGMII或SGMII灵活性最强但调试难度也最高。车载以太网是一个比较特殊的领域。传统以太网PHY走的是RJ45加变压器两对差分线千兆要四对而车载以太网使用100BASE-T1或1000BASE-T1物理介质只有一对双绞线同时传输收发双向数据靠回波消除技术分离收发信号。这带来的直接变化是不能用普通RJ45不能随便拿示波器探头去戳线缆因为那是差分信号需要专门的差分探头也不能像百兆以太网那样用Hub组网100BASE-T1天然是点对点架构。如果你之前一直调试普通PHY第一次碰车载PHY比如MARVELL 88Q2112、NXP TJA1101最大的感受是寄存器结构完全不同自协商机制也和普通以太网不一样。这个领域确实比消费级以太网更“挑食”但原理上MAC和PHY的分界并没有变仍然是框架性的那套体系。3. MAC和PHY之间的接口MII、RMII、GMII、RGMII、SGMII该怎么选接口决定了MAC和PHY之间怎么交换数据。新手最容易被一堆缩写绕晕其实把这些接口当成“马路宽度”来理解就简单了MII是4车道RMII是2车道但跑得更快GMII是8车道RGMII是8车道但上下班各跑一拨SGMII是1条高速隧道。3.1 MII和RMII百兆时代最常见的两种接口MIIMedia Independent Interface是10/100M时代的标准接口。数据线有TXD[3:0]和RXD[3:0]共8根还有TX_EN、TX_CLK、RX_CLK、RX_DV、RX_ER、CRS、COL等管理信号加起来十几个引脚。时钟由PHY提供100M模式下TX_CLK和RX_CLK都是25MHz每个时钟周期传4个bit10M模式下时钟变成2.5MHz。MII信号多但时序简单适合引脚充裕的MCU或FPGA。RMIIReduced Media Independent Interface就是为了省引脚出现的。TXD和RXD都砍成2位时钟固定50MHz100M模式下每个时钟周期传2个bit10M模式下用脉冲密集度来区分另外把CRS和RX_DV合并成CRS_DV一个信号总共七八根线就能搞定。代价是整体时钟频率提高对PCB和EMC要求更高而且RMII的收发时钟必须同源一般需要一个50MHz时钟同时给MAC和PHY。STM32接LAN8720就是标准的RMII接法这个坑很多人踩过时钟源不一致MAC和PHY跑着跑着就不同步了数据偶发错乱。对比项MIIRMII数据位宽TXD/RXD各4bitTXD/RXD各2bit时钟频率100M:25MHz / 10M:2.5MHz固定50MHz引脚数量约16个约7~9个适用场景引脚充足、时序敏感MCU引脚紧张、成本敏感3.2 GMII和RGMII千兆时代的两种风格千兆以太网速率高MII和RMII扛不住了。GMII把数据位宽扩大到8位时钟125MHz一个时钟周期传8bit正好凑出1000Mbps数据线TXD[7:0]、RXD[7:0]加起来16根加上控制信号总共二十多根线。这个接口在FPGA上常见但布线麻烦芯片引脚也金贵。RGMII则用DDR双沿采样125MHz时钟的上升沿和下降沿各传4bit等效8bit数据线从16根减到8根非常省引脚。代价是时序裕量变小对走线等长和时钟相位要求极高。很多PHY芯片提供RX delay自校准功能比如RTL8211系列可以通过寄存器开启RX内部延迟来补偿PCB走线的偏斜。实际调试时RGMII最常见的现象是能link上但数据CRC狂错基本就是RX_DELAY没配好要么是主控端没有加延迟要么PHY端延迟加了两遍。3.3 SGMII与那个“必须配成MAC模式”的坑SGMIISerial Gigabit Media Independent Interface是把千兆MAC和PHY之间的并行数据转成1.25Gbps的串行差分信号只用两对线一对一发一收。它内部有自己的PCS层和8B/10B编码本质上是一个串行自协商链路。SGMII在FPGA、交换机芯片之间非常流行因为它能大幅减少引脚数量也给布局布线留了更多空间。这里必须说一个非常经典的坑当你用FPGA里的SGMII IP核去外接一颗PHY芯片时SGMII IP核必须配置成MAC模式而不是PHY模式。为什么因为SGMII IP核在功能上既可以被当作MAC侧的串行接口也可以被当作PHY侧的串行接口取决于它对接什么设备。你现在用SGMII IP核接的是外部PHY那么IP核这端顶替的是MAC侧的角色必须工作在MAC模式如果配成了PHY模式两边都以为自己是PHY自协商的时序、PCS层的对齐逻辑、速率协商代码都会错位表现出来就是PHY的link灯是亮的但MAC侧没有任何有效数据甚至ARP都发不出去偶尔能看到几帧错误计数在涨但网络就是不通。排查方法很直接——回到IP核配置界面确认“SGMII Interface Mode”选择的是MAC再检查上电后状态寄存器里的PCS状态机是否进入对齐态。这个坑我见过不止三次十有八九是配置界面上选错了角色。如果FPGA里的serdes模块不带PCS也可以直接用1000BASE-X接口去接PHY那是另一种协议自协商内容更少适合纯光模块对接。但凡是SGMII都有MAC/PHY角色问题配置时先想清楚这一侧到底在扮演什么。3.4 MDIO/MDC管理接口给PHY递小纸条的通道数据平面靠上面这些接口跑业务但PHY芯片的配置和状态监控全靠MDIOManagement Data Input/Output和MDCManagement Data Clock两根线。MDC是时钟由MAC侧或主控侧产生最高频率可以到2.5MHz甚至更高MDIO是双向数据线用来读写PHY内部的寄存器。MDIO的帧格式很简单前导码、起始码、操作码读或写、PHY地址、寄存器地址、数据。一般PHY芯片有5个地址引脚通过上拉下拉配出0到31的地址。实际调试中MDIO读写不成功排除接线问题后九成是PHY地址没对上。有的PHY地址是0有的是1还有的芯片带两个地址用寄存器0x02和0x03读出OUI、0x04/0x05读出型号和修订号核对一下就知道读写有没有成功。4. 从link灯亮到数据包真正通起来一次完整的PHY调试实录理论讲再多不如把一次真实调试过程铺开说。我以一块FPGA板卡外接RTL8211千兆PHY为例走一遍从硬件检查到抓包验证的完整流程。你在MCU上调试RMII/MII PHY时套路完全一样只是寄存器地址和引脚名不同。4.1 上电先看硬件电源、晶振、复位、strap引脚PHY芯片上电后不工作绝大多数是硬件层面出了问题而不是软件。按我的习惯顺序是电源核对每个电源轨电压是否正常纹波是否在规格内。很多PHY有多个电源比如1.0V核心、2.5V模拟、3.3V IO任何一个不对都可能不工作。晶振/时钟用示波器确认时钟引脚有没有起振频率是否准确。有的PHY支持用MAC侧的时钟输入有的必须自带晶振。复位确认RST引脚复位时序上电后要保证低电平脉冲宽度足够有些PHY要求复位信号在电源稳定后至少保持10ms。如果复位时间太短PHY内部寄存器可能处于不确定状态最典型的就是自协商异常。strap引脚PHY的PHY地址、接口模式、时钟模式、LED功能很多是通过复位释放瞬间这几个引脚的电平锁存的。比如RTL8211的PHY地址就是由LED0/1/2的strap组合决定的。如果strap电平被外部电路拉错寄存器读写全乱套。看了这么多调不通的板子strap引脚是最容易被忽略的。检查完这四项再上电看PHY的link LED如果插上网线灯亮了说明物理层收发基本正常问题多半在MAC侧。4.2 用MDIO让PHY开口说话读寄存器验证链路状态硬件确认没问题下一步是读PHY的寄存器。Linux系统下可以用mii-tool或ethtool但更底层一点我习惯用mdio-tools直接访问寄存器最直接。# 查看PHY基本状态寄存器0x01的bit2是link status mdio read eth0 0x01如果读到0x01的bit2为1说明PHY认为自己已经link上了。再看寄存器0x00基本模式控制寄存器可以读到当前自协商配置。寄存器0x05可以读到自协商双方的能力寄存器0x04能读到协商结果。如果你的环境没有mdio-tools在MCU里用GPIO模拟MDIO时序读同一组寄存器效果一样只是慢一些。顺便给一段简单的MCU读PHY寄存器的逻辑核心就是按MDIO时序拉MDC和MDIO引脚uint16_t mdio_read(uint8_t phy_addr, uint8_t reg_addr) { uint16_t data 0; gpio_write(MDC, 0); gpio_write(MDIO, 1); // start of frame gpio_toggle(MDC); gpio_write(MDIO, 1); // op code: read gpio_toggle(MDC); gpio_write(MDIO, 0); gpio_toggle(MDC); // write phy address, 5 bit for (int i 4; i 0; i--) { gpio_write(MDIO, (phy_addr i) 1); gpio_toggle(MDC); } // write reg address, 5 bit for (int i 4; i 0; i--) { gpio_write(MDIO, (reg_addr i) 1); gpio_toggle(MDC); } // turn around: 2 cycles, second cycle phy drives data gpio_set_dir(MDIO, INPUT); gpio_toggle(MDC); gpio_toggle(MDC); // read 16 bit data for (int i 15; i 0; i--) { data | (gpio_read(MDIO) i); gpio_toggle(MDC); } gpio_set_dir(MDIO, OUTPUT); return data; }这段代码虽然是伪代码但时序是对的实际项目里套一下就行。注意读之前先把MDIO设为输入读完后恢复输出否则总线冲突后面的读写全部失败。4.3 MAC侧配置速率、双工、DMA描述符一个都不能少PHY link上之后MAC侧必须和PHY协商结果保持一致。比如RTL8211自协商的结果是1000M全双工那MAC侧就不能还配成100M半双工。对于带内部MAC的MCU一般直接读状态寄存器然后配置MAC的速率和双工模式或者开自动协商跟随。FPGA里的三速MAC也类似接口时钟频率要匹配千兆用GMII/RGMII是125MHz百兆是25MHz十兆是2.5MHz。接着是DMA部分。以STM32的以太网DMA为例要配置DMA描述符链表的地址、缓冲区地址、描述符的OWN位。这个环节的坑多到数不清描述符地址没对齐、缓冲区长度配错、环形描述符数量不足导致DMA停在某个描述符上再也不动。调试的时候可以看DMA中断状态寄存器如果里面对应描述符的error位一直在置位基本就是描述符格式写错了。4.4 抓包验证让数据从线缆上“现形”链路通了不代表数据是对的。我习惯先在Linux下用ethtool看统计ethtool eth0 ethtool -S eth0查看rx_crc_errors、rx_error_bytes、tx_errors等计数。如果CRC错误一直涨说明物理层信号质量有问题可能走线过长、时钟抖动大、RGMII延迟没配好或者变压器中心抽头电容没焊好。如果统计计数正常再用tcpdump抓包验证MAC层行为tcpdump -i eth0 -e -nn -vv其中-e选项会打印MAC层信息你能看到源MAC、目的MAC、EtherType、以及数据负载的字节内容。发送方向主动构造一个自定义帧接收端用tcpdump抓包看payload是否完整能快速确认问题出在MAC层还是上层的IP协议栈。# 发送端 sendip -p ipv4 -p udp -d 0x12345678 -u 50000 192.168.1.2 # 接收端抓包 tcpdump -i eth0 -e -nn -vv udp port 50000 -X-X参数会同时打印十六进制和ASCII内容对照发送的数据就能判断数据在链路传输过程中有没有被篡改。实际项目中很多“网络不通”最后都定位到MAC侧地址过滤配置错误抓包一看帧根本没被MAC接收但PHY统计里明明有数据。5. 常见问题速查表以太网调不通九成是因为这些坑以太网调试久了会发现很多问题高度重复。我把这几年踩过的坑按症状整理成一张速查表现场排查照着顺序做基本能解决八成问题。症状可能原因排查方法PHY link灯不亮电源/晶振/复位异常网线质量差对端设备不工作用示波器确认时钟用交叉线直连另一台设备互换测试MDIO读不到数据PHY地址strap不对MDIO引脚方向冲突MDC频率过高核对strap电平降低MDC频率改用万用表量引脚电平link灯亮但ping不通MAC侧速率/双工和PHY协商结果不一致寄存器0x04对比读PHY寄存器0x04确认速率双工后重新配置MAC数据CRC错误持续增长接口时钟不同源RGMII RX延迟不对PCB走线不满足等长用ethtool -S确认CRC计数调整RX delay配置检查布线SGMII link正常但流量为0SGMII IP核角色配置错误应配MAC模式却配成PHY模式检查IP配置界面确认SGMII角色查看PCS状态寄存器ARP能通但大包不通MTU不匹配长度超过1500字节被丢弃PHY的巨型帧未开启检查MTU设置尝试ping -s 1472测试分片边界数据时而通时而不通复位时序不满足strap引脚受干扰电源纹波大检查复位时序对strap脚加RC滤波改善电源去耦车载100BASE-T1无法link介质是一对差分线不能用RJ45直连对端必须是T1 PHY确认使用T1专用线束确认对端设备也是100BASE-T15.1 车载以太网调试里两个容易被忽略的点车载以太网和普通以太网除了物理介质差异软件层面的配置也有坑。第一个是自协商机制不同100BASE-T1的自协商报文在普通示波器上看起来是噪声没办法像看MII波形那样直接判断必须靠PHY寄存器里的link状态位。所以我调试T1 PHY时会先把寄存器映射表打印出来核对里面有没有master/slave配置这个在T1里很关键一对链路必须一端是master另一端是slave否则起不来。第二个是回环测试。判断MAC层和PHY层各自是否正常最有效的办法是开PHY的loopback模式。很多千兆PHY芯片都有internal loopback或external loopbackinternal loopback让数据从MAC侧发出后直接在PHY内部绕回MAC不经过网线external loopback则把从MAC发出的数据通过PHY发送电路再环回到接收电路。如果internal loopback能通说明MAC和PHY的连接、寄存器配置是好的如果external loopback能通说明PHY的收发链路是好的只有都不通才可能是PCB走线或变压器的问题。这个排查顺序能帮你把问题范围快速缩小不用一上来就怀疑物理器件。5.2 一个实用的小技巧做个简单的PHY寄存器统计页面调试多块板卡时我习惯写一个简单的脚本循环读取PHY的关键寄存器比如寄存器0x01的link状态、寄存器0x04的协商结果、寄存器0x1F左右的收发光功率部分PHY有然后加上时间戳存成日志。这比拿示波器盯半天的效率高得多特别是遇到偶尔掉链路的偶发问题靠人工盯根本不现实。用Python也能快速实现在Linux下用mmap方式直接访问MDIO总线或者调用mdio-tools的库函数网上都有现成代码改一改就能用。6. 结个尾调试以太网胸有成竹比碰运气强我个人调试以太网这么多年最大的心得是分层排查。先确认物理层看PHY的link状态、寄存器、信号完整性再确认链路层看MAC的统计计数、抓包看帧格式最后才看IP层和传输层。很多人一上来就pingping不通就怀疑ARP、怀疑IP其实九成的故障停留在链路层以下你跑到三层去解决问题只会越查越乱。最后分享一个小技巧手头常备一根自制的交叉网线再准备一个带端口link状态和流量统计的交换机。现场调试时用交换机把两端设备串在中间哪个端口没有流量一目了然能帮你省掉大量无效抓包。等什么时候你能做到不看原理图光靠寄存器状态就能判断问题在MAC还是PHY那才算真正跨过了以太网基础这道坎。
返回列表