简介:这份PDF是Realtek RTL8201F、RTL8201FL与RTL8201FN系列单芯片/单端口10/100M以太网PHY收发器的官方数据手册(Rev.1.4),面向从事嵌入式网络硬件设计、驱动移植与底层调试的软硬件工程师。文档围绕自动MDI/MDIX、10/100Mbps速率自适应、RMII接口与寄存器配置展开,涵盖电气特性、引脚定义、功耗、复位时序等细节,可用于网口初始化、链路状态监测与故障排查。资源包共1个文件,为PDF格式,约1.06MB,按修订记录、功能框图、寄存器表与电气规格分章组织,便于按模块检索查阅,已有1175人学习下载。其中寄存器定义、掉电与PHY复位流程、EEE能力及扩频时钟等章节可直接支撑驱动代码编写与原理图核对;修订历史还标明VB模型、中断指示与绝对最大额定值等改动,便于确认设计所依据的版本。
1. 一块 PHY 的寄存器表为什么值得逐行读完:网线插上就通的那两百毫秒里发生了什么
把网线插进设备,链路灯常亮,系统打印 100Mbps 全双工。多数人把这段归功于"自适应"三个字,好像 PHY 只是把数字信号翻译成差分电平的被动器件。真实的画面完全不是这样:两颗 PHY 在双绞线上用一串快速链路脉冲互相试探,交换各自支持的速率与双工能力,比较优先级,再各自翻转内部收发通道的交叉开关,最后才把状态位翻上去。整个过程没有 CPU 参与,全靠 PHY 内部状态机和寄存器在跑。
这也解释了为什么"插上不通、换根线就通"不是玄学:Auto MDIX 的交叉检测、自协商的能力位、复位期间被引脚锁存的配置,任何一环被上拉电阻或走线带偏,链路就起不来。这份标注为开发伙伴限定版的数据手册把三十多个寄存器、三张私有页和两套寄存器访问机制全部摊开,读懂它等于拿到这颗小控制器的内部地图。
2. 自协商与 Auto MDIX:为什么换根交叉线就通了,为什么又突然掉到半双工
链路建立这一步,几乎所有的现场问题都出在同一处:人们把"能 ping 通"当成"配置对了"。实际上自协商和 Auto MDIX 是两个耦合在一起的状态机,它们跑完之后留下的证据全在寄存器里,读一遍就能分辨是"协商成功"还是"并行检测兜底"。
2.1 自协商协商的到底是什么:16 位链路码字与优先级表
自协商并不交换速率数字,它交换的是一个 16 位的链路码字(base page)。发送侧把码字的每一位编码进快速链路脉冲的时序里:一串 33 个位置的脉冲中,奇数位置固定作为时钟,偶数位置有没有脉冲代表这一位是 1 还是 0。接收侧把这串脉冲解出来,就得到了对方的能力集合。
码字里几个位置是必须记住的:bit15 是 Next Page 标志,bit13 是 Remote Fault,bit12 到 bit5 是技术能力字段,bit4 到 bit0 是选择子字段,取 0b00001 表示遵循 IEEE 802.3。能力字段里 bit8 表示 100BASE-TX 全双工、bit7 表示 100BASE-TX 半双工、bit6 表示 10BASE-T 全双工、bit5 表示 10BASE-T 半双工。
软件侧对应的就是四个标准寄存器:Reg 4 自协商广告寄存器由本地写入自己的能力,Reg 5 自协商对端能力寄存器是只读的、反映对端发来的码字,Reg 6 自协商扩展寄存器记录过程状态,其中 bit4 是并行检测故障、bit1 是收到对端新页、bit0 是对端具备自协商能力。真正决定链路形态的是两个能力集合求交之后按优先级扫描的结果。
优先级顺序是固定的:100BASE-TX 全双工高于 100BASE-TX 半双工,高于 10BASE-T 全双工,高于 10BASE-T 半双工。这套规则的意义在于保证两端独立计算得到同一个结果,不需要任何一方主导。下面这段代码就是把这件事显式算出来,而不是靠"读一下链路状态位就完事"。
# 依据 IEEE 802.3 Clause 28 的优先级顺序,从两个能力集合求交后取最高 PRIORITY = [ (8, "100BASE-TX Full-Duplex"), (7, "100BASE-TX Half-Duplex"), (6, "10BASE-T Full-Duplex"), (5, "10BASE-T Half-Duplex"), ] def resolve(anar: int, anlpar: int): """anar: 本地 Reg4;anlpar: 对端 Reg5。返回实际生效的链路形态""" common = anar & anlpar # 取交集,只有双方都支持的才可能生效 for bit, name in PRIORITY: # 从高优先级往下扫,第一个命中的即为结果 if common & (1 << bit): return name return None # 交集为空:只能退到并行检测 # 常见取值:只开 100M 全双工与自协商 anar = (1 << 8) | 0b00001 # bit8 能力位 + selector=0x01 anlpar = (1 << 8) | (1 << 7) | 0b00001 print(resolve(anar, anlpar)) # -> 100BASE-TX Full-Duplex这段逻辑的价值在于:当链路实际跑成 100M 半双工而预期是全双工时,你能立刻判断是本地没广告全双工能力,还是对端根本没发全双工位,而不是在一堆状态位里猜。参数上要注意anar必须把 selector 字段一起写进去,很多初始化代码只写能力位、把低 5 位留成 0,结果对端解析不出协议族,直接落到并行检测。
2.2 Auto MDIX 的检测机制:交叉开关什么时候翻转
10/100BASE-T 只用两对差分线,一对发、一对收。线序上有两种接法:MDI 是 1-2 发送、3-6 接收,MDIX 是把收发对调。直通线两端都是 MDI,交叉线一端 MDI 一端 MDIX。Auto MDIX 就是在芯片内部放一个交叉开关,让任意一种线缆都能配对成功。
检测过程和自协商共用同一套脉冲:芯片先在某个默认状态下发送链路脉冲,同时监听接收差分对上有没有能量进来。在一个几十到几百毫秒的窗口里如果什么都没收到,就翻转交叉开关再试一次。因此链路建立时间天然比固定接法长,链路断开重连时又要重跑一遍,指示灯会闪一下——这是正常现象,不是接触不良。
这里有个容易被忽略的耦合点:Auto MDIX 的探测脉冲本身就是自协商用的快速链路脉冲,两者是同一个状态机在推进。所以如果把自协商关掉(BMCR bit12 清零),Auto MDIX 的行为在某些实现里也会跟着变形,具体表现是交叉检测变慢或者干脆不切。手册里 Page 0 的 Reg 28 同时管光纤模式和回环,调试时务必确认回环位是关的,否则你会看到一条"自己能通、对端收不到"的假链路。
2.3 并行检测:老设备带来的半双工陷阱
对端如果不支持自协商——固定速率的老设备、某些直连的 MCU、部分工业交换机——它不会发快速链路脉冲。这时本地 PHY 退到并行检测:不听码字,只听物理层上有没有合法的链路活动,10BASE-T 看链路测试脉冲,100BASE-TX 看空闲码流。
问题就在这:并行检测能判断"对端在发什么速率",但判断不了双工。协议规定这种情况下一律按半双工处理。于是现场出现最经典的一类故障——一端自协商成 100M 全双工,另一端并行检测成 100M 半双工,链路是通的,ping 也通,但一跑吞吐就崩,FCS 错误和冲突计数飞涨,速度掉到几 Mbps。
正确的处理方式只有两条路。要么两端都开自协商;要么两端都强制,并且在强制的时候不要清掉自协商使能位,而是把广告寄存器改成只广告那一种能力,让对方在并行检测时也只能落到同一个结果上。只在一端强制、另一端自协商,是这段历史里最贵的一课。下面这段初始化代码体现的就是后一种写法。
/* 强制 100M 全双工的正确姿势:不清 AN enable,而是收窄广告集合 */ static void phy_force_100fd(int phy) { uint16_t bmcr = mdio_read(phy, 0x00); uint16_t anar = mdio_read(phy, 0x04); /* 只广告 100BASE-TX 全双工,其余能力位全部清掉 */ anar &= ~0x01E0; /* 清 bit8..bit5 技术能力字段 */ anar |= (1 << 8); /* 只置 100BASE-TX FD */ anar |= 0x0001; /* selector 必须保留 0b00001 */ mdio_write(phy, 0x04, anar); /* 重启自协商,让新广告集合生效;不要清 bit12 */ bmcr |= (1 << 9); mdio_write(phy, 0x00, bmcr); /* 随后轮询 BMSR bit5 直到置位,再读 BMSR bit2 确认链路 */ }0x01E0这个掩码覆盖 bit8 到 bit5,正好是四个能力位。清掉再单独置 bit8,效果是对端只会看到"100M 全双工"这一项,求交结果唯一。重启自协商写的是 BMCR bit9,写完硬件会自动清零,轮询时不要指望它一直是 1。
3. MII、RMII 与 SMI:MAC 侧到底怎么"看见"这颗 PHY
PHY 有两张脸。一张朝着网线,跑的是模拟前端和编解码;另一张朝着 MAC,跑的是并行数据总线和一根两线制的管理接口。后者才是软件工程师日常打交道的地方,也是"寄存器读不出来"这类问题的源头。
3.1 MII 与 RMII 的取舍:引脚数、时钟与那点可怜的时序余量
MII 是 4 位并行接口,发送侧有 TXD[3:0]、TX_EN、TX_CLK,接收侧有 RXD[3:0]、RX_DV、RX_ER、RX_CLK,再加 CRS、COL 两根状态线,连同管理接口差不多十八根。100M 时 TX_CLK 和 RX_CLK 是 25MHz,10M 时是 2.5MHz,时钟由 PHY 提供,MAC 只需要在自己的采样窗口内把数据取走。
RMII 把数据宽度砍到 2 位,时钟统一成一路 50MHz 参考时钟,引脚数几乎减半,代价是时钟精度和走线匹配的要求上去了。50MHz 参考时钟通常要求 ±50ppm,而且它同时作为发送和接收的采样基准,走线延迟直接吃掉 setup/hold 余量。
| 维度 | MII | RMII |
|---|---|---|
| 数据宽度 | 4 位 | 2 位 |
| 时钟 | TX_CLK / RX_CLK 各一路,由 PHY 输出 | 单路 50MHz REF_CLK |
| 典型引脚数 | 约 18 根 | 约 9 根 |
| 时序约束 | 采样窗口相对宽松 | 依赖 REF_CLK 走线延迟匹配 |
| 10M 模式 | 时钟降到 2.5MHz | 50MHz 上做多次重复采样 |
手册里分别给出了 MII、RMII、MDC/MDIO 三组时序定义图,包括建立时间、保持时间和从时钟上升沿起的输出有效时间。这些数字不是给硬件工程师看的装饰,它们就是布线长度匹配的约束来源。RMII 模式下还有一件事必须做:接口模式不是自动识别的,要通过 Page 7 的 Reg 16 显式配置,默认值并不是 RMII。很多板子第一次上电跑不通,查到最后就是漏了这一次写。
3.2 SMI 的帧结构:MDC/MDIO 上那 32 个比特
管理接口只有两根线,一根时钟 MDC,一根双向数据 MDIO。帧结构是固定的:可选的前导码 32 个 1,然后是起始符 01,操作码读为 10、写为 01,5 位 PHY 地址,5 位寄存器地址,2 位翻转周期,最后 16 位数据。写操作翻转周期是 10;读操作的翻转周期是 Z0,也就是第一个比特由 PHY 释放总线,第二个比特由 PHY 驱动为 0。
MDIO 是双向开漏结构,必须加上拉电阻,典型取值在 1.5kΩ 到 10kΩ 之间,阻值选大了上升沿会变慢,在高速 MDC 下直接读错位。采样沿也要记牢:数据在 MDC 上升沿被采样,读回的数据在上升沿之后才有效,所以主机应当在下降沿切换输出、在上升沿读取输入。
/* Clause 22 读寄存器:bit-bang 版本,MDIO 为双向开漏 */ static int mdio_read(uint8_t phy, uint8_t reg) { uint16_t val = 0; int i; mdio_dir_out(); for (i = 0; i < 32; i++) mdio_write_bit(1); /* 前导码,部分 MAC 可省 */ mdio_write_bit(0); mdio_write_bit(1); /* ST = 01 */ mdio_write_bit(1); mdio_write_bit(0); /* OP = 10 读 */ for (i = 4; i >= 0; i--) mdio_write_bit((phy >> i) & 1); for (i = 4; i >= 0; i--) mdio_write_bit((reg >> i) & 1); mdio_dir_in(); /* 翻转周期:释放总线 */ mdio_read_bit(); /* 第一个比特 Z,丢弃 */ mdio_read_bit(); /* 第二个比特应为 0 */ for (i = 0; i < 16; i++) { /* 上升沿后采样 */ val = (val << 1) | mdio_read_bit(); } return val; }每个mdio_write_bit内部都是"下降沿放数据、上升沿让从机采样"的节奏。mdio_dir_in()这一步不能省,忘记切换方向会导致总线上出现两个驱动源,读回的值恒为全 1。MDC 频率手册给了时序参数,实际工程里保守取 1MHz 到 2.5MHz,先跑通再提速,比一上来就冲高频然后怀疑芯片坏了要省时间。
3.3 分页与 MMD:32 个地址不够用之后的两套补丁
Clause 22 的寄存器地址只有 5 位,总共 32 个位置。前 8 个被标准占满,剩下的要放省电、中断、LED、扩频时钟、EEE 等一大堆私有功能,显然不够。芯片厂商用了两套办法来扩。
第一套是分页。Reg 31 是页选择寄存器,往里面写一个页号,接下来 0 到 31 号寄存器的含义就整体换成另一张表。手册里出现了 Page 0、Page 4、Page 7 三张页,Page 0 是标准寄存器加一部分私有寄存器,Page 4 放 EEE 能力,Page 7 放 RMII 模式、自定义 LED、中断与 WOL、MII 发送隔离、扩频时钟。
第二套是 MMD 间接访问。Page 0 的 Reg 13 是访问控制寄存器,写入设备地址和功能号;Reg 14 是地址数据寄存器,读写它等于读写目标 MMD 寄存器的内容。这套机制绕开了 5 位地址的限制,代价是每次访问要写两次、读一次,还得保证中间不被打断。
手册里 Device 3 是 PCS 层,0x00 是 PCS 控制、0x01 是 PCS 状态、0x14 是 EEE 能力、0x16 是 EEE 唤醒错误计数;Device 7 是自协商层,0x3c 是 EEE 广告、0x3d 是 EEE 对端能力。两套机制并存是历史包袱,也是翻车高发区。
def mmd_read(phy, dev, addr): """通过 Page0 Reg13/Reg14 间接读 MMD 寄存器""" mdio_write(phy, 0x0D, (dev << 8) | addr) # Reg13:设备地址 + 目标地址 mdio_write(phy, 0x0D, mdio_read(phy, 0x0D) | 0x4000) # 置功能位为读 return mdio_read(phy, 0x0E) # Reg14:数据在此这三个参数里dev是 MMD 设备号,addr是设备内的寄存器偏移,phy是 5 位 PHY 地址。功能位的具体位置各厂商略有差异,写之前一定对着手册的位定义核对,凭经验套用别家的宏是最常见的错误来源。
4. 寄存器地图:从 BMCR 到 Page 7,一颗 PHY 的控制面是怎么摊开的
把寄存器按"谁在什么时候会写它"分类,比按地址顺序背要有效得多。驱动初始化阶段写的是标准寄存器,功能定制阶段写的是私有页,故障排查阶段读的是状态和计数。
4.1 前八个标准寄存器:初始化顺序比寄存器本身更重要
Reg 0 基本模式控制寄存器是唯一一个能改变芯片根本行为的寄存器。bit15 软复位,写 1 后硬件自清;bit14 回环;bit13 与 bit6 组合选择速率;bit12 自协商使能;bit11 掉电;bit10 隔离;bit9 重启自协商;bit8 双工选择。
| 位 | 名称 | 作用 | 常见误用 |
|---|---|---|---|
| 15 | Reset | 软复位,自清 | 写完立刻读,读到 1 就以为没复位 |
| 14 | Loopback | 内部回环 | 调试后忘记关,链路假通 |
| 13/6 | Speed | 速率选择 | 强制模式下与 bit8 不同步 |
| 12 | AN Enable | 自协商总开关 | 强制速率时误清,触发并行检测 |
| 11 | Power Down | 掉电 | 忘记清,整颗芯片不工作 |
| 9 | Restart AN | 重启协商 | 轮询时不理解它会自清 |
| 8 | Duplex | 双工选择 | 与对端不一致导致半双工塌陷 |
Reg 1 基本模式状态寄存器是只读的,bit5 表示自协商完成,bit2 表示链路状态,这一位是锁存低电平的,读一次会清零当前的低电平状态,连续读两次才拿到稳定值。bit4 是远端故障,bit1 是冲突检测。Reg 2 和 Reg 3 组成 32 位 PHY 标识,用来在总线上枚举设备、确认地址没配错。
初始化顺序上,稳妥的做法是:写 BMCR bit15 软复位,轮询直到该位自清;写 ANAR 配置广告能力;如果需要重启协商就写 bit9;轮询 BMSR bit5 等协商完成;再轮询 BMSR bit2 等链路建立;最后读回协商结果做一致性校验。中间任何一步用固定延时替代轮询,都会在慢速对端上翻车。
4.2 Page 0 的私有寄存器:省电、回环、中断与信噪比
Page 0 除了标准寄存器,还塞了几个私有功能。Reg 24 是省电模式寄存器,管几种低功耗状态的进入与退出;Reg 28 同时管光纤模式和回环,注意这两个功能挤在一个寄存器里,改动其中一半很容易误伤另一半;Reg 30 是中断指示与信噪比显示,链路质量差的时候这个寄存器里的信噪比数值比任何猜测都直观;Reg 31 是页选择。
这些私有寄存器的共同点是:它们不在任何 IEEE 标准里,换一颗 PHY 就全部失效。工程上值得把它们全部收进一个适配层,用phy_ops之类的结构体隔离,别让业务代码里散落着mdio_write(phy, 0x1F, 7)这种魔数。血泪经验是:一开始图快直接写在初始化函数里,后来换料或者做双供应商,改起来就是全文件搜索替换加逐行验证。
4.3 Page 4 与 Page 7:EEE、RMII 模式、LED 与扩频时钟
Page 4 只干一件事:EEE 能力的管理。Reg 16 是能力使能,Reg 21 是能力状态。EEE 也就是 802.3az 的低功耗空闲,链路空闲时收发双方约定进入静默,有数据再唤醒,唤醒延迟是它的代价。
Page 7 就杂得多。Reg 16 是 RMII 模式设定,用 RMII 接口就必须配;Reg 17 是自定义 LED 设置,决定每个 LED 引脚显示什么事件;Reg 18 是 EEE 指示灯使能;Reg 19 把中断使能、网络唤醒使能和 LED 功能挤在一起;Reg 20 是 MII 发送隔离;Reg 24 是扩频时钟寄存器。
| 页 | 寄存器 | 用途 | 触发时机 |
|---|---|---|---|
| 0 | Reg 13 / 14 | MMD 访问控制与数据 | 读写 Clause 45 寄存器 |
| 0 | Reg 24 | 省电模式 | 低功耗策略 |
| 0 | Reg 28 | 光纤模式与回环 | 介质切换、自测 |
| 0 | Reg 30 | 中断指示与信噪比 | 链路质量诊断 |
| 4 | Reg 16 / 21 | EEE 能力使能与状态 | 开启低功耗空闲 |
| 7 | Reg 16 | RMII 模式设定 | 板级接口选择 |
| 7 | Reg 17 | 自定义 LED | 面板指示定制 |
| 7 | Reg 19 | 中断、WOL、LED 功能 | 唤醒与指示配置 |
| 7 | Reg 24 | 扩频时钟 | EMC 整改 |
这张表本身就是一份调试路线图:链路类问题看 Page 0,接口类问题看 Page 7 Reg 16,功耗类问题看 Page 4 和 Page 0 Reg 24,面板指示灯不对就看 Page 7 Reg 17 和 Reg 18。
4.4 LED 引脚的两副面孔:复位期间是 PHY 地址,复位之后是灯
引脚紧张的时候,厂商会把配置信息在复位期间锁存到复用引脚上。这颗 PHY 就是这么做的:复位期间,几个 LED 引脚的电平被采样并锁存,作为 PHY 地址、接口模式等初始配置;复位释放之后,这些引脚变回 LED 驱动输出。
省了引脚,代价是上电瞬间的电平不由你说了算,而由 LED 的串联电阻和正向压降说了算。常见故障是这样:设计时按典型亮度选了 1kΩ 限流电阻,结果该引脚在复位期间被读成低电平,PHY 地址从预期的 1 变成 0,驱动按地址 1 去访问,读回来的全是 0xFFFF。
处理方式不复杂但必须做:先确认复位期间各复用引脚的目标电平,再反算 LED 支路的等效阻抗,必要时并联或串联电阻把电平拉到安全区。手册里 LED 与 PHY 地址那一节专门讲了这层关系,值得在画板子之前读一遍,而不是在打样回来之后。
4.5 省电、EEE 与扩频时钟:EMC 测试间里的取舍
省电模式、EEE、扩频时钟这三个功能的共同点是:单看指标都很美,叠加在一起就容易互相打架。
省电模式通过 Page 0 Reg 24 控制,进入后部分模拟电路关断,唤醒需要时间。EEE 通过 Page 4 和 MMD 里的几个寄存器协商,链路空闲时进入低功耗静默。扩频时钟通过 Page 7 Reg 24 把内部时钟频率做小幅周期性调制,把窄带的时钟能量摊开到一段频带上,降低辐射峰值。
扩频时钟的代价是时序抖动变大。在时序余量本来就紧张的 RMII 接口上开扩频,可能直接表现为偶发丢包;而对端如果不接受这种频率漂移,链路会周期性重协商。手册的修订记录里专门为这个寄存器加了一节说明,说明它不是默认开启的功能,属于"需要时再开、开完必须复测"的类型。
EEE 的排查路径更绕一些:先看 Page 4 Reg 16 和 Reg 21 确认本地能力打开,再通过 MMD Device 7 的 0x3c 和 0x3d 确认双方广告与能力匹配,然后看 Device 3 的 0x14 能力寄存器和 0x16 唤醒错误计数。唤醒错误计数持续增长,基本可以判定是链路伙伴的唤醒时序不满足,而不是本地配置写错了。
5. 常见问题排查:PHY 不链、链路闪烁与寄存器写不进去
这一节记录的都是"现象看着像硬件坏了,实际是软件或板级配置"的几类。每一条都按现象、原因、处理三步走,方便对着现场现象直接定位。
5.1 链路灯不亮,但自协商状态位显示已完成
现象:读取 BMSR,bit5 自协商完成已经置位,但 bit2 链路状态始终为 0,物理链路上没有任何活动。
原因通常有三类。一是 ANAR 里广告的能力集合为空或者 selector 字段写成了 0,对端解析不出协议族,协商虽然"走完流程"但没有任何共同能力,于是不建立链路。二是发送偏置回路上出了问题,变压器中心抽头的偏置电阻虚焊或者阻值错误,芯片发不出合法电平,链路伙伴根本检测不到能量。三是 Reg 28 里的回环位被误置,芯片把发送的数据绕回自己的接收端,自己觉得有活动,对端什么都收不到。
处理顺序建议从软件到硬件:先把 ANAR 读回来打印,确认低 5 位是 0b00001 且至少有一个能力位;再读 Reg 28 确认回环关闭;最后量变压器中心抽头的直流偏置电压和差分幅度。
5.2 寄存器写进去读回来不一致
现象:调用写函数之后立刻回读,得到的值和写入值不同,有时候读回 0x0000,有时候读回 0xFFFF。
原因分三层。最外层是 MDC 频率过高,MDIO 上拉电阻偏大导致上升沿不够陡,采样到错误的位,读回的值看起来像随机数。中间层是访问了只读寄存器,比如往 BMSR 或 ANLPAR 写数据,硬件直接丢弃,回读自然是旧值。最内层是页没切对——上一次操作切到了 Page 7,之后所有按标准地址读写的代码全都在读 Page 7 的内容。
处理办法是给寄存器访问加一层薄封装:每次访问私有页之前显式切页,访问完成后显式切回 Page 0,并且把页号作为参数传进读写函数而不是依赖全局状态。这个习惯能把这类问题从"偶发诡异"降级成"根本不会发生"。
5.3 LED 行为与手册描述完全不符
现象:配置了链路指示灯常亮、活动指示灯闪烁,实际观察到的是两颗灯同时闪,或者干脆全灭。
原因基本都在复位期间的引脚锁存上。LED 引脚在复位期间承担配置采样,如果外部电路让某个引脚电平偏离预期,锁存进去的配置就变了,复位释放后 LED 的功能映射也跟着变。另一种情况是自定义 LED 功能没有配置,芯片用的是默认映射表,跟你以为的映射不一样。
处理方式是先把 Page 7 Reg 17 读回来,逐位对照实际观察到的行为,确认映射是不是你写的那一份;再在复位期间用示波器抓各复用引脚的电平,看是否与设计预期一致。别在软件里给 LED 打补丁绕过去,那只是把配置错误藏起来。
5.4 RMII 模式下偶发丢包与 CRC 错误
现象:链路稳定,ping 大包时丢包率在千分之几,抓包能看到 FCS 错误,速率越低越不明显。
原因集中在时钟与接口配置。RMII 需要单路 50MHz 参考时钟,精度通常要求 ±50ppm,用普通晶振分频出来的时钟如果偏差过大,长时间运行会积累采样偏移。其次是 REF_CLK 到 MAC 和到 PHY 的走线长度不匹配,接收方向的有效窗口被压缩。第三是 Page 7 Reg 16 的 RMII 模式没配,芯片还在按 MII 时序工作,短包勉强能过、长包必错。
排查顺序:先确认 Reg 16 已正确配置,再用频率计核对 50MHz 的实际偏差,最后看时序余量。手册里 RMII 的建立时间、保持时间和输出延迟三张图就是用来算这个余量的,别跳过。
5.5 复位之后 PHY 地址变了
现象:同一块板子,冷启动时能正常通信,热复位之后寄存器全部读不到。
原因是复位类型不同,引脚锁存的时机也不同。冷启动时电源建立过程较慢,复用引脚上的电平有足够时间稳定到设计值;热复位时复位信号很窄,采样窗口落在电容还没放电完的时刻,锁存进去的地址就变了。手册里上电与复位时序那一节给了复位脉宽和稳定时间的要求,很多设计把这个时间压得太短。
处理方式是把复位脉宽按手册上限留足余量,并在复位释放之后加一段固定延时再发起第一次 MDIO 访问。另一个实用的做法是在驱动里做地址扫描:依次用 0 到 31 的地址读 Reg 2 和 Reg 3,能读出合法标识的那个就是当前地址,这比假设地址固定要稳得多。
6. 设计取舍:这颗 PHY 把复杂度放在了哪一侧
把寄存器地图读完,会发现一个有意思的分布:所有"跑起来就自动完成"的功能,复杂度都被推到了硬件状态机里;所有"需要人做决定"的功能,复杂度都被推到了软件侧的寄存器配置上。Auto MDIX 是前者的典型,你什么都不用做它就能识别线序;LED 映射、RMII 模式、EEE 协商、扩频时钟是后者的典型,不配就不工作,配错了现象还很隐蔽。
这个划分直接决定了两类工作的成本。硬件侧省下来的引脚,用复位期间的锁存换来,代价是上电瞬间的电平不再完全可控;软件侧省下来的寄存器地址,用分页和 MMD 间接访问换来,代价是访问顺序成了隐式依赖。两套扩展机制并存这件事本身就是历史包袱的化石:分页是早期在 32 个地址里挤空间的权宜之计,MMD 是后来标准给出的正解,但为了兼容已经出货的驱动,两套都得留着。
更值得琢磨的是功能取舍的边界。扩频时钟能过 EMC,但要牺牲时序余量;EEE 能省电,但要牺牲唤醒延迟和互操作性;省电模式能降功耗,但唤醒时间进了毫秒级。手册把这些开关全部交给软件,等于把"什么时候值得"这个判断也交给了软件。这也是为什么同一颗 PHY 在不同产品上表现差异巨大——不是芯片不同,是那几个私有寄存器的值不同。
验证上,我的习惯是把这套东西做成一个可复现的检查流程:上电后先扫 PHY 地址、读标识确认器件在位;再读 ANAR 和 ANLPAR 算出预期链路形态,与 BMSR 的实际状态比对;然后逐页读回所有被写过的私有寄存器,确认没有意外的页残留;最后跑一轮长包压力测试,看 FCS 错误计数是否稳定。这套流程不长,但能把九成以上的"玄学不链"挡在出货之前。
从那以后我每次碰新的 PHY,哪怕手册只有薄薄几十页,也强制先把寄存器页表抄一遍再写第一行驱动代码——因为翻车从来不是因为不会写 MDIO,而是因为不知道某个引脚在复位的那几微秒里已经被别人安排好了。希望帮到你。
本文还有配套的精品资源,点击获取