ARTICLE DETAIL

资讯详情

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

ZYNQ上LwIP访问MDIO/MMD寄存器踩坑:从时序到PHY地址全解析

ZYNQ上LwIP访问MDIO/MMD寄存器踩坑:从时序到PHY地址全解析 ZYNQ-7000上跑LwIPPHY用的是Microchip KSZ9031RNX网络能通ping也通但代码里只要想通过MDIO读MMDMDIO Manageable Device寄存器读回来的不是0x0000就是0xFFFF。这个现象我排查了整整两天最后发现根本不是PHY坏了而是从MDIO时序、PHY地址到MMD访问序列一个环节一个环节地踩坑。如果你也在ZYNQ LwIP KSZ9031这个组合上被MMD折磨这篇文章应该能帮你少走很多弯路。1. 不只是寄存器问题KSZ9031的MMD访问机制到底怎么回事1.1 Clause 22寄存器只有32个千兆PHY根本不够用标准上PHY的基础寄存器空间只有32个0x00-0x1F由IEEE 802.3 Clause 22定义。其中0x00是控制寄存器、0x01是状态寄存器、0x02和0x03是PHY ID寄存器剩下的就是厂商自定义区域。这套结构在10M/100M时代足够用了到了1000M就明显不够自动协商参数、Master/Slave配置、链路诊断、温度传感器这类扩展功能全都要往这32个寄存器的厂商保留区里塞塞不下的就只能另想办法。IEEE在后续的Clause 45里定义了新的管理框架把PHY内部的寄存器组织成多个Device每个Device拥有独立的寄存器空间最多65536个寄存器。KSZ9031RNX完整支持这套MMD机制但问题来了ZYNQ的PS端GEMGigabit Ethernet MAC控制器在绝大多数BSP配置下只发Clause 22格式的MDIO帧硬件上不支持直接发Clause 45帧。所以你要访问MMD空间就得通过Clause 22的两个保留寄存器0x0D和0x0E来“开后门”。1.2 0x0D和0x0E一对传送门寄存器KSZ9031的0x0D寄存器叫MMD Address Control0x0E叫MMD Data Control。想要读任何一个MMD寄存器必须按以下顺序走写0x0D写入目标Device地址比如1代表PMA/PMD2代表WOL7代表Auto-Negotiation等。写0x0E写入目标MMD寄存器地址。读0x0D得到目标MMD寄存器的低16位数据如果需要32位数据再读0x0E拿高16位。关键点在于这一步一步之间每一个动作都是普通的Clause 22读写。也就是说GEM控制器不需要任何特殊配置你要做的只是用软件把0x0D/0x0E的间接访问序列拼出来。1.3 为什么LwIP场景下绕不开MMDLwIP本身只做网络协议栈PHY的链路状态、速率、双工模式完全靠外部代码去读PHY寄存器获取。通常大家会用Clause 22的0x01状态寄存器判断link up但1000BASE-T的协商细节比如是不是真的协商上了千兆、主从模式有没有冲突这些信息藏在MMD空间里。更麻烦的是有些板子的LwIP适配层在初始化时会根据PHY ID自动识别PHY型号然后对特定型号执行寄存器配置。如果PHY ID读取失败或者MMD配置写入失败就会走默认路径最后表现为“网络能通但速率不对”“千兆经常掉线”“协商反复失败”。所以我一直把PHY的MDIO管理通道当成整个网络调试的第一优先级它读不准上层LwIP再怎么调都白搭。2. 我在LwIP初始化链路里定位“读不了”的完整过程2.1 LwIP的PHY枚举流程在常见的ZYNQ BSP LwIP代码中以太网初始化路径大概是low_level_init调用PHY初始化函数里面反复读PHY ID寄存器直到读到非0xFFFF或超时随后根据PHY ID判断出芯片型号最后进入链路状态轮询。我遇到的现状是PHY ID能正常读到链路也能up但一到读MMD就废。这说明基础MDIO读写路径没问题问题出在MMD的间接访问序列或者某个与MMD相关的配置上。这里有一个很容易被忽略的细节PHY ID能读到不代表MDIO时序就是健康的只能说在低频读操作下侥幸成功。2.2 用最小测试程序把LwIP隔离掉为了不让LwIP的线程调度、延时逻辑干扰判断我建了一个最小测试工程直接在main函数里通过BSP提供的XEmacPs_MdioRead/XEmacPs_MdioWrite做寄存器读写。只做三件事读寄存器0x02/0x03确认PHY ID。按前面说的三步序列读MMD寄存器。把每次操作的返回值打印出来。这一步非常关键。如果你在LwIP框架里排查代码层层调用你很难分清楚是上层初始化时序问题还是底层驱动问题。隔离得越干净越好。我当时把PHY地址也作为变量传进去来回换地址扫很快就看到了规律。2.3 用返回值特征先判断大方向调试时我总结了一张表后来排查其他板子也一直用建议直接保存现象可能原因读到0xFFFFMDIO总线无应答。优先查PHY地址、PHY上电复位、MDC时钟频率、MDIO引脚上拉读到0x0000PHY有应答但MMD访问序列或参数不对或者目标MMD寄存器本来就为00x01寄存器link bit正常MMD读数恒为0x0000总线正常问题锁定在0x0D/0x0E的间接访问用法上实际调试中90%的“MMD读取不了”问题根因不在MMD本身而在PHY地址或MDC时序。你要是直接钻进MMD访问序列里深挖反而容易把自己绕进去。3. 五个我实测踩过的坑按出现频率排序3.1 PHY地址与代码硬编码不一致KSZ9031的PHY地址由PHYAD[2:0]引脚决定。默认情况下很多评估板配置出来的是0x7但有的板子会通过上下拉电阻改到0x0、0x1、0x1F等。在ZYNQ的BSP里PHY地址通常写在xparameters.h中比如XPAR_XEMACPS_0_PHY_ADDR你如果直接用这个默认值去访问实际硬件一旦不匹配MDIO读出来的结果就是0xFFFF。排查方法很简单直接读0x00/0x01寄存器如果地址不对这两个寄存器也读不对。另外注意ZYNQ GEM的MDIO控制寄存器里会写入PHY地址这一步错了后面全白费。如果你用的是多路扫描逻辑它遍历0-31个地址时遇到不存在的PHY也会返回0xFFFF需要看日志里的MDIO timeout消息来区分。3.2 MDC时钟频率超标MDIO规范允许的最高MDC频率是2.5MHzClause 22。虽然很多PHY硬件上能承受更高频率但板级走线、上下拉电阻、噪声干扰会让高速状态下的读取变成随机失败。ZYNQ GEM的MDC时钟是对处理器时钟分频得到的分频系数字段在MDIO控制寄存器的高字节。如果按BSP默认配置在较高处理器频率下跑MDC可能被顶到十几甚至二十几MHz。这种时候PHY ID偶尔能读出来但MMD这种连续多个MDIO帧的操作就很容易出错。解决方法是把MDC降到2.5MHz以下。我当时把分频系数调大让MDC降到大约1.8MHzMMD读取立刻稳定了。这一步不需要改硬件跑一个测试循环多读几次就能验证。3.3 MMD Device Address选错KSZ9031把寄存器分成多个Device组每个组有独立的Device Address。常见的如PMA/PMD对应Device 1WOL相关对应Device 2Auto-Negotiation相关对应Device 7。不同datasheet版本里给出的编号可能不一样而且网上部分博客抄来抄去容易把人带偏。我踩过最坑的一次想读自动协商状态代码里用了Device 2结果读回来的值稳定为0x0000。表面上看PHY有应答数据也不像0xFFFF那么刺眼但内容完全不对。后来对着官方datasheet一个个Device翻才找到正确的编号。所以一旦发现读取值为0x0000并且怎么调都不变优先怀疑DEV_ADDR选错。3.4 0x0E写入后立即读0x0D时序竞争部分MDIO控制器在硬件上会做帧间隔但PHY内部处理“写0x0D选择Device 写0x0E选择寄存器 读0x0D取数据”这个间接访问时需要几个内部时钟周期来切换目标寄存器。如果第二步写完0x0E后立刻发起对0x0D的读取PHY可能还没完成切换读回旧值或0。ZYNQ GEM的MDIO控制器本身还好但在某些外部MDIO控制器或者总线拓扑复杂的板子上这个现象会更明显。稳妥做法是在两步之间加5-10微秒延时。我当时为了图稳在每步操作后都加了udelay(10)实测没有引入额外问题反而把偶发失败消灭了。3.5 复位后等待时间不足KSZ9031上电后需要完成内部自检和SerDes校准这个时间比很多人想象中长。标准建议是复位释放后至少等150ms再访问MDIO。很多LwIP初始化代码只延时几十毫秒在电源轨上升缓慢或者PHY复位脚和FPGA复位脚共用的时候就会出现随机性失败。这种失败最迷惑的点在于它不是必现的十次启动有八次正常两次读不到PHY ID或者PHY ID正常但MMD读出来是乱的。如果你们硬件上用了独立的PHY复位脚记得在初始化时做足够长的延时如果PHY复位脚和FPGA配置复位连在一起还要考虑FPGA配置完成时间。4. 一个稳定可用的MMD读取函数直接抄4.1 函数本体基于Xilinx BSP的XEmacPs_MdioWrite/XEmacPs_MdioRead我最终把MMD读取封装成下面这样#include sleep.h #include xemacps.h u16 ksz9031_mmd_read(XEmacPs *GemInstance, u32 PhyAddr, u32 DevAddr, u32 RegAddr) { /* 第1步把MMD设备地址写入0x0D */ XEmacPs_MdioWrite(GemInstance, PhyAddr, 0x0D, DevAddr); udelay(10); /* 第2步把MMD寄存器地址写入0x0E */ XEmacPs_MdioWrite(GemInstance, PhyAddr, 0x0E, RegAddr); udelay(10); /* 第3步从0x0D读回低16位数据 */ return XEmacPs_MdioRead(GemInstance, PhyAddr, 0x0D); }我只在这里实现了16位读取。KSZ9031的大部分MMD寄存器都是16位真要读32位寄存器再补一次对0x0E的读取作为高16位即可。这个函数里我没有加重试逻辑因为试错成本太高一旦读出来的值不正常重试只会掩盖配置错误不如直接返回让你去查根因。4.2 为什么必须拆成三次MDIO操作有些PHY驱动会直接发Clause 45帧一个帧就能完成寄存器访问。但ZYNQ GEM的MDIO控制器只支持Clause 22帧格式所以必须老老实实拆成三次普通操作。这个限制来自硬件不是软件能绕过去的。如果你想用Clause 45直通模式需要换支持该功能的控制器不是改个驱动就行。另外XEmacPs_MdioWrite的第三个参数是Clause 22寄存器地址所以0x0D、0x0E可以直接用。不要把MMD的寄存器地址塞到第三个参数里那样写出来的帧会发到错误的位置。4.3 用PHY ID验证函数可用性在用MMD之前先读0x02和0x03确认PHY ID这是最基本的自检u16 id1 XEmacPs_MdioRead(GemInstance, PhyAddr, 0x02); u16 id2 XEmacPs_MdioRead(GemInstance, PhyAddr, 0x03);KSZ9031RNX一般情况下会返回0x0022和0x1622不同批次可能有些微差异。如果这两个值读不对先不要碰MMD回去查PHY地址和MDC分频。如果ID正确再试着读一个已知非0的MMD寄存器做验证比如Device 7里的自动协商状态相关寄存器在千兆协商成功后状态位应该为1。能读到非0值说明整个三步序列已经打通后面就是按需填充具体寄存器号的问题了。4.4 接回LwIP时的注意事项把新的读取函数放进PhyLinkStatus或者PhyIsLinkUp所在的文件时有两点要特别注意。第一LwIP的初始化通常在主线程中执行阻塞时间不能太长。udelay可以用但不要在等待PHY时就地转圈超过几十毫秒否则可能影响整体启动节奏看起来就像“卡死在PHY初始化”。建议在系统启动阶段预留足够的PHY稳定时间而不是指望LwIP线程里反复重试。第二很多BSP的XEmacPs_MdioRead自带超时机制超时后会打印错误日志。这类日志要专门留意如果你的串口输出里频繁出现MDIO timeout相关提示且集中在PHY地址0x0D/0x0E的访问上多半是PHY地址配错或者MDC频率过高而不是MMD访问序列的问题。别一看到timeout就以为是函数写得不对。5. 如果还是读不到下一步这样查5.1 先做PHY回环确认数据通路如果你的MMD读取经过前面所有调整依然异常不要急着怀疑PHY挂掉了。先通过Clause 22的控制寄存器0x00配置PHY回环或者用ZYNQ GEM内部的digital loopback把MAC发送的数据直接环回接收端。只要回环模式下网络能通就说明PHY的数据通路和MAC接口都没问题问题百分之百出在管理接口这一侧。这一步能很有效地把问题切成两半数据面和管理面。数据面正常、管理面异常那要继续查的就是MDIO相关的东西而不是把板子拆下来换PHY。5.2 用逻辑分析仪抓MDIO波形MDIO就两根线MDC和MDIO。逻辑分析仪采样率50MHz以上就能看得清清楚楚。抓波形的时候重点看三个地方帧头是不是32个连续的1。操作码是01写还是10读。PHY有没有在turnaround时间把MDIO拉低也就是有没有应答。如果波形显示PHY根本没应答说明PHY压根没收到有效帧或者它认为这不是发给自己的如果波形完整且PHY有应答但读出来的数据和预期不符那就是寄存器地址或Device号错了。这个方法是终极手段能直接锁定是控制器一侧还是PHY一侧的问题。5.3 检查GEM0/GEM1的MDIO共享问题ZYNQ有两个GEM控制器有的板子会把两个GEM的MDIO并联在一起让一个控制器管理多个PHY有的板子则是各用各的MDIO。如果设备树或BSP里配置成了共享MDIO而两个控制器的MDC频率、PHY地址设置不一致就会出现“有时候能读有时候读不了”的诡异现象。更麻烦的是PHY地址冲突。如果板上两个PHY都设成0x7MDIO读回来的数据有可能来自随机一个PHYMMD读取自然是乱七八糟。这时候只能靠硬件改跳线或者改电阻把两个PHY分配到不同的地址。软件层面能做的只是把地址扫描逻辑写健壮别让一个地址的异常拖死整条初始化链路。6. 调这个组合这些年我保留的三个固定动作每次拿到一块新的ZYNQ KSZ9031板子我现在的固定动作都是一样的顺序基本不变先读PHY ID确认PHY地址再把MDC配到2MHz以下然后确保复位后延时150ms以上最后才上MMD访问。这个顺序不要乱一乱就容易把问题想复杂。另外建议在固件里加一个PHY寄存器诊断的串口命令可以随时读、写任意Clause 22寄存器和MMD寄存器。这个命令的代码量很小但对调试的帮助极大后期做产线测试或者现场故障定位时也能直接复用。我后来几乎所有板卡调试都靠这条命令快速确认PHY状态比反复烧版本、加打印效率高得多。
返回列表