W04-petalinux_axidma_build.md

W04-petalinux_axidma_build.md

社蕙 1377 2022-08-28

petalinux axidma驱动构建

1. 内核级别的dma driver与内建测试

参考Linux soft DMA driver

1.1. 硬件配置

搭建一个简单的DMA回环通路。(我不能确定回环之间放置FIFO是否会导致问题,按理来说应该不会)

dma block design

参考Zynq UltraScale+ PL Masters,Wiki指出了一些ZynqMP在配置PL端master总线时需要注意的问题。

1.1.1. ZyqnMP配置

直接应用block automation,其他什么也不用动。

参考MPSoC configuration GUI what is "Address Fragmentation High Address?",自2018.3后不需要手动配置启用高地址,已默认启用。

1.1.2. dma配置

dma conf

  1. 注意要启用Scatter Gather Engines,尽管Xilinx提到自己的驱动支持Simple Mode,但其内建测试无法再Simple Mode下工作。
  2. 注意要将BufferLength拉满。

dma addr

启用axi_dma_0的DDR_HIGH地址,注意要同时启用OCM否则无法正常分配地址。

Data_SG里有一个地址默认没有启用,因为SG是后来加的,我也不确定启用是否会出问题。

1.2. 软件配置

1.2.1. Kernel设置

petalinux-config -c kernel

打开GUI,进入Device Drivers > DMA Engine support > Xilinx DMA Engines,正常情况下,Xilinx DPDMA Engine正常勾选,然后将DMA Test Client for AXI DMA设置为module。记得保存为默认名.config,否则可能编辑无效。

可以通过再召唤GUI进行确认

1.2.2. 用户设备树编辑

打开system-user.dtsi(参考W03第1.5节:设备树),加入以下修改:

/ {
    axidmatest_0: axidmatest@0 {
        compatible ="xlnx,axi-dma-test-1.00.a";
        dmas = <&axi_dma_0 0
                &axi_dma_0 1>; 
        dma-names = "axidma0", "axidma1";
   };
};

dmas那里需要填入正确的名字,参见pl.dtsi,下面显示dma叫axi_dma_0,也与block design中的对应。

/ {
    amba_pl: amba_pl@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges ;
        axi_dma_0: dma@a0000000 {
            #dma-cells = <1>;
            clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
            clocks = <&zynqmp_clk 71>, <&zynqmp_clk 71>, <&zynqmp_clk 71>, <&zynqmp_clk 71>;
            compatible = "xlnx,axi-dma-7.1", "xlnx,axi-dma-1.00.a";
            interrupt-names = "mm2s_introut", "s2mm_introut";
            interrupt-parent = <&gic>;
            interrupts = <0 89 4 0 90 4>;
            reg = <0x0 0xa0000000 0x0 0x10000>;
            xlnx,addrwidth = <0x40>;
            xlnx,include-sg ;
            xlnx,sg-length-width = <0x1a>;
            dma-channel@a0000000 {
                compatible = "xlnx,axi-dma-mm2s-channel";
                dma-channels = <0x1>;
                interrupts = <0 89 4>;
                xlnx,datawidth = <0x20>;
                xlnx,device-id = <0x0>;
            };
            dma-channel@a0000030 {
                compatible = "xlnx,axi-dma-s2mm-channel";
                dma-channels = <0x1>;
                interrupts = <0 90 4>;
                xlnx,datawidth = <0x20>;
                xlnx,device-id = <0x0>;
            };
        };
    };
};

1.3. 上板测试

$ modprobe axidmatest
[ xxx ] dmatest: Started 1 threads using dma1chan0 dma1chan1
[ xxx ] dma1chan0-dma1c: terminating after 5 tests, 0 failures 3897 iops 22603 KB/s (status 0)

说明回环测试通过。

2. DMA From User Space

本章内容可能已被放弃,第3章可能是其更好的替代

理论参考

  1. wiki: DMA From User Space
  2. wiki: DMA From User Space 2.0
  3. github源码: Xilinx-Wiki-Projects/software-prototypes

这一套东西的问题在于,太他妈抽象了,完全不知道怎么用,可能github的源码需要编译module,我还没有研究,暂时搁置这个方案。

设计参考:

  1. AXI DMA Linux user space application on Zynq MPSoC platform:这个程序会直接报Segmentation Fault然后崩溃。
  2. 70413 - Zynq UltraScale+ MPSoC Example Design: Using 64-bit addressing with AXI DMA:附件代码编译不通过,报错缺少xaxidma.h不知道怎么解决,我认为这可能不是给petalinux用的。
  3. 更多相关:在Xilinx Support搜索Linux DMA from user space

3. Xilinx AXI DMA Driver and Library

参考:

  1. bperez77/xilinx_axidma:github库
  2. ZYNQ系列(十二)linux的DMA使用:中文的流程,缝百家之长

3.1. 修改用户设备树

&amba_pl {
    
	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-channel@a0000000 {
		xlnx,device-id = <0x0>;
	};
	dma-channel@a0000030 {
    
		xlnx,device-id = <0x1>;
	};
};

3.2. 编译

在这里编译采用编辑config.mk文件的方法,这里我们启用交叉编译,并指定zcu106对应的架构:

# Cross Compilation Options
CROSS_COMPILE = aarch64-linux-gnu-
ARCH = arm64
# Build Options
KBUILD_DIR = /home/megumism/xilinx-zcu106-2019.2/build/tmp/work/zcu106_zynqmp-xilinx-linux/linux-xlnx/4.19-xilinx-v2019.2+git999-r0/linux-xlnx-4.19-xilinx-v2019.2+git999
OUTPUT_DIR = outputs

接下来只需要

make driver
make examples

3.3. 程序上板

./output文件夹中的东西一并拷贝到sdcard中,在运行任何程序前需要先加载驱动:

$ insmod axidma.ko

axidma: axidma_dma.c: 672: DMA: Found 1 transmit channels and 1 receive channels.
axidma: axidma_dma.c: 674: VDMA: Found 0 transmit channels and 0 receive channels.

说明驱动正常。

3.4. 将output文件夹

4. 其他问题

4.1. xilinx_dmatest: No Tx channel

考虑两种情况:

  1. 硬件配置有问题,驱动没能检测到硬件。这在我第一次建立完bd之后(那时没有使用axidma.ko)调试了很久无果,后来重新建了一次bd,遂解决。
  2. 通道被其他设备占用,例如当启用axidma.ko的时候,dmatest就无法接触到dma端口。考虑rmmod

4.2. dstbuf[xxx] not copied! Expected aa, got bb

参考Linux AXI DMA test error on Zynq zc706 board

打开dma的Scatter Gather Engine

4.3. axi_dma.c:97:34: error: array type has incomplete element type ‘struct of_device_id’

参考Can not build petalinux module #123lordlothard commented on 12 May 2021 一楼给出的解决方案:

  1. add "#include <linux/of_address.h>" into driver/axi_dma.c
  2. in axidma_chrdev.c, change the parameter from of_dma_configure(dev->device, NULL,) ; to of_dma_configure(dev->device, NULL, true) ;

4.4. axidma: axidma_dma.c: axidma_request_channels: 649: Unable to get slave channel 0: tx_channel

  1. 参考Not finding tx_channel in device tree #47:通道被其他设备占用,例如当启用dmatest的时候,axidma.ko就无法接触到dma端口。考虑rmmod
  2. 参考Problem with two dma in design #98,rx channel探测不到可能是地址没有设置为>=40位
  3. 最后考虑DMA的中断是否正确连接到ZynqMP上

4.5. axidma: axidma_dma.c: axidma_prep_transfer: 236: Unable to prepare the dma engine for the DMA transmit buffer

参考Unable to prepare the dma engine for the DMA transmit buffer. #85

将DMA的"Width of Buffer Length Register"拉满(26)。

调用 int axidma_oneway_transfer(axidma_dev_t dev, int channel, void buf, size_t len, bool wait) 函数报 timed out 问题

5. 其他参考

  1. Introduction to Using AXI DMA in Embedded Linux:外国人在Arty Z7上跑的,附有代码但没有细看。
  2. ZYNQ跑系统 系列(四) AXI-DMA的linux下运行:与ZYNQ系列(十二)linux的DMA使用大同小异。
  3. ZYNQ Linux 应用层 利用 AXI DMA 进行数据传输:附有错误说明。