☰
千兆PHY链路排查:寄存器、自协商与RGMII时序
2026/10/29 21:21:21 网站建设 项目流程

简介:这份资源是 Realtek RTL8211EG-VB-CG 以太网收发器 PHY 芯片数据手册的中文翻译版 PDF,面向从事嵌入式网络硬件与驱动开发的工程师、以太网交换、路由或网卡类产品的设计人员,以及需要查阅寄存器细节的高校师生;原厂英文手册篇幅长、术语密集,中文版可降低阅读门槛,便于快速定位芯片功能与配置要点。压缩包仅含 1 个 PDF 文件,大小约 2.19MB,单文件即完整承载整本手册,无需解压检索多个文档即可通读。手册内容覆盖芯片架构模块划分、寄存器映射与定义(如 BMCR、GBCR、INSR、PHYCR 等)、电气特性与直流参数、应用电路与典型系统方案,并附修订历史与订购信息,可支撑原理图设计、寄存器初始化配置、MDC/MDIO 时序核对与故障排查等实际工作。目前该资源已有 2372 人学习下载,适合作为案头速查资料常备。

1. 千兆 PHY 的手册为什么值得逐页啃:链路起不来的三个瞬间都藏在寄存器里

把一份 PHY 芯片手册翻译成中文,最容易被翻烂的是引脚定义表,最容易被跳过的是寄存器位域那几十页。可真正决定一块板子能不能通网的,恰恰是后者。反直觉的地方在于:千兆链路起不来,多数时候不是焊错了、也不是变压器选错了,而是自协商没走完、RGMII 的采样点偏了、或者复位时序差了几毫秒——这三个瞬间全都不在示波器上显示,只在寄存器里留下痕迹。搞懂这颗收发器怎么把 125MHz 的并行数据变成四对双绞线上的模拟电平,再反过来把对端的电平恢复成时钟和数据,你才有资格在"链路 up 了但不通"这种玄学现场里,靠读几个地址就锁定根因。下面按"寄存器窗口 → 自协商 → 时序 → 排查"的顺序,把这套机制拆开。

2. 寄存器窗口:32 个 Clause 22 地址怎么装下三套协议栈

翻到寄存器描述那一章,第一眼看到的是一张 32 行的表,地址 0x00 到 0x1F,很多工程师扫一眼就下了结论:标准寄存器,内核驱动里都有现成的。这个判断只对了一半。这颗芯片实际上叠了三套寄存器视图在同一个 5 位地址空间上:IEEE 802.3 强制规定的 Clause 22 标准区、Clause 45 风格的 MMD 设备空间、以及厂商自定义的 ExtPage 页。地址线只有 5 根,所以后两套必须靠"间接寻址"挤进来,而这个"挤"的过程,就是绝大多数驱动翻车的地方。

理解了这一层,手册里那些看起来冗余的表格就有了逻辑:它们不是并列的三组寄存器,而是三种访问路径,共用同一对 MDC/MDIO 引脚。你在读某个地址之前,必须先清楚自己此刻站在哪一层,否则读回来的值完全可能是另一个空间的残影。

2.1 0x00~0x0F 是 IEEE 强制划分的公共区域

这一段地址是硬约束,任何一家 PHY 都不能改语义。BMCR(0x00)负责复位、自协商使能、重启协商、掉电、隔离、环回和速率双工选择;BMSR(0x01)回报能力与当前状态;PHYID1/PHYID2(0x02/0x03)是身份;0x04~0x08 是自协商的五件套;GBCR(0x09)和 GBSR(0x0A)专管 1000Base-T。

这里有几个设计痕迹值得单独说。BMCR 的速率编码分散在两个不相邻的位:bit13 是低位、bit6 是高位,两位组合成 00=10M、01=100M、10=1000M。这种"拆开摆"的布局是 1995 年从 10M/100M 扩展到千兆时留下的历史包袱,当年为了不打乱已发布的位定义,只能往空位上塞。所以你在写强制速率代码时,千万别只改一个位——只清 bit13 不清 bit6,芯片会理解成"保留编码",行为未定义,这种 bug 在现场表现为"偶尔能通、偶尔不通",极为折磨。

BMSR 的 bit2(Link Status)是 latch-low 语义:链路断过哪怕一瞬间,这一位就会被锁存为 0,直到你读一次才释放。手册对这一点的描述只有一行小字,但驱动里如果拿单次读的结果当"当前链路状态"用,就会看到链路在疯狂抖动——其实链路好得很,是你自己把历史故障读成了实时状态。正确做法是连读两次,第二次才是当前值。

GBCR(0x09)是千兆专属的控制面:bit9 广告 1000Base-T 全双工、bit8 广告半双工,另外还有一组位用来在关闭自协商时手动指定主/从。这一组位是第一层容易被忽略的关键点:BMCR 只能把速率"强制"成 1000M,但千兆必须解决主从问题,而主从在自协商关闭时是无法自动解析的——你强制了千兆却没指定主从,两端就可能都认为自己是 master,链路直接废掉。手册里 GBCR 那张表不长,但每一个位都对应一种现场故障。

2.2 ExtPage:把 5 位地址扩成"页号 + 偏移"

标准区只有 32 个地址,装不下 LED 行为配置、自动交叉控制、RGMII 时序微调这些厂商自定义功能。这颗芯片的做法是:在 0x1E 放一个 EPAGSR(扩展页选择寄存器),先往里写页号,之后对这 32 个地址的读写就全部落到那一页里。手册里能看到的页有 0x2c(页内有 LACR 0x1a 管 LED 动作、LCR 0x1c 管 LED 组合)和 0x2d(页内有 ACCR 0x18 管自动交叉)。

这个机制的问题在于它是有状态的。页号是一个全局开关,翻过去之后不翻回来,后面所有对 0x00~0x1F 的访问都会落在错误的页上。如果驱动里有一个线程在初始化 LED,中断处理里另一个路径在读 BMSR,两者共享同一条 MDIO 总线却没有互斥,就会出现"读 BMSR 读到了 LED 寄存器"的诡异现象——而且只在特定时序下复现,属于典型的血泪经验型 bug。

常见的实现思路是:把"切页 + 操作 + 切回页 0"封装成一个原子操作,外面套总线锁;任何直接裸写 EPAGSR 的代码都视为违规。这条纪律比寄存器本身重要得多。

/* 扩展页访问必须成对,且全程持锁 */ static DEFINE_MUTEX(mdio_bus_lock); int extpage_write(struct phy_dev *p, uint8_t page, uint8_t reg, uint16_t val) { int ret; mutex_lock(&mdio_bus_lock); /* 1. 先切到目标页,0x1E 是页选择寄存器 */ ret = p->write(p, p->addr, 0x1E, page); if (ret) goto out; /* 2. 此刻的 reg 偏移才落在目标页里 */ ret = p->write(p, p->addr, reg, val); if (ret) goto out; out: /* 3. 无论成败都要切回页 0,否则后续标准寄存器全读错 */ if (page != 0) p->write(p, p->addr, 0x1E, 0); mutex_unlock(&mdio_bus_lock); return ret; }

参数说明:page是页号,reg是页内偏移(仍然是 0x00~0x1F 的 5 位地址),val是 16 位数据。注意goto out的分支里依然要执行切页归零,这就是为什么不能简单 return——页号泄漏比写失败更危险,它会让后面所有看似无关的读操作全部失真。

2.3 MMD 间接寻址:一次读要拆成三笔 MDIO 事务

第三套视图是 MMD(MDIO 可管理设备)。IEEE 把 PHY 内部功能切成若干"设备":PMD/PMD 相关、PCS、PHY 相关、以及厂商自定义设备号。Clause 45 本来给每个 MMD 分配了独立的地址空间,但这颗芯片只引出 Clause 22 的 5 位地址,于是用 0x0D(MACR)和 0x0E(MAADR)做中转:MACR 承载"功能码 + 设备号",MAADR 承载"寄存器地址"或"数据"。

读一个 MMD 寄存器的完整时序是三步:写 MACR 选设备 → 写 MAADR 送寄存器地址 → 再写 MACR 置读功能码 → 最后从 MAADR 取数据。四笔 MDIO 事务,每一笔都是一次完整的 32 位帧(前导码 + 起始 + 操作码 + PHY 地址 + 寄存器地址 + 转向 + 16 位数据)。

#define MII_MACR 0x0D #define MII_MAADR 0x0E /* MACR 高位是功能码:0=送地址 1=写数据 2=读并自增 3=读不自增 */ #define MMD_FUNC_ADDR 0u #define MMD_FUNC_WRITE 1u #define MMD_FUNC_READ 3u uint16_t mmd_read(struct phy_dev *p, uint8_t devad, uint16_t addr) { uint16_t val; mutex_lock(&mdio_bus_lock); p->write(p, p->addr, MII_MACR, (MMD_FUNC_ADDR << 14) | (devad & 0x1F)); p->write(p, p->addr, MII_MAADR, addr); p->write(p, p->addr, MII_MACR, (MMD_FUNC_READ << 14) | (devad & 0x1F)); val = p->read(p, p->addr, MII_MAADR); mutex_unlock(&mdio_bus_lock); return val; }

为什么功能码用 3 而不是 2?2 表示"读后地址自增",适合连续读一整段寄存器,效率高。但只要有任何一条路径在中间插进来一次读,自增指针就被打乱,下一次读到的位置就跑偏了。在中断和轮询并存的驱动里,这种偏移极难复现。宁可多花一笔事务,也别用自增——这是我在踩过两次之后固定下来的习惯。

还有一个隐形成本:每次 MDIO 事务在 2.5MHz 的 MDC 下大约 20 微秒,四笔就是 80 微秒。如果上电初始化里有一百次 MMD 读,光寄存器访问就吃掉 8 毫秒。手册 10.6.1 节给了 MDC/MDIO 的建立保持时间约束,实际能跑多快取决于你板子上 MDIO 的上拉阻值和走线电容——上拉用 10k 还是 1.5k,直接决定了你能不能用满手册给的上限频率。

3. 自协商:为什么插上网线要等几百毫秒才通

把网线插上去,交换机端口灯要闪几下才常亮,中间那几百毫秒到底在干什么?很多人的理解是"在测链路质量",其实完全不是——自协商期间双方根本没有建立有效链路,它们是在用一组定义好的脉冲序列互相"报菜名"。搞懂这套编码,你就能解释为什么有些板子插上去秒通,有些要等三秒,有些干脆协商到一半卡死。

3.1 快速链路脉冲与 16 位基页:能力是怎么编码的

自协商用的是 FLP(Fast Link Pulse)突发:一串 33 个位置的脉冲,其中奇数位置是时钟脉冲(固定发),偶数位置是数据脉冲(发不发代表 0 或 1),一次突发正好携带 16 位。这 16 位就是"基页",格式由 Clause 28 规定:选择子字段标明这是以太网,技术能力字段用一串比特列出自己支持的组合——10M 半双工、10M 全双工、100M 半双工、100M 全双工、100Base-T4、100Base-T2,再加上暂停(PAUSE)和非对称暂停的意愿。

这里有一个关键的设计约束:基页只有 16 位,装不下 1000Base-T。所以千兆能力必须走"下一页"机制,用额外的突发送一段扩展数据,里面携带 1000Base-T 全/半双工能力、本端是否具备主从能力、以及本端倾向做 master 还是 slave。也就是说,一次完整的千兆协商至少要交换两页内容,任意一页丢失或校验失败,结果就退化——这就是"千兆怎么都上不去、只能跑百兆"最常见的物理层原因之一。

理解这一点后,很多现场现象就顺了:链路在 100M 稳定、换千兆就反复重协商,八成不是芯片坏了,而是四对双绞线里有一对的回波损耗不达标,导致下一页那段突发在传输中出错。线序错、变压器某一对中心抽头虚焊、RJ45 某一对针脚氧化,都会表现成这个症状。

3.2 主从解析:1000Base-T 必须选出一个"主"

100Base-TX 只用两对线,一对发一对收,方向是固定的。1000Base-T 用四对线,每一对同时双向传输,靠回声消除把对方发来的信号从自己的发射信号里减掉。要这么做,两端的采样时钟必须严格同步到同一个频率源,于是必须选出一个 master(用本地时钟)和一个 slave(从对端信号里恢复时钟)。

协商时双方各自声明"我能当 master"和"我想当 master",然后按一套确定的规则解析:一方想做 master、另一方无所谓,就直接定;双方都想当 master,就进入冲突解析,用各自的种子数比较,输的一方转成 slave。这套规则在标准里写得很清楚,落到寄存器上就是 GBSR 里那个"主从配置故障"位和"本端解析结果"位。

主从冲突本身不致命,协议会自愈。真正致命的是你手动强制了千兆却没配主从——前面说过,BMCR 强制速率时自协商是关的,主从解析也就无从谈起,两端都默认自己是 master,链路直接起不来,而链路状态位可能还是 up 的(物理上有信号),只是数据全错。这种"看着通、实际不通"的现场,最能体现读寄存器比看灯的价值。

3.3 从 ANAR/ANLPAR/GBCR 读出协商结果的实操

排查自协商问题,需要按固定顺序读四个地方:ANAR(0x04)看本端广告了什么、ANLPAR(0x05)看对端广告了什么、GBCR(0x09)看千兆能力有没有被广告出去、GBSR(0x0A)看主从解析最终落在哪边。ANLPAR 里的值是链路伙伴真实发过来的,跟本端配置无关,所以它能直接证明"对方到底支不支持千兆"。

/* 触发一次自协商,并等到链路真正可用 */ int phy_bringup_link(struct phy_dev *p, int timeout_ms) { uint16_t bmsr; int waited = 0; /* bit9=重启协商,bit12=使能协商;两个位一起写才是完整的一次触发 */ p->write(p, p->addr, 0x00, BMCR_ANENABLE | BMCR_ANRESTART); while (waited < timeout_ms) { bmsr = p->read(p, p->addr, 0x01); /* bit5 = 自协商完成。注意它和 bit2 链路状态是两件事 */ if (bmsr & BMSR_ANEGCOMPLETE) { /* bit2 是 latch-low,连读两次,第二次才是当前值 */ (void)p->read(p, p->addr, 0x01); bmsr = p->read(p, p->addr, 0x01); if (bmsr & BMSR_LINKSTATUS) { /* 到这里链路才真的可用:读对端能力确认协商结果 */ uint16_t lpar = p->read(p, p->addr, 0x05); uint16_t gbsr = p->read(p, p->addr, 0x0A); phy_log_result(lpar, gbsr); return 0; } } mdelay(10); waited += 10; } return -ETIMEDOUT; }

这段里有两个容易写错的点。其一是BMSR_ANEGCOMPLETE和BMSR_LINKSTATUS必须分开判断:协商完成只代表双方把能力交换完了,链路状态才代表模拟前端真的建立起来了,两者之间可能隔着几十毫秒。其二是超时值——千兆协商加上链路训练,正常在 300~600 毫秒之间收敛,给 3 秒是保守值;如果每次都跑满 3 秒才通,那不是"慢",是下一页交换在反复重试,应该去看线。

4. RGMII 时序:同一块板换一批芯片就要改 delay 的原因

这是整颗芯片里最容易被当成"玄学"的部分。同一份 PCB、同一份驱动,换一批芯片就有个别板子开始丢包;把内部 delay 从 2ns 调到 1.5ns,问题消失,但另一批板子又开始出问题。要理解为什么会这样,得先算清楚 RGMII 在 1000M 下的时间预算到底有多紧。

4.1 源同步接口的眼图预算:为什么 125MHz 的 DDR 这么脆

RGMII 在千兆模式下用 125MHz 时钟跑 DDR:上升沿送 TXD[3:0],下降沿送 TXD[7:4],一个周期 8 位数据、一位控制(TX_CTL,反映 TXEN 和 TXERR 的异或)。时钟和数据从同一侧同时发出,这叫源同步——接收端不靠自己的时钟采样,而是用你发过去的时钟采样你发过去的数据。

一个周期是 8 纳秒,半周期 4 纳秒,看上去窗口很宽。但真正的采样窗口不是 4 纳秒:要减掉发送端的输出偏斜、接收端的建立保持要求、时钟本身的抖动、封装和过孔的寄生、以及 PCB 走线偏斜。PCB 上微带线大约 6~7 皮秒每毫米,带状线更慢,10 厘米的走线就有 600~700 皮秒的延迟。把这些全减完,留给设计师的余量往往只剩几百皮秒。

几百皮秒是什么概念?一根 5 厘米的时钟线与数据线不等长,就吃掉大半。所以 RGMII 必须做 delay:把时钟相对数据整体挪动 1.5~2 纳秒,让采样沿正好落在数据的中心而不是跳变沿上。这个"挪"可以在三个地方做——PCB 上把时钟线绕长、MAC 侧内部 delay、PHY 侧内部 delay——但只能在一处做。

4.2 TX/RX delay 的配置路径与取舍

这颗芯片把 RGMII 的时序微调放在扩展页里,手册的 ExtPage 表中有专门一组位控制 TXC 和 RXC 的延迟档位。TX 方向是 MAC 发、PHY 收,延迟控制的是 PHY 侧输出的 TXC 相对 TXD 的相位;RX 方向反过来,控制的是 PHY 采样 MAC 发来的数据时用哪个相位。

常见的实现思路是四条路径组合成一张决策表:

场景PCB 时钟线MAC 侧 delayPHY 侧 delay备注
常规设计,走线等长等长关闭开启约 2ns最省事,靠 PHY 内部补
走线时钟短于数据短关闭加大档位用寄存器补走线亏损
MAC 自带 delay 能力等长开启关闭注意别和 PHY 同时开
老 MAC 无 delay时钟绕长关闭关闭全靠 PCB,最脆弱

同开两处是最常见的翻车方式:MAC 挪了 2ns、PHY 又挪了 2ns,采样点从数据中心直接跑到边沿,表现为链路灯亮着、ping 小包正常、一跑大流量就 CRC 错。这类问题的可怕之处是它在轻载下完全正常,只有在连续满带宽传输、缓冲区被填满时才暴露。

4.3 用回读与误码统计验证,而不是靠眼睛看灯

时序配好没配好,不能靠"能不能 ping 通"判断。可验证的做法是三步:先把配置写下去再回读,确认位真的生效;然后在 MAC 侧读 CRC 错、对齐错、超短帧的计数器;最后用递增包长(64、128、512、1024、1518 字节)各打一轮满速流量,看错误计数是否随包长变化。如果错误集中在大包,多半是时序边际不够;如果小包也错,可能是 delay 开得太多。

# 打一轮满速流量并观察 MAC 侧错误计数增长 # 每轮 30 秒,包长递增,观察 crc_err / align_err 是否单调上升 for sz in 64 128 512 1024 1518; do ip link set eth0 mtu 1500 ping -f -s $((sz-28)) -c 20000 -i 0.002 192.0.2.1 >/dev/null 2>&1 echo "size=$sz crc=$(ethtool -S eth0 | awk '/crc_err/{print $2}')" done

这段脚本的价值在于把"感觉丢包"变成"有数字的增长曲线"。如果 64 字节完全无错、1518 字节错误率 0.1%,那是典型的建立时间不足,往 delay 大的方向调一档;如果所有包长都有固定比例错误,先怀疑线序和变压器,别急着动寄存器。

另外要记住 RGMII 在 100M 和 10M 下是降频工作的(25MHz 和 2.5MHz),同样的内部 delay 在低速下相对周期占比更大,可能变成过度延迟。有些设计会在速率切换时动态关掉内部 delay,手册里没有明说这个必须做,但如果你的板子在百兆下反而比千兆更容易出错,这就是第一个该怀疑的点。

5. 避坑与排查:链路不通时该先读哪几个寄存器

硬件调试现场最浪费时间的不是不会修,而是不知道从哪开始看。PHY 这类器件的特殊性在于:它有一半的状态是"电气层"的,示波器探不到;另一半是"协议层"的,逻辑分析仪又抓不全。寄存器是唯一同时覆盖两层的观测点。

5.1 五类高频故障的现象、根因与解法

第一条:读 PHYID1/PHYID2 全是 0xFFFF 或 0x0000。现象是驱动 probe 阶段就报"no phy found"。原因通常有三个:MDIO 线上拉缺失(很多 MAC 内部有弱上拉,但驱动能力不够长走线)、PHY 地址配错(地址由复位时的 LED 引脚电平锁存决定,某个 LED 引脚被外部电路拉错,地址就变了)、复位还没走完就开始读。解法是先量 MDIO 空闲电平是否为高,再核对复位期间各配置引脚的电平,最后在驱动里把"释放复位 → 等 10ms 以上 → 首次读 ID"写成固定顺序。

第二条:链路 up,小包正常,大包大量丢。现象是 ping 通、HTTP 卡、iperf 跑不动。原因是双工不匹配——一端被强制成 100M 全双工、另一端在自协商,协商方检测不到对端能力,默认退回半双工,于是全双工那侧不停发、半双工那侧检测到冲突就丢。解法是全线统一:要么都自协商,要么两端都强制且参数完全一致。手册的 ANLPAR 能直接告诉你对端到底广告了什么,比猜快得多。

第三条:千兆协商不上,稳定跑 100M。现象是速率永远停在 100M,怎么重启都一样。原因是四对线里有一对不通或指标不达标,下一页交换失败,双方按基页结果退回百兆。解法是逐对测通断和回波损耗,重点查变压器某一对的中心抽头电容和 RJ45 针脚。软件侧可以先读 GBCR 确认千兆能力有没有广告出去、读 GBSR 看主从解析是否报了故障,这能区分"没广告"和"广告了但没成"。

第四条:RGMII 随机 CRC 错,负载越高越严重。现象是空闲时完全正常,压测几分钟后错误计数开始涨。原因几乎都是 delay 只在一处生效或两处都开了,采样点落在边沿。解法是按 4.3 的递增包长法定位方向,一次只调一个方向的 delay 档位,调完必须回读确认并重新压测。

第五条:休眠唤醒后链路起不来,或者要等很久。现象是系统 resume 后网络要几秒才恢复,有时直接不通。原因涉及掉电节能和链路下降节能的状态机没退干净——芯片在低功耗模式下会关掉部分模拟电路,唤醒需要重新走一遍上电时序和协商。解法是在 resume 路径里显式做一次软复位再触发协商,别指望硬件自动恢复;同时检查节能相关的寄存器有没有在初始化时被误开。

5.2 一张速查表和一条最小排查路径

现场时间宝贵,把关键寄存器压成一张表贴在工位上,比翻手册快:

地址名称排查时看什么
0x00BMCRbit15 复位是否自清、bit12 协商是否使能、速率位组合是否合法
0x01BMSRbit5 协商完成、bit2 链路状态(需连读两次)
0x02/0x03PHYID身份是否读得到,0xFFFF 说明总线或复位有问题
0x04ANAR本端广告了哪些能力
0x05ANLPAR对端真实广告了什么,判断对方支不支持千兆
0x09GBCR千兆能力是否广告、是否手动指定主从
0x0AGBSR主从解析结果与配置故障位
0x13INSR中断源,链路变化/协商完成都能从这里读
0x1BLDPSR链路下降节能,排查"唤醒后不通"必看
0x1EEPAGSR扩展页选择,读到莫名其妙的值得先怀疑它

最小排查路径可以固化成四步:读 PHYID 确认总线通 → 读 BMSR 两次确认链路真实状态 → 读 ANAR/ANLPAR/GBCR 确认协商内容 → 读 GBSR 确认主从落到哪边。这四步走完,八成的问题都能定性到"电气层"还是"配置层",剩下的才是示波器和网分仪的活。

6. 设计取舍:把 PHY 当黑匣子,还是当一颗会说话的芯片

做嵌入式网络的人迟早要面对一个选择:把 PHY 当成一个"配好就不用管"的黑匣子,还是当成一颗随时可以问话的外设。前者省事——初始化写几张表、链路状态轮询一下、出问题换板子;后者费劲——要读手册、要理解位域、要在驱动里留调试接口。短期看前者赢,长期看后者救命。

真正的取舍点不在"要不要读寄存器",而在"用什么方式感知链路"。轮询简单但延迟高、占 CPU;中断实时但有共享资源风险(INSR 读清、和自协商状态机抢 MDIO 总线);硬件配置引脚最可靠但改一次要动 PCB。我一般会这么理解:链路状态用中断,速率双工用轮询,时序和电气参数用一次性配置加回读校验。三种机制各管一段,别指望一种打天下。

还有一层取舍是手册本身的用法。英文原版 PDF 的交叉引用做得不友好,讲 RGMII 时序的章节在前、对应的寄存器位在几十页后、电气参数又跳到另一章,排查一次要来回翻五六趟。这也是为什么一份对着寄存器表逐页校过的中文对照版有实际价值——它省下的不是翻译时间,而是"在三个章节之间反复跳转建立关联"的认知成本。如果你手上正有一块千兆板子卡在链路阶段,这份中文手册配合上面的四步排查路径,能把定位时间从半天压到半小时以内。

最后说个教训。早年我调一块新板,链路死活不通,把能换的器件换了一遍,最后发现是复位释放后只等了 2 毫秒就去读 ID,芯片内部 PLL 还没锁上,读回来全是 0xFFFF,而我当时把这当成了"总线没通"的证据,一路往错误方向查了两天。从那以后我每次 bring-up 一块新板,都强制先走一遍"复位时序 → 读 PHYID → 连读两次 BMSR → 读 ANAR/ANLPAR"的固定动作,一步都不跳,哪怕看起来完全没必要。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询