参考文章

  1. ZYNQ跑系统 系列(四) AXI-DMA的linux下运行.
  2. ZYNQ 在Linux系统层上通过DMA传输数据
  3. 基于xilinx zynq7000平台(zynq7030),使用 github上的axi_dma驱动操作dma(PL与PS通信)
  4. Linux DMA From User Space 2.0

前言

软件版本: VIVADO2018.2
操作系统: Debian 9
硬件平台: ZYNQ-MZ7100FA
嵌入式操作系统:Debian(4.19.0-xilinx)
AXI-DMA驱动:GitHub:bperez77/xilinx_axidma


一、搭建硬件环境

参见:《ZYNQ 修炼秘籍裸机篇 2019 版》第21章:CH21 利用 AXI DMA 进行批量数据环路测试

  1. 创建vivado工程,编译生成bit文件,导出硬件(包含bit文件)
  2. 打开SDK,创建zynq_fsbl和device_tree工程
    在这里插入图片描述
    在这里插入图片描述

二、编译GitHub上AXI-DMA驱动例程

GitHub上AXI-DMA驱动例程下载地址:https://github.com/bperez77/xilinx_axidma

注:Kernel 版本为:4.19.0 ,需要修改程序,否则无法编译成功。4.14版本不需要修改, 其他版本未测试。

  1. xilinx_axidma/driver/axi_dma.c中添加头文件:

    #include <linux/mod_devicetable.h>
    
  2. xilinx_axidma/driver/ axidma_chrdev.c中修改277行:

    // Configure the DMA device
    of_dma_configure(dev->device, NULL);
    

    为:

    // Configure the DMA device
    of_dma_configure(dev->device, NULL, true);
    

模块化编译

  1. 进入下载的驱动目录:xilinx_axidma-master
  2. 复制config_template.mk为config.mk
    在这里插入图片描述
  3. 根据需要修改config.mk中变量
    CROSS_COMPILE = arm-linux-gnueabihf-
    ARCH = arm
    KBUILD_DIR = /home/osrc/Projects/zynq-mz7100fa/osrc-lab-linux-4.19/sources/kernel/
    OUTPUT_DIR = outputs
    
  4. 编译驱动和测试例程
    make driver
    
    make examples
    
  5. 编译完成后,生成文件将出现在outputs文件夹里
    在这里插入图片描述

编译进内核

  1. 将GitHub上下载的xilinx_axidma_master文件夹放到内核中:drivers/dma/xilinx/xilinx_axidma目录中

  2. 进入xilinx_axidma_master目录,将driver和include目录中.h、.c文件拷贝到内核中的drivers/dma/xilinx/xilinx_axidma目录中
    在这里插入图片描述

  3. 创建Makefile文件,写入如下内容:

    DRIVER_NAME = xilinx-axidma
    $(DRIVER_NAME)-objs = axi_dma.o axidma_chrdev.o axidma_dma.o axidma_of.o
    obj-$(CONFIG_XILINX_AXI_DMA_GitHub) += $(DRIVER_NAME).o
    
  4. 在内核的drivers/dma/xilinx/Kconfig文件中添加如下内容:

    config XILINX_AXI_DMA_GitHub
    	tristate "Xilinx AXI DMA GitHub"
    	select DMA_ENGINE
    	help
    	  Enable support for Xilinx Axi DMA(GitHub).	  
    

    在这里插入图片描述

  5. 在内核的drivers/dma/xilinx/Makefile文件中添加如下内容

    obj-$(CONFIG_XILINX_AXI_DMA_GitHub) += xilinx_axidma/
    

    在这里插入图片描述

三、配置Linux系统

参考文章: ZYNQ跑系统 系列(四) AXI-DMA的linux下运行.

修改设备树

在设备树中添加AXI-DMA相关内容:

/* AXI-DMA */
axidma_chrdev: axidma_chrdev@0 {
    compatible = "xlnx,axidma-chrdev";
    dmas = <&axi_dma_0 0 &axi_dma_0 1>;
    dma-names = "tx_channel", "rx_channel";
};

axi_dma_0: dma@40400000 {
	#dma-cells = <1>;
	clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
	clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>;
	compatible = "xlnx,axi-dma-1.00.a";
	interrupt-names = "mm2s_introut", "s2mm_introut";
	interrupt-parent = <&intc>;
	interrupts = <0 29 4 0 30 4>;
	reg = <0x40400000 0x10000>;
	xlnx,addrwidth = <0x20>;
	xlnx,sg-length-width = <0x17>;
	dma-channel@40400000 {
		compatible = "xlnx,axi-dma-mm2s-channel";
		dma-channels = <0x1>;
		interrupt-parent = <&intc>;
		interrupts = <0 29 4>;
		xlnx,datawidth = <0x20>;
		xlnx,device-id = <0x0>;
	};
	dma-channel@40400030 {
		compatible = "xlnx,axi-dma-s2mm-channel";
		dma-channels = <0x1>;
		interrupt-parent = <&intc>;
		interrupts = <0 30 4>;
		xlnx,datawidth = <0x20>;
		xlnx,device-id = <0x1>;
	};
};

在这里插入图片描述

配置内核

  1. 配置CMA大小,设置为32MB

    make menuconfig
    

    在这里插入图片描述

  2. 配置AXI-DMA

    在这里插入图片描述
    未编译进内核无该选项
    在这里插入图片描述

四、编译Linux系统

参见:《ZYNQ 修炼秘籍 LINUX 篇 基于 debian9 系统》第21章:CH04_LINUX 移植 debian 系统

参考文章: ZYNQ跑系统 系列(四) AXI-DMA的linux下运行.

  1. 编译Uboot
  2. 编译Kernel
  3. 生成镜像
  4. 烧写SD卡

五、运行测试例程

  1. Linux系统启动后查看启动信息:
    CMA信息:
    在这里插入图片描述
    AXI-DMA信息
    在这里插入图片描述
    在这里插入图片描述

  2. 将GitHub上AXI-DMA编译生成的outputs文件夹里内容拷贝到开发板上
    说明: libaxidma.so 一定要拷贝,程序运行需要
    在这里插入图片描述

  3. 运行axidma_benchmark例程

    chmod +x ./axidma_benchmark
    ./axidma_benchmark
    

    结果:
    在这里插入图片描述

  4. 运行axidma_transfer例程

    chmod +x ./axidma_transfer
    ./axidma_transfer a.txt b.txt
    

    结果:
    在这里插入图片描述

六、错误说明

  1. 报如下错误,是因为CMA空间不够导致的,重新设置CMA大小可解决。
    内核设备树中设置cma大小未能成功,暂未找到原因,只能 make menuconfig 设置cma大小。

    在这里插入图片描述
    原因:因为transfer_file函数中对发送和接收都分配了cma空间,所以发送和接收的文件大小不能超过设置的cma空间的一半
    在这里插入图片描述

  2. 调用 int axidma_oneway_transfer(axidma_dev_t dev, int channel, void buf,
    size_t len, bool wait)
    函数报 timed out 问题:
    在这里插入图片描述
    ~~原因:wait 标志设置为true
    如果wait 标志设置为true,并且
    发送数据大于fifo最大值*,那么该函数会等待超时。
    因为fifo满了以后无法继续发送数据,不会将TLAST 信号拉高,所以发送会超时(默认10s)。~~

    int axidma_oneway_transfer(axidma_dev_t dev, int channel, void *buf,
            size_t len, bool wait)
    

    超时的典型原因:
    (1) IP没有发送TLAST信号,因此永远不会触发中断
    (2) AXI DMA IP的中断未正确连接
    (3) 设备树中AXI DMA IP的属性不正确,导致使用了错误的中断号
    (4) AXI DMA IP的缓冲区地址宽度设置得太低。默认为14位,这最多只能进行16 KB的传输(最大为23位,8MB)
    (5) PL程序有问题:比如 TKEEP值不对(TKEEP是用来表明TDATA相关字节的内容是否作为数据流的一部分被处理。TKEEP字节修饰符未被确认的那些相关字节是空字节,可以从数据流中去除),程序逻辑有问题等。
    (6)Linux系统启动后 重新下载bit,导致AXIDMA未正确配置。

  3. timed out 后,下次无法正常发送接收问题:
    原因:AXI-DMA IP核的 MM2S 或 S2MM任何一个通道复位整个IP核都复位,寄存器值恢复默认值。
    xilinx_dma.c 驱动中 通道复位后只对当前通道的中断使能,其他通道未使能中断

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    解决:在 xilinx_dma.c 中的 xilinx_dma_start_transfer 函数中添加中断使能设置:
    (kernel/sources/kernel/drivers/dma/xilinx/xilinx_dma.c)

    /* Enable interrupts */
    dma_ctrl_set(chan, XILINX_DMA_REG_DMACR,
    	      XILINX_DMA_DMAXR_ALL_IRQ_MASK);
    

    在这里插入图片描述
    或者修改 xilinx_dma.c 中的 xilinx_dma_chan_reset 函数如下:

    u32 reg;
    reg = dma_read(chan, XILINX_DMA_S2MM_CTRL_OFFSET + XILINX_DMA_REG_DMACR);
    dma_write(chan, XILINX_DMA_S2MM_CTRL_OFFSET + XILINX_DMA_REG_DMACR, 
    		  XILINX_DMA_DMAXR_ALL_IRQ_MASK | reg);
    reg = dma_read(chan, XILINX_DMA_MM2S_CTRL_OFFSET + XILINX_DMA_REG_DMACR);
    dma_write(chan, XILINX_DMA_MM2S_CTRL_OFFSET + XILINX_DMA_REG_DMACR, 
    		  XILINX_DMA_DMAXR_ALL_IRQ_MASK | reg);
    

    在这里插入图片描述

  4. 其他问题,暂未解决

    在这里插入图片描述
    在这里插入图片描述

七、采用proxy-dma驱动

官方wiki链接:Linux DMA From User Space 2.0
官方GitHub上proxy-dma源码链接:Linux User Space DMA Repository

源码中没提供 Makefile 来生成ko文件,可参考 基于xilinx zynq7000平台(zynq7030),使用 github上的axi_dma驱动操作dma(PL与PS通信)博客中的 Makefile:

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
 
KERNEL_DIR=xxx/linux-xlnx(这里指定内核源码根目录路径)
 
 
all:
	make -C $(KERNEL_DIR) M=`pwd` modules
 
clean:
	rm -rf *.o *.cmd *.order Module.symvers *.ko *.mod* ./.* .tmp_versions
 
obj-m += dma-proxy.o

驱动编译:
将源码中的 dma-proxy.hdma-proxy.c 以及 自己编写的 Makefile放到同一文件夹中,配置好交叉编译环境后,直接 make 即可:

编译最后输出:

  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/osrc/Projects/qkd/app/software-prototypes-master/driver/dma-proxy.mod.o
  LD [M]  /home/osrc/Projects/qkd/app/software-prototypes-master/driver/dma-proxy.ko
make[1]: Leaving directory '/xxx/kernel'

生成 dma-proxy.ko 文件:
在这里插入图片描述
测试程序编译:
将源码中的 dma-proxy.hdma-proxy-test.c 放到同一文件夹中,配置好交叉编译环境后运行:

arm-linux-gnueabihf-gcc ./dma-proxy-test.c -o ./dma-proxy-test -pthread

生成 dma-proxy-test 二进制文件:
在这里插入图片描述

修改设备树:
将上文中的 axidma_chrdev 节点 替换为 dma_proxy 节点:

驱动代码中固定dma-names名称为dma_proxy_rx和dma_proxy_tx,因此不能随意修改,且驱动初始化中默认同时开启rx和tx通道,因为,你如果不是成对使用,需要注释掉驱动中多余通道的初始化代码。

/* AXI-DMA: github.com/bperez77/xilinx_axidma */
/*axidma_chrdev: axidma_chrdev@0 {
	compatible = "xlnx,axidma-chrdev";
	dmas = <&axi_dma_0 0 &axi_dma_0 1>;
	dma-names = "tx_channel", "rx_channel";
};*/

/* AXI-DMA: github.com/Xilinx-Wiki-Projects/software-prototypes */
dma_proxy {
	compatible ="xlnx,dma_proxy";
	dmas = <&axi_dma_0 0 &axi_dma_0 1>;
	dma-names = "dma_proxy_rx", "dma_proxy_tx";
};

内核编译:
将上文内核配置中的 Xilinx Axi Dma GitHub 选项取消

运行程序:

insmod dma-proxy.ko

在这里插入图片描述
在这里插入图片描述

./dma-proxy-test 1000 1000

在这里插入图片描述
说明:
目前该驱动未进行详细测试。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐