4. MDIO 控制器硬件设计:寄存器接口、时钟要求、PCB 布局注意事项

设计 MDIO 控制器,很多人上来就写代码。我劝你先想清楚硬件接口怎么做。寄存器配置错了,时钟抖了,PCB 上串扰了,软件怎么调都白搭。这一节我把三个关键点拆开讲,每个都是我在项目里踩过的坑。

4.1 寄存器接口:APB 还是 AHB?地址分配怎么定?

MDIO 控制器一般挂在外设总线上。我个人习惯用 APB,因为它简单、时序好约束。但如果你要跑 50 MHz 以上的 MDC,建议用 AHB 加上 FIFO 缓冲,否则 CPU 会被频繁中断拖死。

寄存器配置清单(典型 32 位):
偏移地址寄存器名功能
0x00MDIO_CTRL控制:使能、模式选择、中断使能
0x04MDIO_CLK_DIV分频系数:MDC = 总线时钟 / (2*(div+1))
0x08MDIO_ADDRPHY 地址 + 寄存器地址
0x0CMDIO_WDATA写数据寄存器
0x10MDIO_RDATA读数据寄存器(只读)
0x14MDIO_STATUS状态:忙、错误、完成

注意一个细节:读数据寄存器一定要加同步逻辑。曾有一次,我的 MDC 域和总线域没处理好亚稳态,读回来的数据偶尔跳变。我加了两级触发器做跨时钟同步,问题才解决。记住,MDIO 是双时钟域——总线时钟和 MDC 时钟可能不同源,寄存器接口是第一个亚稳态陷阱。

4.2 时钟要求:MDC 怎么生成?抖动怎么控制?

MDC 由控制器内部分频产生。标准规定 MDC 最大 2.5 MHz,但实际操作中,如果你只接一个 PHY,跑到 12.5 MHz 也常见。不过别盲目提速——PCB 走线一长,反射和串扰会让眼图闭合。

分频器的实现很简单:

// Verilog 示例:MDC 分频生成(双沿采样时可减半)
reg [7:0] clk_cnt;
reg mdc_reg;
always @(posedge bus_clk) begin
    if (clk_cnt < CLK_DIV - 1)
        clk_cnt <= clk_cnt + 1;
    else begin
        clk_cnt <= 0;
        mdc_reg <= ~mdc_reg;
    end
end
assign mdc = mdc_reg;
陷阱:占空比不对称
如果分频系数是奇数,MDC 高电平与低电平宽度差一个周期。过大的占空比偏差会让 PHY 采样出错。我建议只用偶数分频,或者用双边沿计数保证 50% 占空比。

再说抖动。MDC 的抖动主要来自分频电路的相位噪声和供电噪声。我有一次在 FPGA 上验证,发现 MDC 上升沿抖动达 2 ns,PHY 那边直接丢包。后来我在 MDC 输出路径上加了一个 33 Ω 的串联电阻,并靠近 FPGA 引脚放置,抖动降到了 500 ps 以内。说到 PCB……下面重点讲。

4.3 PCB 布局注意事项

MDIO 是低速信号(2.5 MHz),但很多人忽视它。你想想看,MDC 和 MDIO 两条线的长度差超过 2 英寸,时序窗口就偏了。我归纳了三条核心规则:

  1. 等长走线:MDC 与 MDIO 长度差控制在 500 mil 内。如果有多个 PHY,每个 PHY 的分支也要等长。
  2. 远离噪声源:别跟开关电源的 DC-DC 电感、高频时钟线平行走。我在一个项目中碰到过,MDIO 线挨着 100 MHz DDR 时钟走,上拉电阻都救不了。后来割线绕开,问题消失。
  3. 上拉与端接:MDIO 是开漏,必须外部上拉到 1.8V 或 3.3V。上拉电阻通常 1.5kΩ ~ 4.7kΩ,根据线长和负载调整。我一般用 2.2kΩ,效果最均衡。如果 MDC 走线超过 6 英寸,建议在源端加 22 Ω 串联电阻抑制过冲。
MDIO 控制器硬件设计核心要素 MDIO 控制器 寄存器接口 APB/AHB 总线 跨时钟同步 时钟要求 MDC 分频生成 占空比 / 抖动控制 PCB 布局 等长走线 / 隔离 上拉电阻 / 端接 重点:三个模块互相影响,任何一个出问题都会导致 MDIO 通信失败 设计时优先保证寄存器接口时序闭合,再优化时钟质量,最后做 PCB 仿真验证
避坑指南: 我曾经在一个 6 层板的项目中,把 MDIO 信号走在两个地平面之间的中间层,结果因为过孔阻抗不连续,眼图完全闭合。后来我把 MDC 和 MDIO 都改到表层,并用地线包围,再也没出过问题。记住:低速信号也要有完整的参考平面,别以为 2.5 MHz 就可以随便走。

嗯,关于时钟还有一个常见误区——有些人喜欢用 PLL 分频出 MDC。我个人不推荐,除非你有专用的 IO 时钟输出管脚。用普通 GPIO 输出 MDC,抖动会比 PLL 专用时钟输出大一个数量级。如果必须用 PLL,记得在靠近 PHY 的地方加一个 74LVC1G125 做时钟整形。

最后提一下寄存器接口的测试点。设计时一定要把 MDIO_CTRL 和 MDIO_STATUS 的各个位引出到测试焊盘。我在调试 FPGA 原型时,靠一个逻辑分析仪抓 MDC 和 MDIO 线上的波形,同时读 STATUS 寄存器里的 “忙” 位,才快速定位到分频系数算错了。硬件设计时多留几个测试点,能省你一周的调试时间。