实战指南:在Vitis 2024.1的lwIP库中集成KSZ9031以太网PHY驱动
1. 为什么需要集成KSZ9031 PHY驱动
在Zynq-MPSoC平台上使用Vitis开发以太网功能时,默认的lwIP库可能不包含某些PHY芯片的驱动支持。KSZ9031作为Micrel(现Microchip)推出的千兆以太网PHY芯片,在工业领域应用广泛,但官方库中往往缺少现成的驱动代码。这就导致开发者需要手动添加对KSZ9031的支持,否则会遇到网络无法连接或速度协商失败的问题。
我最近在一个机器人控制项目中就遇到了这种情况。硬件团队选用了KSZ9031作为PHY芯片,但在Vitis 2024.1的lwIP 2.2.0库中找不到对应的驱动支持。经过一番摸索,我发现需要修改xemacpsif_physpeed.c文件,添加KSZ9031的识别代码和速度获取函数。这个过程看似简单,但有几个关键点需要注意,否则很容易踩坑。
2. 开发环境准备
2.1 确认工具版本
首先确保你的开发环境符合以下要求:
- Vitis版本:2024.1(不同版本的文件路径可能略有差异)
- lwIP库版本:2.2.0
- 硬件平台:Zynq-MPSoC系列(如ZU+、Zynq UltraScale+等)
建议在开始前先检查Vitis安装目录下的lwIP库路径。通常位于:
<Vitis安装目录>/data/embeddedsw/ThirdParty/sw_services/lwip220_v1_0/src/lwip-2.2.0/contrib/ports/xilinx/netif2.2 定位关键文件
对于PS端以太网(EMAC)的应用,主要需要修改的是xemacpsif_physpeed.c文件。这个文件负责PHY芯片的识别和速度协商功能。如果你使用的是PL端以太网(如通过FPGA逻辑实现的MAC),可能需要修改其他文件,但本文聚焦PS端的情况。
3. 添加KSZ9031支持代码
3.1 定义PHY识别参数
在文件开头添加以下宏定义,这些是KSZ9031的识别标志和寄存器配置:
/* KSZ9031 PHY标识 */ #define MICREL_PHY_IDENTIFIER 0x22 #define MICREL_PHY_KSZ9031_MODEL 0x220 /* 特殊功能寄存器定义 */ #define JLSEMI_IDENTIFIER 0x937C #define JLSEMI_PHY_SELECT_REG_OFFSET 0x1F #define JLSEMI_PHY_SPECIFIC_STATUS_REG_OFFSET 0x1A #define JLSEMI_PHY_SPECIFIC_PAGE 0xA43 #define JLSEMI_PHY_LCR_PAGE 0xD04 #define JLSEMI_PHY_LED_BLINK_PAGE 0x1000 #define JLSEMI_PHY_LED_CONTROL_REG_OFFSET 0x10 #define JLSEMI_PHY_LED_BLINK_REG_OFFSET 0x14这些定义告诉系统如何识别KSZ9031 PHY芯片。MICREL_PHY_IDENTIFIER是厂商ID,MICREL_PHY_KSZ9031_MODEL是设备ID,通过读取PHY的寄存器可以获取这些值。
3.2 实现速度获取函数
接下来添加get_phy_speed_ksz9031函数,这是驱动KSZ9031的核心代码:
static u32_t get_phy_speed_ksz9031(XEmacPs *xemacpsp, u32_t phy_addr) { u16_t temp; u16_t control; u16_t status; u16_t status_speed; u32_t timeout_counter = 0; u32_t temp_speed; u32_t phyregtemp; xil_printf("Start PHY autonegotiation \r\n"); // 配置RGMII时序 XEmacPs_PhyWrite(xemacpsp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control); control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control); XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); // 配置自协商参数 XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control); control |= IEEE_ASYMMETRIC_PAUSE_MASK | IEEE_PAUSE_MASK | ADVERTISE_100 | ADVERTISE_10; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); // 配置千兆自协商 XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &control); control |= ADVERTISE_1000; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, control); // 启动自协商 XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE | IEEE_STAT_AUTONEGOTIATE_RESTART; XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); // 等待自协商完成 xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); do { sleep(1); XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); if (timeout_counter++ >= 30) { xil_printf("Auto negotiation error \r\n"); return 0; } } while (!(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE)); xil_printf("autonegotiation complete \r\n"); // 读取协商结果 XEmacPs_PhyRead(xemacpsp, phy_addr, 0x1f, &status_speed); if ((status_speed & 0x40) == 0x40) return 1000; // 1000Mbps if ((status_speed & 0x20) == 0x20) return 100; // 100Mbps if ((status_speed & 0x10) == 0x10) return 10; // 10Mbps return 0; }这个函数完成了几个关键操作:
- 配置RGMII接口的时序参数
- 设置自协商广告能力(支持10/100/1000M)
- 启动并等待自协商完成
- 读取并返回协商后的速度
3.3 集成到现有代码中
最后,在现有的get_IEEE_phy_speed函数中添加对KSZ9031的识别逻辑:
if(phy_identity == MICREL_PHY_IDENTIFIER) { RetStatus = get_phy_speed_ksz9031(xemacpsp, phy_addr); }这段代码应该放在其他PHY识别逻辑的后面,确保系统能正确识别KSZ9031并调用我们实现的驱动函数。
4. 常见问题与调试技巧
4.1 PHY无法识别
如果系统无法识别KSZ9031,首先检查:
- 硬件连接是否正确,特别是MDIO/MDC信号线
- PHY地址是否正确(通常为0或1)
- 读取的PHY ID是否匹配
MICREL_PHY_IDENTIFIER和MICREL_PHY_KSZ9031_MODEL
可以在代码中添加调试打印:
xil_printf("PHY ID: 0x%04X, Model: 0x%04X\r\n", phy_identity, phy_model);4.2 自协商失败
如果自协商总是失败,可能是以下原因:
- 对端设备不支持相同的速度模式
- RGMII时序配置不正确
- 硬件信号质量问题
可以尝试强制设置特定速度(不推荐长期使用):
// 强制100M全双工 XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, IEEE_CTRL_SPEED_100 | IEEE_CTRL_FULL_DUPLEX);4.3 性能优化建议
- 启用中断模式而非轮询,降低CPU负载
- 根据实际应用调整缓冲区大小
- 考虑使用DMA传输提升吞吐量
5. 验证与测试
完成代码修改后,建议按以下步骤验证:
- 重新编译lwIP库和应用程序
- 加载到目标板运行
- 使用ping测试基本连通性
- 使用iperf测试实际吞吐量
- 长时间运行测试稳定性
典型的成功日志输出应该类似:
Start PHY autonegotiation Waiting for PHY to complete autonegotiation. autonegotiation complete Link speed: 1000Mbps如果遇到问题,可以逐步检查每个阶段的寄存器值,确保配置正确生效。我在实际项目中发现,RGMII时序配置特别关键,不当的设置会导致数据错误或连接不稳定。
