1. 车载以太网PHY基础与100BASE-T1链路建立

车载以太网PHY基础与100BASE-T1链路建立 学习卡片

任务目标

用最小可复现的软件模型完成 100BASE-T1 PHY 的上电初始化、链路建立与链路状态寄存器读取,最终确认 Link Up。真实硬件上,PHY 通过 MDIO 管理接口暴露寄存器;本案例用一段可编译运行的 C 代码模拟 PHY 寄存器与链路状态机,把“上电→复位→配置→自协商/链路建立→读状态”的完整闭环跑通,便于在没有板子的情况下验证逻辑,也便于后续替换为真实 MDIO 读写。

关键寄存器与流程

  • BMCR(0x00):bit15 软复位,bit12 自协商使能,bit9 重启自协商。
  • BMSR(0x01):bit2 Link Status,bit5 自协商完成。
  • PHY 专用寄存器 0x10:bit0 主从模式(1=Master),bit1 链路使能。
  • 流程:上电→软复位→等待复位完成→配置主从与自协商→触发链路建立→轮询 BMSR 直到 Link Up。
上电 Power-On 软复位 BMCR.15 配置主从/使能 REG 0x10 触发链路建立 BMCR.9 轮询 BMSR bit2 Link Status Link Up 确认成功 bit2=1

完整实现

下面代码用结构体保存 PHY 寄存器,用 mdio_read/mdio_write 模拟 MDIO 访问,用 phy_tick 模拟链路建立需要的时间。核心入口 phy_init_and_link 完成初始化并轮询到 Link Up。

#include <stdio.h>
#include <stdint.h>

/* 寄存器地址 */
#define REG_BMCR   0x00
#define REG_BMSR   0x01
#define REG_PHYCR  0x10   /* 100BASE-T1 专用:主从/使能 */

/* BMCR 位 */
#define BMCR_RESET    (1u << 15)
#define BMCR_ANENABLE (1u << 12)
#define BMCR_ANRESTART (1u << 9)

/* BMSR 位 */
#define BMSR_LINK   (1u << 2)
#define BMSR_ANEGDONE (1u << 5)

/* PHYCR 位 */
#define PHYCR_MASTER (1u << 0)
#define PHYCR_LINKEN (1u << 1)

typedef struct {
    uint16_t reg[32];
    int link_timer;   /* 链路建立倒计时,模拟物理层握手时间 */
} Phy;

static Phy g_phy;

/* 模拟 MDIO 读 */
static uint16_t mdio_read(Phy *p, uint8_t addr) {
    return p->reg[addr & 0x1F];
}

/* 模拟 MDIO 写,并处理复位与链路触发副作用 */
static void mdio_write(Phy *p, uint8_t addr, uint16_t val) {
    p->reg[addr & 0x1F] = val;

    if (addr == REG_BMCR && (val & BMCR_RESET)) {
        /* 软复位:清状态,复位位自清 */
        p->reg[REG_BMSR] = 0;
        p->reg[REG_BMCR] &= ~BMCR_RESET;
        p->link_timer = 0;
    }
    if (addr == REG_BMCR && (val & BMCR_ANRESTART)) {
        /* 触发链路建立:需要若干 tick 才 Link Up */
        p->link_timer = 3;
        p->reg[REG_BMSR] &= ~(BMSR_LINK | BMSR_ANEGDONE);
    }
}

/* 模拟时间推进:倒计时到 0 时链路建立完成 */
static void phy_tick(Phy *p) {
    if (p->link_timer > 0) {
        p->link_timer--;
        if (p->link_timer == 0) {
            p->reg[REG_BMSR] |= (BMSR_LINK | BMSR_ANEGDONE);
        }
    }
}

/* 上电初始化并建立链路,返回 0 成功,-1 超时 */
static int phy_init_and_link(Phy *p, int is_master, int max_poll) {
    /* 1. 软复位 */
    mdio_write(p, REG_BMCR, BMCR_RESET);
    /* 2. 配置主从模式并开启链路 */
    uint16_t cr = PHYCR_LINKEN | (is_master ? PHYCR_MASTER : 0);
    mdio_write(p, REG_PHYCR, cr);
    /* 3. 使能自协商并重启,触发链路建立 */
    mdio_write(p, REG_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);

    /* 4. 轮询 BMSR 直到 Link Up */
    for (int i = 0; i < max_poll; i++) {
        phy_tick(p);
        uint16_t bmsr = mdio_read(p, REG_BMSR);
        if (bmsr & BMSR_LINK) {
            printf("Link Up after %d poll(s), BMSR=0x%04X\n", i + 1, bmsr);
            return 0;
        }
    }
    printf("Link timeout, BMSR=0x%04X\n", mdio_read(p, REG_BMSR));
    return -1;
}

int main(void) {
    int rc = phy_init_and_link(&g_phy, 1 /* Master */, 10);
    printf("PHYCR=0x%04X BMCR=0x%04X\n",
           mdio_read(&g_phy, REG_PHYCR),
           mdio_read(&g_phy, REG_BMCR));
    return rc == 0 ? 0 : 1;
}

运行与验证

gcc -Wall -O2 phy_link.c -o phy_link && ./phy_link

判定标准:程序退出码为 0,输出包含 Link Up,且打印的 BMSR 中 bit2(Link) 与 bit5(自协商完成) 均为 1。若把 max_poll 改为小于 3,会走超时分支并返回 1,可用于验证失败路径。

接入真实硬件

把 mdio_read/mdio_write 替换为真实 MDIO 控制器读写即可:REG_BMCR/REG_BMSR 是 IEEE 802.3 标准寄存器,REG_PHYCR(0x10) 的主从位需按具体 PHY 数据手册确认。真实链路建立时间由物理层决定,轮询上限应结合手册的链路建立时间设置。