ZYNQ Linux 应用层 利用 AXI DMA 进行数据传输
ZYNQ Linux应用层 利用 AXI DMA 进行数据传输软件版本: VIVADO2018.2操作系统: Debian 9硬件平台: ZYNQ-MZ7100FA嵌入式操作系统:Debian(4.19.0-xilinx)AXI-DMA驱动:GitHub:bperez77/xilinx_axidma一、搭建硬件环境参见:《ZYNQ 修炼秘籍裸机篇 2019 版》第21章:CH21 利用 AXI D
目录
参考文章
- ZYNQ跑系统 系列(四) AXI-DMA的linux下运行.
- ZYNQ 在Linux系统层上通过DMA传输数据
- 基于xilinx zynq7000平台(zynq7030),使用 github上的axi_dma驱动操作dma(PL与PS通信)
- 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 进行批量数据环路测试
- 创建vivado工程,编译生成bit文件,导出硬件(包含bit文件)
- 打开SDK,创建zynq_fsbl和device_tree工程
二、编译GitHub上AXI-DMA驱动例程
GitHub上AXI-DMA驱动例程下载地址:https://github.com/bperez77/xilinx_axidma
注:Kernel 版本为:4.19.0 ,需要修改程序,否则无法编译成功。4.14版本不需要修改, 其他版本未测试。
-
xilinx_axidma/driver/axi_dma.c中添加头文件:
#include <linux/mod_devicetable.h>
-
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);
模块化编译
- 进入下载的驱动目录:xilinx_axidma-master
- 复制config_template.mk为config.mk
- 根据需要修改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
- 编译驱动和测试例程
make driver
make examples
- 编译完成后,生成文件将出现在outputs文件夹里
编译进内核
-
将GitHub上下载的xilinx_axidma_master文件夹放到内核中:drivers/dma/xilinx/xilinx_axidma目录中
-
进入xilinx_axidma_master目录,将driver和include目录中.h、.c文件拷贝到内核中的drivers/dma/xilinx/xilinx_axidma目录中
-
创建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
-
在内核的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).
-
在内核的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>;
};
};
配置内核
-
配置CMA大小,设置为32MB
make menuconfig
-
配置AXI-DMA
未编译进内核无该选项
四、编译Linux系统
参见:《ZYNQ 修炼秘籍 LINUX 篇 基于 debian9 系统》第21章:CH04_LINUX 移植 debian 系统
参考文章: ZYNQ跑系统 系列(四) AXI-DMA的linux下运行.
- 编译Uboot
- 编译Kernel
- 生成镜像
- 烧写SD卡
五、运行测试例程
-
Linux系统启动后查看启动信息:
CMA信息:
AXI-DMA信息
-
将GitHub上AXI-DMA编译生成的outputs文件夹里内容拷贝到开发板上
说明: libaxidma.so 一定要拷贝,程序运行需要
-
运行axidma_benchmark例程
chmod +x ./axidma_benchmark ./axidma_benchmark
结果:
-
运行axidma_transfer例程
chmod +x ./axidma_transfer ./axidma_transfer a.txt b.txt
结果:
六、错误说明
-
报如下错误,是因为CMA空间不够导致的,重新设置CMA大小可解决。
内核设备树中设置cma大小未能成功,暂未找到原因,只能 make menuconfig 设置cma大小。
原因:因为transfer_file函数中对发送和接收都分配了cma空间,所以发送和接收的文件大小不能超过设置的cma空间的一半
-
调用 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未正确配置。 -
报 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);
-
其他问题,暂未解决
七、采用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.h
和 dma-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.h
和 dma-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
说明:
目前该驱动未进行详细测试。
更多推荐
所有评论(0)