linux的DMA对于新手而言一直是一个噩梦,先不谈如何实现用户空间的零拷贝DMA传输,光是Linux环境下的DMA传输就已经感觉比较棘手,一方面是对Linux了解不够深入,另一方面则是Linux在相关的使用说明方面的确没有比较好的官方支持。
Xilinx提供了一个AXI-DMA的IP核,其可以通过AXI-Lite进行配置,命令其从AXI高性能总线(HP)上直接的对内存数据进行读取存储,这一切在PS使用裸机时感觉是那么的简单,就如同之前在MCU上一般,调用库函数对DMA配置好起始、结束地址、传输大小及相关的即可。但是这一切到linux上则变得狰狞起来。复杂的基于DMA engine 的操作机制使得刚开始上手在zynq上使用linux系统操作AXI-DMA变得不那么简洁明了。
而到了实际应用中,用户往往是在用户空间申请一块内存区域,想要从PL端读一些数据到这个内存区域,或者是从这块内存区域写到PL端,如果直接的使用cpu进行搬运,则会耗费大量的时间,DMA是不可或缺的。
为了“避免”繁杂的linux下dma engine的操作,有的用户想到了是否可以只把AXI-DMA这个IP核的寄存器(挂载在AXI-Lite总线上)通过mmap的方式映射到内存中,然后像之前裸机上一样,对这块内存读写就直接配置AXI-DMA寄存器,完成了对AXI-DMA的配置操作,但是,其也不可避免的从内核空间通过copy_to_usr来拷贝数据到用户空间,在大批量的数据时,这是很缓慢的一个过程。
幸好,有一个开源项目xilinx_axidma,实现了从用户空间使用AXI-DMA的零拷贝,并且将其封装为了库,这篇文章主要就是记录如何使用这个库的。
开发板:黑金的zynq7010。
petalinux版本:2017.4。
vivado版本:2017.4。
根据第一章的内容,建立petalinux工程,hdf文件需要选择准备工作中第二条产生的hdf(有需要的直接评论留言给我)。
我使用的是黑金自带的内核,我检查DMA的项都已开启,就没有动。
如果使用自己编译的内核,可能需要查看几个CONFIG是否开启。(别的博客都这么说)
CONFIG_CMA=y
CONFIG_DMA_CMA=y
CONFIG_XILINX_DMAENGINES=y
CONFIG_XILINX_AXIDMA=y
CONFIG_XILINX_AXIVDMA=y
CONFIG_DMA_SHARED_BUFFER=y
根据petalinux的版本不同,好像有几个配置也不需要了,自己网上看吧,我用的黑金的内核,直接Petalinux-config -c kernel,查看DMA的项都开启了,就没搭理。
CMA是什么我也不知道,具体为什么配置CMA我也不知道,我只看到网上有人说没有配CMA,运行的时候会出错。
根据其他博主的文章现在知道了。
原因:因为transfer_file函数中对发送和接收都分配了cma空间,所以发送和接收的文件大小不能超过设置的cma空间的一半。
后面根据别人的博客出一个错误的说明。
打开设备树文件
$ vi project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
增加dma相关内容,这里使用的设备树的引用覆盖的方法来修改device-id。也有直接去pl.dtsi中更改的。
&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@40400000 {
xlnx,device-id = <0x0>;
};
dma-channel@40400030 {
xlnx,device-id = <0x1>;
};
};
不做过多解释。
修改config_template.mk
将config_template.mk 名字改为confi.mk,里面内容修改如下
第21行改为 CROSS_COMPILE = arm-linux-gnueabihf-
第25行改为 ARCH = arm
第35行改为 KBUILD_DIR = /home/workzc/alinx/50-dma_test/dma_test/build/tmp/work/plnx_arm-xilinx-linux-gnueabi/linux-xlnx/4.9-xilinx-v2017.4+git999-r0/linux-xlnx-4.9-xilinx-v2017.4+git999
第41行改为 OUTPUT_DIR = outputs
第35行 是petalinux-build -c kernel以后才产生的,路径可能和这个不一样,自己查找。
将include中的两个.h文件移动到driver目录中
编译驱动程序
$ make driver
编译应用程序
$ make examples
该步骤可以使用petalinux module那种方式将ko文件弄到文件系统,或者直接编译进内核,方法网上看吧。
接下来就是挂载,运行了。
运行axidma_transfer,回环测试。
创建两个txt文件,1.txt和2.txt。
在1.txt文件中胡乱输入点内容。
运行axidma_transfer,2.txt中会出现和1.txt一样的内容。
笔者也是小白一枚,都是按照网上别人博客来的,下面将我看到几个不错的博客贴出来,供大家欣赏。
这几篇博客都是根据 github上的那个dma代码来做解释的。
1. ZYNQ Linux应用层 利用 AXI DMA 进行数据传输
该文章有错误说明,第二个错误我就碰上了,我是用自己创建的vivado工程产生的hdf,结果不中用,出了第二个问题,还没仔细分析原因。改为黑金的DMA回环的hdf好用了。
我关于config.mk的修改来源此文章,就是他没说KBUILD_DIR = /home/osrc/Projects/zynq-mz7100fa/osrc-lab-linux-4.19/sources/kernel/ 这东西的来源是哪让我费了点功夫。
2. ZYNQ #3 - Linux环境下在用户空间使用AXI-DMA进行传输
该文章还有个后续#4 讲的是PS使用直接采集PL外接的ADC数据,相当不错。
3. ZYNQ跑系统 系列(三) AXI-DMA的linux下运行
他们都可以改了设备树,直接弄到sd卡里启动就行,我不会啊。我都是改了设备树在重新编译,重新烧录,相当麻烦,会的大佬留言教教小弟。这篇开始cma没有配置,然后改了设备树,扔SD卡里就直接用了,好神奇。
4. Zynq7000学习 1.如何在Linux平台上运行DMA模块
这篇是使用petalinux module 方式将ko编译文件系统了
5. zynq操作系统:Linux驱动开发AXIDMA补充篇 多路DMA
多路DMA的使用。