ZYNQ7000平台UIO框架下可写不可读及DMA传输异常排查
一、内存预留失败的原因及修复
内存预留失败是后续读写异常的核心根源,优先解决该问题:
设备树配置格式错误
检查system-user.dtsi中的reserved-memory节点是否符合ZYNQ要求的规范结构,示例如下:reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; dma_reserved: dma@0x30000000 { reg = <0x30000000 0x1000000>; // 起始地址+内存大小,必须为4KB整数倍 no-map; }; };同时需确认预留地址段未被内核或其他设备占用——直接查看
/proc/iomem的现有分配情况,避开已使用区域。另外要验证编译后的dtb文件是否包含该节点,避免修改设备树后未编译生效。内核配置缺失
内核必须开启预留内存支持,执行zcat /proc/config.gz | grep CONFIG_RESERVED_MEM检查配置状态,若未开启需重新编译内核并勾选CONFIG_RESERVED_MEM选项。地址未按页对齐
ZYNQ要求预留内存的起始地址和大小必须是4KB页的整数倍,未对齐会导致内核拒绝预留,调整地址和大小至符合对齐要求即可。
修复完成后重启开发板,通过dmesg | grep reserved查看预留成功日志,再检查/proc/iomem确认预留区域存在。
二、UIO读写异常排查(内存预留修复后执行)
UIO设备树未关联预留内存
需将UIO节点与预留内存绑定,示例配置如下:my_dma_uio: uio@0x43c00000 { compatible = "generic-uio"; reg = <0x43c00000 0x1000>; // PL端寄存器基址+寄存器区域大小 memory-region = <&dma_reserved>; // 关联预留的DMA内存区域 };确保
compatible属性为generic-uio,否则内核无法识别为UIO设备。mmap映射参数错误
用户空间mmap时,需确认映射的是正确的/dev/uioX设备文件,映射长度需与设备树中reg或预留内存的大小匹配。同时检查mmap返回值,若返回MAP_FAILED,优先排查程序权限(如使用sudo运行)、设备节点是否存在等基础问题。PL端硬件逻辑异常
写入后读不到预期值大概率与PL侧逻辑有关:- 检查PL端寄存器是否配置为可读写模式,而非仅写
- 确认DMA传输方向配置正确(如需要PL回传数据,需将DMA设为PL到PS方向)
- 使用ILA工具抓取PL端信号,验证PS写入的数据是否成功传输至PL,以及PL是否将数据写回PS预留内存
用户空间代码细节问题
- 用
memcpy替代read时,需确认映射的虚拟地址对应正确的物理内存,且内存属性为可读写 - ZYNQ的PS与PL可能存在字节序差异,若PL使用大端、PS使用小端,需对读取的值做字节序转换
- PS侧缓存会导致写入数据未同步至物理内存,写入操作后可添加
__sync_synchronize()或cacheflush()函数强制同步缓存
- 用
三、分步验证流程
- 彻底解决内存预留问题,确保
dmesg和/proc/iomem均能确认预留区域存在 - 检查UIO设备加载状态:查看
/sys/class/uio/uio0/下的maps文件,确认映射地址与大小和设备树配置一致 - 编写极简测试代码,单独测试PL寄存器的读写(不涉及DMA),确认寄存器能正常响应
- 逐步测试DMA传输:先验证PS写PL读的单向传输,再验证PL写PS读的反向传输,缩小问题范围
内容的提问来源于stack exchange,提问作者Creator

