Zynq Petalinux环境下,如何通过UIO驱动访问预留内存?
问题描述
我有一块运行Petalinux的Zynq开发板,.xsa文件配置为通过AXI DMA实现处理系统(PS)与可编程逻辑(PL)间的数据传输,目前在VITIS中编写验证应用。已在system-user.dtsi中配置预留内存和DMA的UIO节点:
}; reserved-memory{ #address-cells=<1>; #size-cells =<1>; ranges; dma_reserved:buffer@0x30000000{ compatible="shared-dma-pool"; no-map; reg=<0x30000000 0x10000000>; }; }; dma_proxy@0 { compatible ="xlnx,dma_proxy"; dmas = <&axi_dma_0 1>; dma-names = "dma0"; memory-region = <&dma_reserved>; }; }; &axi_dma_0{ compatible = "generic-uio"; interrupt-names = "mm2s_introut", "s2mm_introut"; interrupt-parent = <&intc>; interrupts = <0 29 4 0 30 4>; reg = <0x40400000 0x10000>; };
通过dmesg确认预留内存已创建:
three:~$ dmesg | grep eserved Reserved memory: created DMA memory pool at 0x30000000, size 256 MiB OF: reserved mem: initialized node buffer@0x30000000, compatible id shared-dma-pool cma: Reserved 16 MiB at 0x2f000000 Memory: 735864K/1048576K available (7168K kernel code, 248K rwdata, 1940K rodata, 1024K init, 120K bss, 296328K res) hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers. three:~$
尝试用以下代码访问预留内存并配置DMA寄存器,但写入S2MM_DA寄存器未生效:
#define DRAM_BUFFER_ADDR 0x30000000 ... void DMA_Set(unsigned int * memaddr, uint32_t value) { *((volatile uint32_t *) (memaddr)) = value; } DMA_Set(DMA_Base_Addr + S2MM_DA, DRAM_BUFFER_ADDR);
核心疑问:如何将DMA作为UIO与预留内存关联使用,解决寄存器写入无效的问题?
解决方案
1. 修正UIO设备的寄存器映射方式
用户态程序不能直接操作物理地址,必须通过UIO设备提供的/dev/uioX接口将物理寄存器映射到用户虚拟空间:
- 先找到AXI DMA对应的UIO设备,执行
ls /dev/uio*,通常会显示/dev/uio0或类似节点 - 在应用中通过系统调用完成映射:
#include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #define UIO_DEV "/dev/uio0" #define DMA_REG_SIZE 0x10000 // 对应dts中reg的size值 int fd = open(UIO_DEV, O_RDWR); if (fd < 0) { // 处理设备打开失败逻辑 } volatile uint32_t *dma_regs = (volatile uint32_t *)mmap(NULL, DMA_REG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (dma_regs == MAP_FAILED) { // 处理内存映射失败逻辑 } // 用映射后的虚拟地址访问DMA寄存器,注意寄存器偏移需除以4(32位寄存器) dma_regs[S2MM_DA / 4] = DRAM_BUFFER_ADDR;
2. 正确访问no-map标记的预留内存
带no-map的预留内存不能直接用物理地址访问,需通过系统提供的dma_buf接口映射到用户空间:
#include <sys/stat.h> #define RESERVED_MEM_DEV "/sys/class/reserved-memory/dma_reserved/device/dma_buf" #define RESERVED_MEM_SIZE 0x10000000 int mem_fd = open(RESERVED_MEM_DEV, O_RDWR); if (mem_fd < 0) { // 处理设备打开失败逻辑 } void *dma_buf = mmap(NULL, RESERVED_MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0); if (dma_buf == MAP_FAILED) { // 处理内存映射失败逻辑 } // 现在可通过dma_buf指针直接操作预留内存 *(volatile uint32_t *)dma_buf = 0x12345678;
3. 清理Device Tree冗余配置
当前dts中同时配置了dma_proxy和generic-uio的DMA节点,会导致资源冲突。如果使用UIO方式操作DMA,需删除dma_proxy@0节点,保留如下配置即可:
}; reserved-memory{ #address-cells=<1>; #size-cells =<1>; ranges; dma_reserved:buffer@0x30000000{ compatible="shared-dma-pool"; no-map; reg=<0x30000000 0x10000000>; }; }; }; &axi_dma_0{ compatible = "generic-uio"; interrupt-names = "mm2s_introut", "s2mm_introut"; interrupt-parent = <&intc>; interrupts = <0 29 4 0 30 4>; reg = <0x40400000 0x10000>; };
4. 验证总线地址一致性
Zynq中PS物理地址与DMA总线地址通常一致,但需确认DRAM_BUFFER_ADDR是DMA可识别的总线地址。可执行cat /proc/iomem查看预留内存的物理地址范围,确保与dts中配置的0x30000000-0x3FFFFFFF一致。
内容的提问来源于stack exchange,提问作者Creator
相关产品推荐
相关产品推荐

