ZYNQ系列(十二)linux的DMA使用

ZYNQ系列(十二)linux的DMA使用


文章目录

  • ZYNQ系列(十二)linux的DMA使用
  • 前言
  • 开发环境
  • 准备工作
  • petalinux工程建立
    • 建立工程
    • 配置内核
      • 1. 配置DMA
      • 2. 配置CMA
    • 修改设备树
    • 生成BOOT.BIN烧录
  • 编译Github的DMA代码
  • 运行
  • 收工
    • 出错集锦


前言

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。

准备工作

  1. 下载dma的源码:https://github.com/bperez77/xilinx_axidma/tree/master
  2. 黑金的dma回环例程的*.HDF文件或者自己根据其他博客搭建dma硬件环境。

petalinux工程建立

建立工程

根据第一章的内容,建立petalinux工程,hdf文件需要选择准备工作中第二条产生的hdf(有需要的直接评论留言给我)。

配置内核

1. 配置DMA

我使用的是黑金自带的内核,我检查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的项都开启了,就没搭理。

2. 配置CMA

CMA是什么我也不知道,具体为什么配置CMA我也不知道,我只看到网上有人说没有配CMA,运行的时候会出错。
根据其他博主的文章现在知道了。

  1. 报错如下为CMA空间不够导致的,重新设置CMA大小可解决。
    ZYNQ系列(十二)linux的DMA使用_第1张图片

  2. 原因:因为transfer_file函数中对发送和接收都分配了cma空间,所以发送和接收的文件大小不能超过设置的cma空间的一半。

  3. 后面根据别人的博客出一个错误的说明。

  4. 配置方式
    ZYNQ系列(十二)linux的DMA使用_第2张图片

修改设备树

打开设备树文件

$  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>;
	};
};

生成BOOT.BIN烧录

不做过多解释。

编译Github的DMA代码

  1. 代码路径截图如下
    ZYNQ系列(十二)linux的DMA使用_第3张图片

  2. 修改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以后才产生的,路径可能和这个不一样,自己查找。

  3. 将include中的两个.h文件移动到driver目录中

  4. 编译驱动程序

     $ make driver
    
  5. 编译应用程序

     $ make examples
    
  6. 查看outputs文件夹,有以下文件说明安装成功。
    在这里插入图片描述

  7. 该步骤可以使用petalinux module那种方式将ko文件弄到文件系统,或者直接编译进内核,方法网上看吧。

运行

接下来就是挂载,运行了。

  1. 使用insmod加载模块,出现以下信息说明加载成功。
    在这里插入图片描述

  2. 运行axidma_benchmark,查看dma速率。
    ZYNQ系列(十二)linux的DMA使用_第4张图片

  3. 运行axidma_transfer,回环测试。

     创建两个txt文件,1.txt和2.txt。
     在1.txt文件中胡乱输入点内容。
     运行axidma_transfer,2.txt中会出现和1.txt一样的内容。
    

ZYNQ系列(十二)linux的DMA使用_第5张图片
ZYNQ系列(十二)linux的DMA使用_第6张图片

收工

笔者也是小白一枚,都是按照网上别人博客来的,下面将我看到几个不错的博客贴出来,供大家欣赏。

这几篇博客都是根据 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的使用。

出错集锦

你可能感兴趣的:(ZYNQ,#,PS,内核,linux,dma)