Zynq-Linux移植实战之GPIO模拟MDIO协议驱动多PHY芯片
1. 项目背景与需求分析在嵌入式网络设备开发中我们经常会遇到需要管理多个PHY芯片的场景。最近我在一个国产ZYNQ平台上做开发时就遇到了一个典型问题板载9个PHY芯片型号YT8521但ZYNQ PS端自带的MDIO接口只能直接管理其中2个剩下的7个PHY需要另寻解决方案。这种情况在实际项目中很常见特别是多端口交换机、工业网关等设备。传统做法是使用MDIO扩展芯片但这会增加BOM成本和PCB面积。经过评估我决定采用GPIO模拟MDIO协议的方式原因有三成本最优直接利用ZYNQ现有的GPIO资源无需额外芯片灵活性高可以自由控制时序适配不同PHY芯片可控性强软件实现便于调试和问题定位2. 硬件设计要点2.1 Vivado工程配置在Vivado中为每个PHY配置了两个AXI GPIO IP核MDC GPIO配置为纯输出模式默认低电平MDIO GPIO配置为双向模式默认上拉这里有个设计细节需要注意虽然每个PHY占用两个GPIO IP核看起来有点浪费但实测发现这种架构最稳定。我曾经尝试过用单个IP核控制多个PHY的MDIO线结果出现了信号完整性问题。关键配置参数参数项MDC GPIO配置MDIO GPIO配置数据位宽1位1位默认输出值低电平高电平中断使能禁用禁用三态控制无使能2.2 物理层设计经验PCB布局时有几个坑我踩过这里特别提醒上拉电阻MDIO线上必须加1.5K上拉电阻我最初漏接导致通信不稳定走线长度MDC和MDIO走线要尽量等长差异控制在50mm以内电源滤波每个PHY的VDD脚要加0.1μF去耦电容3. MDIO协议深度解析3.1 协议帧结构详解MDIO协议看似简单但时序要求严格。完整的数据帧包含以下几个部分前导码(Preamble)32个连续的1信号用于同步时钟起始位(Start)2位01模式标志帧开始操作码(OP Code)2位10表示读01表示写PHY地址(PHYAD)5位可寻址32个PHY设备寄存器地址(REGAD)5位每个PHY支持32个寄存器转换位(TA)2位读操作时切换数据方向数据(Data)16位有效数据空闲状态(Idle)MDIO恢复高阻态3.2 关键时序参数通过逻辑分析仪实测YT8521的时序要求MDC时钟频率最高2.5MHz实测稳定工作在1MHz建立时间(tSU)数据在MDC上升沿前至少稳定10ns保持时间(tH)数据在MDC上升沿后至少保持5ns输出延迟(tOD)PHY响应数据最大延迟300ns4. 软件实现关键代码4.1 底层GPIO操作先看最基本的GPIO控制函数这是整个驱动的基础// 设置MDC线电平 void mdc_low(int index) { switch(index) { case 2: Xil_Out32(MDC2_GPIO_ADDR,0x0); break; // 其他PHY实例省略... } } // 切换MDIO方向 void mdio_in(int index) { switch(index) { case 2: Xil_Out32(MDIO2_GPIO_ADDR0x4,0x1); break; // 其他PHY实例省略... } }4.2 位操作实现基于GPIO的位操作是模拟协议的核心void Mcu_Yt8521_Soft_Smi_Bit_Set(int index,u8 bit) { mdio_out(index); // 设置为输出模式 if(bit) mdio_high(index); else mdio_low(index); // 生成时钟上升沿 mdc_low(index); mdc_high(index); }4.3 完整读写流程读寄存器函数的实现特别要注意TA阶段的处理u16 Srv_Yt8521_Soft_I2c_Device_Read(int index,u8 phy_addr, u8 reg_addr) { // 发送前导码 for(int i 0; i 32; i) { Mcu_Yt8521_Soft_Smi_Bit_Set(index,1); } // 发送帧头 Mcu_Yt8521_Soft_Smi_Bit_Set(index,0); // Start Mcu_Yt8521_Soft_Smi_Bit_Set(index,1); // 发送操作码(读) Mcu_Yt8521_Soft_Smi_Bit_Set(index,1); Mcu_Yt8521_Soft_Smi_Bit_Set(index,0); // 发送PHY地址 for(int i 0; i 5; i) { Mcu_Yt8521_Soft_Smi_Bit_Set(index, phy_addr (0x10 i)); } // 发送寄存器地址 for(int i 0; i 5; i) { Mcu_Yt8521_Soft_Smi_Bit_Set(index, reg_addr (0x10 i)); } // TA阶段切换方向 mdio_in(index); u8 dummy; Mcu_Yt8521_Soft_Smi_Bit_Get(index,dummy); Mcu_Yt8521_Soft_Smi_Bit_Get(index,dummy); // 读取数据 u16 data 0; for(int i 0; i 16; i) { u8 bit; Mcu_Yt8521_Soft_Smi_Bit_Get(index,bit); data (data 1) | (bit 0x1); } return data; }5. 调试技巧与实战经验5.1 常见问题排查在开发过程中我遇到过几个典型问题PHY无响应检查硬件上拉电阻是否接好确认PHY地址设置正确YT8521默认0x01用示波器查看MDC/MDIO信号质量数据错位检查TA阶段的方向切换时机确认时钟极性正确上升沿采样调整GPIO操作之间的延时性能优化将频繁调用的函数声明为inline使用-O2编译优化适当降低时钟频率提高稳定性5.2 测试验证方法我常用的验证流程如下读取PHY ID所有YT8521的ID寄存器(0x03)应该返回0x11a环回测试配置PHY进入环回模式发送测试数据包压力测试连续读写不同寄存器1000次检查错误率# 示例测试命令 ./mdio_test r 2 0x03 # 读取PHY2的ID ./mdio_test w 3 0x00 0x1140 # 配置PHY3为100M全双工6. 性能优化建议经过实测这个方案在ZYNQ-7000上可以达到最大时钟频率1.25MHz满足大多数PHY需求单次读写耗时约320μs包含32位前导码CPU占用率全速运行时约15%如果需要进一步提高性能可以考虑使用PL端逻辑用Verilog实现MDIO控制器DMA加速批量传输寄存器数据中断优化用定时器中断代替轮询在实际项目中这个GPIO模拟方案已经稳定运行超过6个月管理着7个YT8521 PHY芯片日均处理超过50万次寄存器访问。相比商用MDIO扩展芯片方案节省了约12%的BOM成本特别适合对成本敏感的多端口设备。