相较于市面上多数将eMMC存储器直接挂在PS(Processing System)侧的核心板方案,我们这款核心板的设计有所不同——其eMMC是连接在PL(Programmable Logic)侧的。这种设计为驱动eMMC提供了两种思路:一是通过EMIO(Extended Multiplexed I/O)进行驱动,其软件驱动方式与挂载在PS侧基本一致;另一种则是在FPGA侧编写专用的硬件驱动代码,这种方式虽然可以利用8线模式实现更高速率,但开发复杂度也显著增加。本文将聚焦于第一种方法,详细介绍如何通过ZYNQ的EMIO接口,高效可靠地驱动PL侧的eMMC设备。
第一步:在Vivado Block Design中添加SDIO1接口
首先,需要在Vivado的Block Design(BD)中,为ZYNQ Processing System IP核添加并启用SDIO1外设接口。这将生成对应的硬件信号引脚,用于后续在PL侧进行连接。

第二步:创建顶层模块连接并缓冲信号
接下来,在PL侧创建一个eMMC_Top顶层模块(或类似的HDL模块)。该模块的主要任务是将从PS端通过EMIO映射过来的SDIO1接口信号引入,并进行必要的路由和缓冲处理。
我们主要关心四个关键信号:SDIO1_CLK、SDIO1_CLK_FB、SDIO1_CMD和SDIO1_DATA[3:0]。对于SDIO1的其他辅助信号,如SDIO1_LED、SDIO1_CDN等,在本应用中可以忽略不计。
一个关键的细节是时钟反馈信号SDIO1_CLK_FB的处理。为了提升时序稳定性和数据采样可靠性,通常建议将其与输出的SDIO1_CLK进行连接。一种更优的做法是使用FPGA内的IDELAYE2原语,在反馈路径上引入一个微小(例如1~2ns)的可控相位延迟,这能有效补偿板级走线延迟,优化建立/保持时间。
信号驱动到物理引脚前,必须使用IOBUF原语进行缓冲。以下是SDIO1_CMD和其中一位SDIO1_DATA信号的Verilog示例代码:
IOBUF #(
.DRIVE(12), // 指定输出驱动强度
.IBUF_LOW_PWR("FALSE"), // 低功耗模式设为FALSE以获得高性能
.IOSTANDARD("DEFAULT"), // 指定I/O标准
.SLEW("SLOW") // 指定输出压摆率为SLOW
) SDIO_1_0_cmd_iobuf_0 (
.I(SDIO_CMD_O), // 来自PS的输出信号
.IO(eMMC_CMD_pin_io), // 连接到物理引脚的双向端口
.O(SDIO_CMD_I), // 输入到PS的信号
.T(SDIO_CMD_T) // 三态控制信号
);
IOBUF #(
.DRIVE(12),
.IBUF_LOW_PWR("FALSE"),
.IOSTANDARD("DEFAULT"),
.SLEW("SLOW")
) SDIO_1_0_data_iobuf_0 (
.I(SDIO_DATA_O[0]),
.IO(eMMC_DATA_pin_io[0]),
.O(SDIO_DATA_I[0]),
.T(SDIO_DATA_T[0])
);
完成上述硬件连接与约束后,即可生成最终的XSA硬件描述文件,供软件开发使用。

第三步:在Vitis中导入并修改SDIO例程
打开Vitis IDE,基于上一步导出的XSA文件创建平台工程和应用项目。Xilinx提供了丰富的外设驱动示例,我们可以直接导入SDIO的例程作为起点。
在Vitis的Board Support Package设置中,找到ps7_sd_1(对应SDIO1)并点击“Import Examples”,选择xsdps_raw_example。

需要特别注意,默认的例程代码是针对SDIO_0(即ps7_sd_0)编写的。为了驱动我们所用的SDIO_1,必须修改设备ID。找到代码中调用XSdPs_LookupConfig()的地方,将其参数从XPAR_XSDPS_0_DEVICE_ID修改为XPAR_XSDPS_1_DEVICE_ID。这是确保驱动正确识别硬件SDIO接口的关键一步。
第四步:编译、下载与调试验证
完成代码修改后,编译整个工程,将生成的二进制文件下载到ZYNQ开发板中运行。为了验证eMMC是否驱动成功,最直接的方法是使用调试器。
在Vitis的Debug视图中运行程序,重点关注测试函数返回的Status变量值。根据SDIO驱动示例的设计逻辑,如果eMMC的初始化、读写测试全部通过,Status的值应为0(即XST_SUCCESS)。通过观察调试窗口中该变量的值,可以快速判断驱动是否成功。

总结
通过以上四个步骤,我们完成了从硬件引脚配置、FPGA逻辑连接,到软件驱动适配与调试的全流程。利用ZYNQ的EMIO功能驱动PL侧eMMC,实质上是在PS端运行标准SDIO驱动,并通过可编程逻辑单元完成电气接口的转换与适配。这种方法在性能与开发效率之间取得了良好平衡,是快速实现存储功能扩展的可靠方案。希望这篇在云栈社区分享的实践指南,能为你后续的ZYNQ项目开发提供清晰的参考。