You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 17:02:07