4、基本控制寄存器(BCR):复位、环路、速度选择、自动协商使能等位域详解

基本控制寄存器(BCR)是PHY芯片里你最先碰到的寄存器,没有之一。它的地址通常是0x00,每个bit都有自己的脾气。我个人习惯在调试链路时,第一件事就是读BCR,看它当前的状态——是复位了还是自协商跑完了。

BCR一共16位,IEEE 802.3规范把它安排得明明白白。我挑几个关键位域拆开讲,这些你在项目里几乎天天都要打交道。

4.1 第15位:复位(Reset)

写1到这个位,PHY会执行一次硬件复位。所有寄存器回到默认值,内部状态机从头跑。我在项目中遇到过一种坑:复位后立刻写其他寄存器,结果写入不生效。为什么?因为复位操作需要一点时间(典型值50μs到100μs),你写得太快,PHY还没准备好。我曾经排查过一整天,最后发现是复位后等待不够——从那以后我习惯复位后至少等200μs再操作。

警告: 复位位是自清零的。你写1,它自动变回0。读它永远返回0。别指望读它来判断复位是否完成;正确做法是读其他寄存器的默认值是否恢复,或者干脆等待固定延时。

4.2 第14位:回环模式(Loopback)

写1让PHY进入内部回环。发送的数据直接环回到接收路径,不经过外部线缆。这个位我在调试板级互联时特别喜欢用——先把环回打开,发已知报文,看能不能收回来。如果能,说明MAC到PHY的串行通路没问题;如果不行,问题大概率在本地。

注意:不同PHY对回环的实现细节有差异。有的PHY只环回数据路径,有的连MDI/MDIX状态也一起环。我建议在产品文档里确认一下具体行为。

4.3 第13位:速度选择(Speed Selection)

这个位需要和第6位(Speed Selection MSB)配合使用,因为PHY通常至少支持10M/100M/1000M三档速度。组合方式:

位13 (LSB) 位6 (MSB) 速度
0010 Mbps
10100 Mbps
011000 Mbps
11保留(通常不用)

手动配置速度时,记得同时设置自动协商使能位为0。否则你可能发现PHY根本不听你的,因为它还在自动协商。

技巧: 手动速度选择在产线测试和固定链路场景下很实用。我做过一个项目,对接的老设备只跑10M半双工,必须关掉协商硬设才能通。

4.4 第12位:自动协商使能(Auto-Negotiation Enable)

这是BCR里最重要的位之一。写1启动自协商,PHY会通过FLP Burst和对端交换能力信息。协商完成后,双方自动选择最佳速度和工作模式。

为什么要关掉它?有些场景下你必须关:

  • 对端设备不支持自协商
  • 链路固定,不需要协商过程(省时间)
  • 调试时想固定强制模式

我踩过的坑是:有些PHY在关掉自协商之后,速度选择位的优先级会变化。比如,你设了100M全双工,但PHY的实际行为可能还是按上次协商结果跑。解决方法是先复位,再设强制参数,最后才写BCR关协商。顺序很重要,你想想看。

4.5 第9位:全双工(Full Duplex)

只有在自协商关闭、手动配置模式下才有效。写1表示全双工,0表示半双工。这哥们儿和速度选择一起决定了最终的链路参数。

我见过有人只改了速度,忘了改全双工位,结果链路协商成半双工,吞吐量直接腰斩。排查时一定要把BCR整个读回来,别只盯着某一个位。

4.6 第8位:重启自协商(Restart Auto-Negotiation)

写1触发重新协商。注意这是单脉冲触发,不是保持位。写入后PHY会中断当前链路,重新开始FLP交换。我通常在修改了自协商能力寄存器(比如ANAR)后,必须写这个位让新能力生效。

实战经验: 有一次在客户现场,网口死活不通。抓包发现PHY在反复重启自协商。后来读BCR发现第8位被驱动误写成了1,导致它不断重启。解决方法是:驱动里每次写该位后要确保后续清零(硬件会自动清,但软件不要重复写1)。

4.7 其他位(第7、10、11位)

第7位:无意义(保留)。第10位:隔离模式(Isolate),极少用。第11位:电源下状态(Power Down),掉电模式能省电但会把链路断开。

BCR寄存器整体结构可以用下面这张图看清关系:

BCR(基本控制寄存器)位域结构图 Bit 15 复位 (自清零) Bit 14 回环 Loopback Bit 13 速度LSB Bit 12 自协商 使能 Bit 11 电源下 Power Down Bit 10 隔离 Isolate Bit 9 全双工 Bit 8 重启 自协商 Bit 6 速度MSB Bit 0~5 保留 Bit 7 保留 BCD寄存器地址 0x00,共16位 BCR配置注意事项 • 修改速度或双工模式前,先把自协商关掉(Bit12=0),否则改不动。 • 复位(Bit15)后至少等待200μs再做后续操作,不要依赖读回。 • 回环(Bit14)只用于本地自测,不能保留在生产模式。 • 重启自协商(Bit8)是边沿触发,写1后硬件会自动清0。

4.8 编程实战:如何正确读/写BCR

下面是一段伪代码,展示配置100M全双工关自协商的流程:

// 第一步:读BCR
u16 bcr = phy_read(phy_addr, 0x00);

// 第二步:清掉自协商使能位和速度位,再设置新值
bcr &= ~(1 << 12);       // 关自协商
bcr &= ~(3 << 6);        // 清速度两位(Bit13和Bit6)
bcr | = (1 << 13);        // 速度LSB=1 → 100M
bcr &= ~(1 << 6);        // 速度MSB=0
bcr | = (1 << 9);         // 全双工

// 第三步:写入BCR
phy_write(phy_addr, 0x00, bcr);

// 第四步:等待链路稳定
mdelay(300);  

// 读BCR确认
bcr = phy_read(phy_addr, 0x00);
if (bcr & (1 << 12)) {
    printf("WARNING: 自协商被意外打开!\n");
}

你想想看,这段代码里最容易被忽略的是第二步——很多人关自协商时没同时清掉速度位,结果PHY可能混用了上次协商残留的数值。

我个人保留一个习惯:每次写完BCR后,马上再读一遍,做一次回读校验。这么做虽然慢一点,但能立刻发现总线上的毛刺或写入错误。曾经在一次PCB改版后,MDIO走线过长导致写0x00变成了写0x1000,幸亏回读校验抓住了,不然板子焊上去三天都查不出问题。

进阶技巧: 有些PHY支持通过扩展寄存器直接返回BCR的协商结果(比如链接状态、实际速度)。但标准BCR不提供这些信息,你得另外读状态寄存器(BSR,地址0x01)来确认。BCR只管控制,不管状态,别搞混了。