MDIO 总线概述
做硬件调试这么多年,我上手一个新板子,头一件事就是拿示波器点 MDIO 和 MDC 这两根线。为啥?因为管理实体(PHY、交换机、光模块)出没出问题,全看它俩聊不聊得起来。MDIO 总线,说白了就是 CPU/MAC 跟物理层芯片之间的“管理频道”。
你想想看,数据通道跑得再快,你都不知道对端芯片温度、链路状态、协商结果,那跟开盲盒有什么区别。MDIO 就是帮你把这些信息读出来的工具。嗯,我们一步步来拆解。
一、MDIO 是什么?
MDIO(Management Data Input/Output)是一个同步串行总线,专门用于访问以太网 PHY(物理层芯片)的管理寄存器。一共就两根线:
- MDC(Management Data Clock)—— 时钟线,由主控(通常是 MAC)提供
- MDIO(Management Data Input/Output)—— 双向数据线
它遵循 IEEE 802.3 标准,最常用的是第 22 条款定义的模式,支持 32 个 PHY 地址,每个 PHY 有 32 个 16 位寄存器。后来 45 条款扩展到 32 个端口,每个端口最多 65536 个寄存器,主要用于 10GBASE-T 这类高性能 PHY。
我个人习惯:拿到一份新 PHY 的数据手册,先翻寄存器映射表,找到 0x00(BMCR,基本模式控制寄存器)和 0x01(BMSR,基本模式状态寄存器)。能正常读写这两个寄存器,整条 MDIO 链路大概率就通了。
二、为什么要用它?—— 实际场景回顾
MDIO 总线在我经手的项目里,几乎无处不在。我随便列几个典型场景:
| 应用场景 | 读取/控制的典型信息 |
|---|---|
| 自动协商结果确认 | 本地/对端能力、协商后的速度与双工 |
| 链路状态监控 | Link Up/Down、远端故障、极性反转 |
| MDI/MDIX 自动翻转状态 | 是否已翻转、启用/禁用 |
| PHY 温度/电压监测 | 通过扩展寄存器或 45 条款直接读 |
| EEE(节能以太网)配置 | 使能、唤醒时间参数 |
| 诊断/环回测试 | 设置环回模式、读取错误计数器 |
| 固件升级 | 某些 PHY 支持通过 MDIO 加载固件 |
举个例子。我有一次调试一块 8 口交换机板,PHY 的自动协商始终失败。按一般思路先查硬件焊接、再查差分走线,折腾半天没结果。后来用 MDIO 读 BMSR 寄存器,发现 Link 状态寄存器一直保持“0”。再检查 MDIO 读回来的原始数据,发现时钟周期对,但数据位一直保持高电平——原来是 MDIO 上拉电阻焊错了位置,导致主控和 PHY 的驱动冲突。这就是典型的“MDIO 两线正常但数据读不回”的问题。嗯,后面排查章节会细讲。
三、MDIO 总线的发展简史
MDIO 最早出现在 IEEE 802.3u(1995 年,快速以太网)中。当时 PHY 功能简单,只定义了一个 16 位数据宽度、32 个地址,足够用了。
到了千兆以太网(802.3ab/802.3z),PHY 的寄存器需求增加,但第 22 条款仍然够用。真正逼着标准升级的是万兆(10GBASE-T)以及更复杂的 PHY 芯片——寄存器数量超过 1024 不是稀奇事。于是 IEEE 802.3 在 2000 年之后加入了第 45 条款,定义了“端口地址+设备地址+寄存器地址”的三层结构,让你能访问最多 65536 个 16 位寄存器。很多新型 PHY 同时支持 Clause 22 和 Clause 45,靠 ST 码(起始码)区分。
这里有个坑:很多低成本的 MAC 控制器只实现了 Clause 22,接上 Clause 45-only 的 PHY 就会无法通信。我曾经在项目选型时没仔细看 MAC 的 MDIO 控制器文档,结果板子打回来死活读不到 PHY ID,最后发现是 MAC 不支持 Clause 45 帧格式。避坑指南:选 PHY 时一定要确认你的主控 MDIO 控制器支持哪种 Clause。
四、MDIO 在系统架构中的位置
为了让你看得更清楚,我画了一张 MDIO 总线在典型嵌入式系统里的位置图。理解这张图,后面分析故障时脑海里就有“信号流”了。
整个系统就是一个主控(MAC)通过 MDC 提供时钟,MDIO 线上串行读写多个 PHY 的寄存器。注意,MDIO 是半双工——同一时刻要么主控写,要么主控读,由帧格式控制。为什么只能半双工?因为数据线是双向的,而且 PHY 不能主动发起传输,所有访问都由 MAC 发起。这个特性决定了故障时一边发、一边收的时序分析逻辑。
五、寄存器模型速览
Clause 22 的基本寄存器模型,对于硬件调试来说,记住下面这三个就够了:
| 地址 | 寄存器名 | 关键位 |
|---|---|---|
| 0x00 | BMCR 基本模式控制 | Bit15 (复位)、Bit13 (速度选择)、Bit8 (双工)、Bit12 (自协商使能) |
| 0x01 | BMSR 基本模式状态 | Bit2 (Link状态)、Bit5 (自协商完成)、Bit0 (扩展寄存器存在) |
| 0x02 | PHY ID 高 16 位 | OUI 编号的高位 |
| 0x03 | PHY ID 低 16 位 | OUI 编号低位 + 型号/版本 |
为什么我这么强调 PHY ID?因为你通过 MDIO 读 0x02 和 0x03,如果返回的数值跟数据手册一致,就能证明物理层通信 100% 正常。反过来说,我最怕的一种坑是“MDIO 通讯正常但寄存器值全是 0xFFFF”或者“全是 0x0000”——那意味着 PHY 要么没上电,要么地址冲突,要么复位引脚一直拉低。这些在排查章节会一个一个拆。
六、帧格式——你知道怎么“看波形”吗?
Code 层面的细节,我们在后面的章节里讲。但这个地方先有必要提一句:MDIO 帧由 64 个时钟周期组成(Clause 22 写帧是 64 个周期,读帧是 64 个周期)。包含前导码、起始码、操作码、PHY 地址、寄存器地址、转向、数据。你拿示波器抓 MDIO 波形时,只要看到 32 个连续“1”的前导,心里就有底了——主控至少开始发帧了。
// Clause 22 读帧时序(简化)
// 前导码: 32个1
// 起始码: 01
// 操作码: 10 (读)
// PHY_ADDR: 5bit (例如 00001)
// REG_ADDR: 5bit (例如 00000)
// 转向: Z0 (主控释放总线, PHY驱动)
// 数据: 16bit (PHY回复)
嗯,要注意的是,很多 FPGA 实现的 MDIO 控制器不遵守 32 个 1 的前导要求,只发几个 1 就跳入起始码。虽然大多数 PHY 能容忍,但兼容性差。我曾经遇过一个老款 Broadcom PHY,它对前导长度很敏感,少于 32 个 1 就拒绝响应——当时查了三天才定位到是 FPGA 软核里写了个“while(count<10)”的 bug。从那以后,我写 MDIO 时序都严格按照标准来。
警告:MDIO 线上的上拉电阻值不要随便选。一般 1.5kΩ ~ 4.7kΩ,取决于供电电压(1.8V/2.5V/3.3V)和负载数量。阻值太大,上升沿变缓,高速模式(比如 25MHz MDC)下容易读错位;阻值太小,驱动电流太大可能损坏 PHY IO。我通常按 2.2kΩ 起步,测波形再微调。
七、MDIO 总线的局限性——提前给你打预防针
最后说两点 MDIO 天生的短板,你在实际排查中一定会遇到:
- 速度慢。 MDC 频率一般是 2.5MHz(标准)或 25MHz(高速),最快 30MHz 左右。读 100 个寄存器不过几毫秒,但对时延敏感的调试场景(比如监控链路闪烁)就不够用。有些 PHY 提供了中断输出引脚来弥补。
- 缺乏错误校验。 帧格式没有 CRC 或校验和。寄存器读回来对不对,全看你读的值合不合理。比如你读 BMSR 寄存器,Link 状态位要持续稳定,如果忽高忽低,可能是 MDIO 波形有毛刺,或者上拉不足。
好了,这一章我们把 MDIO 的来龙去脉、系统位置、关键寄存器、帧格式基本概念聊了一遍。后面我会带你一步一步深入:如何搭建调试环境、如何用示波器抓帧、如何分析常见的“读回来全是 FFFF”之类的顽症。