精华内容
下载资源
问答
  • Zynq中使用AXI_DMADMA向FIFO1中写入数据,PL从FIFO1中读出数据;PL向FIFO2中写入数据,当写入完成后,触发GPIO中断,控制DMA开启接收,从FIFO2中读出数据。写入数据、读出数据的开启均由VIO控制。开发环境:Vivado...
  • zynq-linux-dma Petalinux 下 Zynq 平台 (ZC706) 上的用户空间 DMA 示例。 环境要求 EDK 版本 带有相应 Petalinux SDK 的 Vivado 2014.4。 硬件平台 ZC706 评估板。 硬件架构 该项目实现了一个与 PL 和 PS DDR ...
  • ZYNQ系列(十二)linux的DMA使用 文章目录ZYNQ系列(十二)linux的DMA使用前言 前言

    ZYNQ系列(十二)linux的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大小可解决。
      在这里插入图片描述

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

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

    4. 配置方式
      在这里插入图片描述

    修改设备树

    打开设备树文件

    $  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. 代码路径截图如下
      在这里插入图片描述

    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速率。
      在这里插入图片描述

    3. 运行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的使用。

    出错集锦

    展开全文
  • Zynq中使用AXI_DMA,使用SG模式。DMA向FIFO1中写入数据,PL从FIFO1中读出数据;PL向FIFO2中写入数据,当写入完成后。开发环境:Vivado 2018.3 开发板:黑金AX7010。
  • 第八节,ZYNQDMA

    2019-04-16 21:03:31
    ZYNQDMA 1 DMA的特点和体系结构 DMA外设特点: DMA引擎拥有一个灵活的指令设置DMA的传输; 拥有8个cache线,每一个cache线宽度是4个字; 拥有8个可以并行的DMA通道线程; 拥有8个中断给中断控制器; 拥有8...

    ZYNQDMA

    1 DMA的特点和体系结构

    DMA外设特点:

    1. DMA引擎拥有一个灵活的指令设置DMA的传输;
    2. 拥有8个cache线,每一个cache线宽度是4个字;
    3. 拥有8个可以并行的DMA通道线程;
    4. 拥有8个中断给中断控制器;
    5. 拥有8个DMA触发事件并且可以编码控制;
    6. 128个(64bit)的MFIFO,在传输的时候读写端可写入到此FIFO;
    7. 支持任意内存到内存的传输;

    整个系统中的DMA控制器如图1所示,

    图1 DMA控制器系统图

    DMAC包含一个指令处理单元,其能够编码控制DMA传输,每一个线程包含一个独立的状态机处理各自的DMA事件,包括通道仲裁,通道优先级。

    当一个通道线程执行加载或者存储指令的时候控制器会将指令增加到读队列和写队列中,控制器使用这些队列来存储指令,并且按队列指令顺序在AXI总线上完成传输。

    DMA在AXI总线上传输:

    所有的DMA传输使用AXI接口移动数据,包括片上内存的移动,DDR内存,以及PL上的从外设内存。PL端的从外设正常连接到DMAC外设接口上控制其数据流。DMAC在PS端可以访问到IOPs,但是正常情况下不会使用,因为这些路径不会提供数据流信号。DMAC数据路径正常使用情况如图2所示,没一个AXI路径可以执行一个读或者一个写,其中拥有好多组合,典型的DMA传输有:1,内存到内存的传输(片上内存到DDR内存);2,内存到PL端外设或者PL端外设到内存(DDR内存到PL端外设)。

    图2 数据流传输

    DMA的管理:

    DMAC实时操作时,用户可以通过下面的指令设置DMA的传输:

    DMAGO:开始一个用户指定通道的DMA传输;DMASEV:用户指定的一个事件或者中断发生时给出信号;DMAKILL:终止一个线程。

    当DMA管理器接受到一个从APB从接口的指令后,会等待若干个时钟周期,在执行指令之前(pipeline是处于忙的状态,在执行其他指令)。

    多通道数据FIFO(MFIFO):

    MFIFO是一个当前所有活动通道共享的,基于先进入先服务的共享资源。对于编程角度来讲,它是以份额深度可变的并行的FIFO集合,每个通道都有一个FIFO,但是所有FIFO的总深度不能超多MFIFO的大小,DMAC的MFIFO深度最大为128个64bit的大小。

    事件和中断:

    DMAC支持16个事件,开始的8个事件是中断信号,IRQs[7:0],这8个中断都会输出到PS或者PL的中断控制器。这些事件使用内部的DMA引擎通道与通道之间的传输。EMAC的中断事件表如图3所示。

    图3 事件与中断

    2 DMA的配置实例

    配置DMA做内存到内存的传输实例。

    DMA配置步骤:

    1. 初始化dma的命令数据结构,主要配置传输源地址,目的地址,传输长度,burst的大小等信息;
    2. 通过DMA的ID信息,找到DMA外设信息;
    3. 初始化dma的数据结构;
    4. 连接到硬件中断,将GIC中断映射到中断向量表中;
    5. 通过GIC的ID信息,找到GIC外设信息;
    6. 链接DMA中断和GIC,将DMA中断映射到GIC控制器上;
    7. 时能GIC中断;
    8. 使能硬件中断;
    9. 设置中断服务函数的映射配置;
    10. 开始DMA的传输;
    11. 等待DMA的传输完成;

    程序源码:

        XDmaPs_Config *DmaConfigPtr;

        XScuGic_Config *GicConfigPtr;

        XDmaPs_Cmd DmaCmd;

        volatile int Checked = 0;

        int Index = 0;

        memset(&DmaCmd, 0, sizeof(XDmaPs_Cmd));

     

        DmaCmd.ChanCtrl.SrcBurstSize = 4;

        DmaCmd.ChanCtrl.SrcBurstLen = 4;

        DmaCmd.ChanCtrl.SrcInc = 1;

        DmaCmd.ChanCtrl.DstBurstSize = 4;

        DmaCmd.ChanCtrl.DstBurstLen = 4;

        DmaCmd.ChanCtrl.DstInc = 1;

        DmaCmd.BD.SrcAddr = (u32) Src;

        DmaCmd.BD.DstAddr = (u32) Dst;

        DmaCmd.BD.Length = DMA_LENGTH * sizeof(int);

     

        //find device

        DmaConfigPtr = XDmaPs_LookupConfig(XPAR_XDMAPS_1_DEVICE_ID);

        //config xdmaps data

        XDmaPs_CfgInitialize(&Dma,DmaConfigPtr,DmaConfigPtr->BaseAddress);

     

        //config gic

        //config hardware interrupt

        Xil_ExceptionInit();

    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)&Gic);

     

        //find device

        GicConfigPtr = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);

        //config gic data

        XScuGic_CfgInitialize(&Gic,GicConfigPtr,GicConfigPtr->CpuBaseAddress);

     

        //connect gic handler

    XScuGic_Connect(&Gic,XPAR_XDMAPS_0_FAULT_INTR,(Xil_InterruptHandler)XDmaPs_FaultISR, (void *)&Dma);

    XScuGic_Connect(&Gic,DMA_DONE_INTR_0,(Xil_InterruptHandler)XDmaPs_DoneISR_0, (void *)&Dma);

     

        //enable gic

        XScuGic_Enable(&Gic,XPAR_XDMAPS_0_FAULT_INTR);

        XScuGic_Enable(&Gic,DMA_DONE_INTR_0);

     

        //enable hardware interrupt

        Xil_ExceptionEnable();

     

     

        //handler connect

        XDmaPs_SetDoneHandler(&Dma,0,(XDmaPsDoneHandler)DmaDoneHandler,(void *)&Checked);

        /* Initialize source */

        for (Index = 0; Index < DMA_LENGTH; Index++)

            Src[Index] = DMA_LENGTH - Index;

     

        /* Clear destination */

        for (Index = 0; Index < DMA_LENGTH; Index++)

            Dst[Index] = 0;

     

        //start dma tran

        XDmaPs_Start(&Dma, 0,&DmaCmd,0);

     

        //wait tran over

        while(1)

        {

            if(Checked == 1)

            {

                print("tran over!");

                break;

            }

     

      }

     

    展开全文
  • ZYNQ DMA

    2021-06-08 21:10:34
    ZYNQDMA LOOPBACK实验1 原理介绍1.1 AXI4(AXI-full)总线学习1.2 zynq ps端几种接口1.3 AXI DMA简介1.4 AXI DMA编程顺序1.5 实验目的2 实验步骤2.1 建立BD文件2.2 SDK文件3 结果分析3.1 实验条件3.2 实验结果3.3 ...

    1 AXI4(AXI-full)总线学习

    1.1 什么是AXI

      AXI(Advanced eXtensible Interface)是一种总协议,该协议的第一个版本AXI3是ARM公司提出的AMBA(Advanced Microcontroller Bus Architecture)3.0协议中最重要的部分。2010发布的AMBA4.0包含了AXI的第二个版本AXI4。
      AXI4包含3种类型的接口(或者说ZYNQ的三种AXI总线)。
      (1)AXI4:(For high-performance memory-mapped requirements.)主要面向高性能地址映射通信的需求,是面向地址映射的接口,允许最大256轮的数据突发传输。
      (2)AXI4-Lite:(For simple, low-throughput memory-mapped communication )是一个轻量级的地址映射单次传输接口,占用很少的逻辑单元。适用于吞吐量较小的地址映射通信总线。
      (3)AXI4-Stream:(For high-speed streaming data.)面向高速流数据传输;去掉了地址项,允许无限制的数据突发传输规模。
    现在说明AXI的优势。
      (1)高效:通过标准化的AXI接口,开发者只需要学习一种IP核的通讯协议即可;
      (2)易用:针对具体应用提供合适的接口协议。AXI4:面向地址映射的接口,在单地址传输的情况下最大允许256个时钟周期的数据突发长度;AXI4-Lite:一个轻量级的地址映射单次传输接口,占用较少的资源;AXI4-Stream:去掉了地址传输的功能,允许无限制的数据突发传输,无需考虑地址映射。
      (3)易得:标准化的AXI接口协议资源,不仅可以在xilinx官网上获得,也可以在全球范围内ARM的所有合作伙伴处获得。大量的IP core支持AXI4协议;大量的第三方AXI工具可提供多样的系统开发、验证和功能定制。

    1.2 AXI管脚说明

    AXI4总线和AXI4-Lite总线具有相同的组成部分:
      (1)读地址通道:包含ARVALID,ARADDR,ARREADY信号;
      (2)读数据通道:包含RVALID, RDATA,RREADY,RRESP信号;
      (3)写地址通道:包含AWVALID,AWADDR,AWREADY信号;
      (4)写数据通道:包含WVALID,WDATA,WSTRB, WREADY信号;
      (5)写应答通道:包含BVALID,BRESP,BREADY信号;
      (6)系统通道:包含:ACLK,ARESETN信号。
    而AXI4-Stream总线的组成有:
      (1)ACLK信号:总线时钟,上升沿有效;
      (2)ARESETN信号:总线复位,低电平有效;
      (3)TVALID信号:主机告诉从机数据本次传输有效;
      (4)TREADY信号:从机告诉主机做好传输准备;
      (5) TLAST信号:主机告诉从机该次传输为突发传输的结尾;当TLAST为高时,表明当前数据是最后一次传输。
      (6)TDATA信号:数据,可选宽度32、64、128、256bit;
      (7)TKEEP信号:类似于字节修饰符,表示数据是有效的。
      (8) TSTRB信号:每bit对应TDATA的一个有效字节,宽度为TDATA/8;
      (9)TUSER信号:用户定义信号,宽度为128bit。

    下面对这些信号的管脚做一个具体的说明。
    在这里插入图片描述
    在这里插入图片描述

    1.3 zynq的三种AXI总线

    AXI DMA 用到了三种总线:
    (1)AXI4-Lite :AXI4-Lite用于对寄存器进行配置和获取状态,从而控制DMA的数据传输。选用AXI-lite(AXI4-Lite )接口的原因为AXI-lite(AXI4-Lite )接口一般用于简单的低吞吐量内存映射通信(例如,与控制和状态寄存器之间的通信)。一般用于低速少量的数据通信,比如传递参数。
    (2)AXI4 Memory Map:AXI4 Memory Map 用于与存储器交互的接口。又分为 AXI4 Memory Map Read 和 AXI4 Memory Map Write 两个接口,一个是读一个是写。
    (3)AXI4 Stream 接口:AXI4 Stream 接口用于对外设的读写。其中 AXI4 Stream Master(MM2S,Memory Map to Stream,存储器映射AXI4-full(AXI4)到AXI4-stream)用于对外设写,AXI4-Stream Slave(S2MM,Stream to Memory Map,,AXI4-stream到存储器映射AXI4-full(AXI4))用于对外设读。
    总之,在以后的使用中需要知道 AXI_MM2S 和AXI_S2MM 是存储器端映射的 AXI4 总线,提供对存储器(DDR3)的访问。AXIS_MM2S 和 AXIS_S2MM是 AXI4-streaming (外设)总线,可以发送和接收连续的数据流,无需地址。

    1.4 AXI的三种接口

    总共有9个接口:
      (1)AXI-GP接口(4个):是通用的AXI接口,包括两个32位主设备接口和两个32位从设备接口,用该接口可以访问PS中的片内外设。这个对应AXI-lite总线使用。这接口我用的挺多,传输速度也不快,用于PS与PL少慢数据通信。
      (2)AXI-HP接口(4个):是高性能/带宽的标准的接口,PL模块作为主设备连接。主要用于PL访问PS上的存储器(DDR和On-Chip RAM)。PS都作为从设备,对应AXI-full总线。因为这个高速传输接口,PS太慢了,要顺着PL走。
      (3)AXI-ACP接口(1个):是ARM多核架构下定义的一种接口,中文翻译为加速器一致性端口,用来管理DMA之类的不带缓存的AXI外设,PS端是Slave接口。这个直接通往ARM内部,不经过DDR,所以速度是最快的。

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

    1.5 AXI协议

      协议的制定是要建立在总线构成之上的。因此说AXI4,AXI4-Lite,AXI4-Stream都是AXI4协议。AXI总线协议的两端可以分为分为主(master)、从(slave)两端,他们之间一般需要通过一个AXI Interconnect相连接。作用是提供将一个或多个AXI主设备连接到一个或多个AXI从设备的一种交换机制。
      AXI Interconnect的主要作用是:当存在多个主机以及从机器时,AXI Interconnect负责将它们联系并管理起来。由于AXI支持乱序发送,乱序发送需要主机的ID信号支撑,而不同的主机发送的ID可能相同,而AXI Interconnect解决了这一问题,他会对不同主机的ID信号进行处理让ID变得唯一。
      AXI协议将读地址通道,读数据通道,写地址通道,写数据通道,写响应通道分开,各自通道都有自己的握手协议。每个通道互不干扰却又彼此依赖。这是AXI高效的原因之一。
    AXI握手协议:
      AXI4 所采用的是一种 READY,VALID 握手通信机制。简单来说主从双方进行数据通信前,有一个握手的过程。传输源产生 VLAID 信号来指明何时数据或控制信息有效。而目地源产生READY信号来指明已经准备好接受数据或控制信息。传输发生在VALID和 READY信号同时为高的时候,如下图所示。
    在这里插入图片描述

    2 AXI DMA简介

    2.1 基本介绍

    ZYNQ 提供了两种 DMA,一种是集成在 PS 中的硬核 DMA,另一种是 PL 中使用的软核AXI DMA。下面我们简单的介绍下 PL 的 DMA,即 AXI DMA IP 核。
    AXI DMA:AXI Direct Memory Access,直接内存访问。是指外部设备不通过CPU,直接与系统内存交换数据的接口技术。官方解释为内存与AXI4-Stream外设之间提供高带宽的直接存储访问,其可选的scatter/gather功能可将CPU从数据搬移任务中解放出来。在ZYNQ中,AXI DMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。
    要将外设数据读入内存或将内存传送到外设,一般都要通过 CPU 控制完成,如采用查询或中断方式。虽然中断方式可以提高CPU的利用率,但是也会有效率问题。对于批量传送数据的情况,采用 DMA 方式,可解决效率与速度问题,CPU只需要提供地址和长度给DMA,DMA 即可接管总线,访问内存。等 DMA 完成工作后,告知CPU,交出总线控制权。也就是说,使用DMA时,CPU 向 DMA 控制器发出一个存储传输请求,这样当DMA 控制器在传输的时候,CPU 执行其它操作。传输操作完成时,DMA 以中断的方式通知 CPU。
    为了发起传输事务,DMA 控制器必须得到以下数据:
    • 源地址 — 数据被读出的地址
    • 目的地址 — 数据被写入的地址
    • 传输长度 — 应被传输的字节数
    DMA 存储传输的过程如下:

    1. 为了配置用 DMA 传输数据到存储器,处理器发出一条 DMA 命令
    2. DMA 控制器把数据从外设传输到存储器,而让 CPU 腾出手来做其它操作。
    3. 数据传输完成后,向 CPU 发出一个中断来通知它,DMA传输可以关闭了。
      在 ARM CPU 设计的过程中,已经考虑到了大量数据搬移的情况,因此在 CPU 中自带了一个 DMA 控制器 DMAC,这个 DMAC 驻留在 PS 内,而且必须通过驻留在内存中的 DMA 指令编程,这些程序往往由CPU 准备,因此需要部分的 CPU 参与。DMAC与PL的连接是通过 AXI_GP 接口,这个接口最高支持到 32 位宽度,这也限制了这种模式下的传输速率,理论最高速率为 600MB/s。这种模式不占用 PL 资源,但需要对 DMA 指令编程,会增加软件的复杂性。
      通过 PL 的 DMA 和 AXI_HP 接口的传输适用于大块数据的高性能传输,带宽高。该种传输方式的拓扑图如下(灰色填充的框图或红色边框圈出的框图):
      在这里插入图片描述
      可以看到,DMA 的数据传输经S_AXI_HP 接口(以下简称 HP 接口)。ZYNQ 拥有 4 个 HP 接口,提供了 ZYNQ 内最大的总带宽。每一个 HP 接口都包含控制和数据 FIFO。这些 FIFO 为大数据量突发传输提供缓冲,让 HP 接口成为理想的高速数据传输接口。对 DMA 的控制或配置通过 M_AXI_GP 接口,传输状态通过中断传达到 PS 的中断控制器。下面我们简单的介绍下 PL 的 DMA,即 AXI DMA IP 核。
      AXI Direct Memory Access(AXI DMA)IP 内核在 AXI4 内存映射和 AXI4-Stream IP 接口之间提供高带宽直接储存访问。其可选的 scatter gather 功能还可以从基于处理器的系统中的中央处理单元(CPU)卸载数据移动任务。初始化、状态和管理寄存器通过 AXI4-Lite 从接口访问。核心的功能组成如下图所示:
      在这里插入图片描述
      在ZYNQ中,AX IDMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。AXI DMA IP有6个接口,S_AXI_LITE是ARM配置DMA寄存器的接口,M_AXI_SG是从存储器加载buffer descriptor的接口,剩下4个接口构成两对,S2MM和MM2S表示数据的方向,AXI是存储器一侧的接口,AXIS是FPGA一侧的接口。AXI DMA IP和ARM自带的DMA是很像的,只不过不具备从存储器到存储器的功能,当然也可以将S2MM和MM2S接口与AXIS接口直接相连,其结构如下图所示。
      在这里插入图片描述

    2.2 Direct Register Mode(简单DMA模式)

    Direct Register DMA 模式也就是 Simple DMA。Direct Register 模式提供了一种配置,用于在 MM2S 和S2MM 通道上执行简单的 DMA 传输,这需要更少的 FPGA 资源。Simple DMA 允许应用程序在 DMA 和Device 之间定义单个事务。它有两个通道:一个从 DMA 到 Device,另一个从 Device 到 DMA。应用程序必须设置缓冲区地址和长度字段以启动相应通道中的传输。
    通过访问DMACR(DMA控制寄存器)、源地址或者目的地址和长度寄存器发起DMA传输。当传输完成后,如果使能了产生中断输出,那么DMASR(状态寄存器)相关联的通道位会有效,即产生中断输出。
    Direct Register Mode具备DMA的基本功能,除了控制寄存器和状态寄存器之外,给出目的地址和传输长度之后就可以开启一次传输了。但是,Direct Register Mode模式配置完一次寄存器之后只能完成存储器连续地址空间的读写,如果有需求往不同空间搬运数据的话,那就需要重新配置寄存器开启一次新的传输。
    简单DMA模式的编程顺序如下:
    S2MM 通道的 DMA 传输的设置和启动顺序
    (1)开启/使能S2MM通道。
    (2)如果需要的话,可以使能中断(一般都会使能中断)。
    (3)写一个有效的源地址到S2MM_SA寄存器(源地址寄存器)。即指定数据从哪里开始搬运。
    如果没有使能DRE功能,在指定起始地址时,需要注意字节地址对齐。哪些地址是对齐或者不对齐的,取决于Stream流的数据位宽。For example, if Memory Map Data Width = 32, data is aligned if it is located at word offsets (32-bit offset), that is 0x0, 0x4, 0x8, 0xC, and so forth. If DRE is enabled and Streaming Data Width < 128, then the Source Addresses can be of any byte offset.
    (4)写待传输的字节数到S2MM_LENGTH寄存器。需要注意的是,一个长度为0的值是无效的。而一个非零的值,将决定存储器映射到Stream流的数据个数。除此之外,S2MM_LENGTH寄存器需要最后配置,而其他寄存器的配置顺序没有要求。

    3 DMA LOOPBACK实验

    3.1 实验目的

    使用PL的AXI DMA IP核从DDR3中读取数据,并将数据写回到DDR3中。在实际应用中,DMA一般与产生数据或需求数据的 IP 核相连接,该 IP 核可以是带有 Stream 接口的高速的 AD(模拟转数字)或 DA(数字转模拟) IP 核。不失一般性,在本次实验中,我们使用 AXI4 StreamData FIFO IP 核来充当这类 IP 进行 DMA 环回实验。大致的系统框图如下,具体的见图6.1所示的PL 的 DMA 和 AXI_HP 接口拓扑图。
    原理框图
    在这里插入图片描述

    3.2 实验步骤

    在这里插入图片描述
    Address Width (32 - 64):
    指定地址空间的宽度,可以是 32 到 64 之间的任何值。此处保持默认值 32。
    Memory Map Data Width:
    AXI MM2S 存储映射读取数据总线的数据位宽。有效值为 32,64,128,256,512和 1024。此处保持默认值 32。
    Stream Data Width:
    AXI MM2S AXI4-Stream 数据总线的数据位宽。该值必须小于或等于 Memory MapData Width。有效值为 8、16、32、64、128、512 和 1024。此处保持默认值 32。

    3.2.1 建立BD文件

      具体BD文件的建立参考黑金SDK开发或者正点原子的SDK开发教材=程。
    在这里插入图片描述

    3.2.2 SDK工程文件

    /*
     * 本章的实验任务是在领航者 ZYNQ 开发板上使用 PL 的 AXI DMA IP 核从 DDR3 中读取数据,并将数据写回到 DDR3 中
     *
     *   TX_BUFFER_BASE,RX_BUFFER_BASE;
     *   0x01200000,0x01400000;
     */
    
    #include "xaxidma.h"
    #include "xparameters.h"
    #include "xil_exception.h"
    #include "xscugic.h"
    
    /************************** Constant Definitions *****************************/
    // DMA的器件ID
    #define DMA_DEV_ID          XPAR_AXIDMA_0_DEVICE_ID			 //位于PL侧的DMA,xparameters.h
    //定义设备号
    #define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID	// 器件中断ID
    //定义DDR的基地址
    #define DDR_BASE_ADDR       XPAR_PS7_DDR_0_S_AXI_BASEADDR   // 0x00100000  DDR的基地址
    //定义内存的地址
    #define MEM_BASE_ADDR       (DDR_BASE_ADDR + 0x01000000)    // 0x01100000
    //定义发送缓冲区的基地址
    #define TX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00100000)    // DMA读取数据的起始地址0x01200000
    //定义接收缓冲区的基地址
    #define RX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00300000)    // 写到 DDR3中的起始地址0x01400000
    
    // DMA接收与发送通道的中断ID
    #define RX_INTR_ID          XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR//接收中断ID
    #define TX_INTR_ID          XPAR_FABRIC_AXI_DMA_0_MM2S_INTROUT_INTR//发送中断ID
    
    //定义一个复位时间计数器
    #define RESET_TIMEOUT_COUNTER   10000    // 复位时间
    //定义一个测试的起始值
    #define TEST_START_VALUE        0x0      // 测试起始值
    //定义测试长度
    #define MAX_PKT_LEN             0x100    // DMA传输的数据包的长度(256)
    
    
    /************************** 函数声明 ******************************/
    //数据核验函数
    static int check_data(int length, u8 start_value);//检查写入到 DDR3 中的数据是否正确
    //发送中断的处理函数
    static void tx_intr_handler(void *callback);//DMA TX中断处理函数
    //接收中断的处理函数
    static void rx_intr_handler(void *callback);//DMA RX中断处理函数
    //建立中断系统
    static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
            u16 tx_intr_id, u16 rx_intr_id);	//建立 DMA中断系统函数
    //禁用中断函数
    static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
            u16 rx_intr_id);					//禁用DMA引擎的中断函数
    
    
    
    /************************** Variable Definitions *****************************/
    static XAxiDma axidma;     //XAxiDma实例
    static XScuGic intc;       //中断控制器的实例
    volatile int tx_done;      //发送完成标志
    volatile int rx_done;      //接收完成标志
    volatile int error;        //传输出错标志
    
    
    int main(void)
    {
        int i;
        int status;//是否完成标志位
        u8 value;
    
    
        /* 等价于
        u8 tx_buffer_ptr[MAX_PKT_LEN];      //发送缓冲区指针,指针指向的数据为8bit无符号数
        u8 rx_buffer_ptr[MAX_PKT_LEN];      //接受缓冲区指针,指针指向的数据为8bit无符号数
        */
        u8 *tx_buffer_ptr;//发送缓冲区指针,指针指向的数据为8bit无符号数
        u8 *rx_buffer_ptr;//接受缓冲区指针,指针指向的数据为8bit无符号数
    
    
        /*
         XAxiDma_Config是一个AXI_DMA配置的信息结构体,它里面包含需要配置的各种信息,
         	 类似于一个空表,表里面有各种需要填的事项,
         	 填表的方式是将AXI_DMA的设备号作为传入参数传递到XAxiDma_LookupConfig查找函数中,
         	 如果传输的设备号和函数内部的设备号一样的话,就将根据PL侧的设计参数传递给查找表
        */
        XAxiDma_Config *config;
    
    
        /*
               从C语言的角度看指针.TX_BUFFER_BASE与RX_BUFFER_BASE都是一个地址,在地址前面加上(u8 *)修饰符,
         	 这样理解:
          	  a=8'b1;
          	  u8* p;
          	  *p=(u8*)(&a);
          	  p=就是a的地址
    
          	  此处解释:
           	   (u8 *) TX_BUFFER_BASE;
           	   	   将TX_BUFFER_BASE转换成指向8位无符号数指针的内容, 然后这个地址传递给tx_buffer_ptr
           	   	   (u8*)的作用是指针该地址指向的数据为8bit无符号数,不可以多操作或者少操作
        */
        tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
        rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;
    
    
        xil_printf("\r\n--- Entering main() --- \r\n");
    
    
        /*
         	 进行DMA配置参数传递
         	 通过调用DMA查找配置函数,传入设备ID,获取设备参数
         	 需要注意的是,其中的参数是根据PL端的IP core的配置选项生成的参数
        */
        //Look up the hardware configuration for a device instance
        config = XAxiDma_LookupConfig(DMA_DEV_ID);
        if (!config) {
            xil_printf("No config found for %d\r\n", DMA_DEV_ID);
            return XST_FAILURE;
        }
    
    
        /*
        //初始化DMA引擎
         	 根据PL端对DMA core的配置参数,PS对DMA进行真正的配置初始化过程,
         axidma还存储在PS端的AXI——DMA配置表,根据对PL参数的读取,
         PS运行对PL侧的DMA配置,这个配置过程是通过GP0接口对AXI_Lite4总线的控制完成的
        */
        status = XAxiDma_CfgInitialize(&axidma, config);
        if (status != XST_SUCCESS) {
            xil_printf("Initialization failed %d\r\n", status);
            return XST_FAILURE;
        }
    
    
        /*
         	 我们配置的是使用PL侧DMA的直接寄存器访问模式,所以数据传递也是通过该方式运行的,
         	 为了以防万一,在这里运行一下SG查询函数看看是不是配置成了SG模式
        */
        if (XAxiDma_HasSg(&axidma)) {//判断是不是工作在SG模式
            xil_printf("Device configured as SG mode \r\n");
            return XST_FAILURE;
        }
    
    
    
        /*
                   建立中断系统,详见函数定义
         CallBackRef is the callback reference, usually the instance pointer of the connecting driver.
         CallBackRef是回调引用,通常是连接驱动程序的实例指针。
         axidma是这些传入参数里面最没用的东西,不过还是保留的
                  对于中断系统,分为PPI(私有外设中断)、SGI(软件生成中断)、SPI(共享外设中断)
                  我们在中断系统中根据AXI_DMA的接收发送中断号注册两种中断
         axidma作用不大,起码没有直接感觉出来,解释中只是说axidma是回调引用,通常连接到驱动程序的实例指针,所以前面有一个取地址&
        */
        status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
        if (status != XST_SUCCESS) {
            xil_printf("Failed intr setup\r\n");
            return XST_FAILURE;
        }
    
        //初始化标志信号
        tx_done = 0;
        rx_done = 0;
        error   = 0;
    
        //对要写入的数据赋值
        value = TEST_START_VALUE;
        for (i = 0; i < MAX_PKT_LEN; i++) { // 向 DDR3 的指定地址写入数据,写入的第一个地址为TX_BUFFER_BASE
            tx_buffer_ptr[i] = value;		// 即 0x01200000,数据值为 TEST_START_VALUE 即 0x0
            value = (value + 1) & 0xFF;     // 写入的地址长度为MAX_PKT_LEN,即 0x100(256)
        }
    
    
        //将要写入fifo的数据刷入Cache
        Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, MAX_PKT_LEN); //刷新 Data Cache,以防 Data Cache缓存数据
    
    
        //开启 DMA传输
        status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr,
        		MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);//DDR到外设
        if (status != XST_SUCCESS) {
            return XST_FAILURE;
        }
    
    
        //开始接收
        status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr,
        		MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);//外设到DDR
        if (status != XST_SUCCESS) {
            return XST_FAILURE;
        }
    
    
        //传输结束.为接收数据刷新cache
        //再次刷新 Data Cache,由于 DDR3中的数据已经更新,但 Cache 中的数据并没有更新,CPU 如果想从 DDR3 中读取数据需要刷新 Data Cache
        Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, MAX_PKT_LEN);
        while (!tx_done && !rx_done && !error)
            ;
    
    
        //传输出错
        if (error) {
            xil_printf("Failed test transmit%s done, "
                    "receive%s done\r\n", tx_done ? "" : " not",
                    rx_done ? "" : " not");
            goto Done;
        }
    
    
        //传输完成,检查数据是否正确
        status = check_data(MAX_PKT_LEN, TEST_START_VALUE);
        if (status != XST_SUCCESS) {
            xil_printf("Data check failed\r\n");
            goto Done;
        }
    
        xil_printf("Successfully ran AXI DMA Loop\r\n");
    
        //取消 DMA 中断
        disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);//取消 DMA 中断
    
    
        Done: xil_printf("--- Exiting main() --- \r\n");
    
    
        return XST_SUCCESS;
    }
    
    
    //检查数据缓冲区
    //检查写入到 DDR3 中的数据是否正确
    static int check_data(int length, u8 start_value)
    {
        u8 value;
        u8 *rx_packet;
        int i = 0;
    
        value = start_value;
        rx_packet = (u8 *) RX_BUFFER_BASE;
        for (i = 0; i < length; i++) {
            if (rx_packet[i] != value) {
                xil_printf("Data error %d: %x/%x\r\n", i, rx_packet[i], value);
                return XST_FAILURE;
            }
            value = (value + 1) & 0xFF;
        }
    
        return XST_SUCCESS;
    }
    
    
    
    
    //DMA TX中断处理函数   ,用于处理 DMA 发送中断
    /*当发送中断时间发生时*/
    static void tx_intr_handler(void *callback)
    {
        int timeout;
        u32 irq_status;
        XAxiDma *axidma_inst = (XAxiDma *) callback;
    
        //读取待处理的中断
        irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
        //确认待处理的中断
        XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);
    
    
        //Tx出错时候,复位驱动实例,即axidma_inst
        if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
            error = 1;
            XAxiDma_Reset(axidma_inst);       // 如果发现是接收出现错误的中断,则使用 XAxiDma_Reset 函数复位 DMA,
            timeout = RESET_TIMEOUT_COUNTER;  // 并使用 XAxiDma_ResetIsDone 函数判断是否复位完成
            while (timeout) {
                if (XAxiDma_ResetIsDone(axidma_inst))
                    break;
                timeout -= 1;
            }
            return;
        }
    
        // Tx完成      如果是发送完成的中断,则置位发送完成标志 tx_done
        if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
            tx_done = 1;
    }
    
    
    
    //DMA RX中断处理函数   与TX中断处理函数类似
    //DMA RX中断处理函数
    static void rx_intr_handler(void *callback)
    {
        u32 irq_status;
        int timeout;
        XAxiDma *axidma_inst = (XAxiDma *) callback;
    
        irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
        XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);
    
        //Rx出错
        if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
            error = 1;
            XAxiDma_Reset(axidma_inst);
            timeout = RESET_TIMEOUT_COUNTER;
            while (timeout) {
                if (XAxiDma_ResetIsDone(axidma_inst))
                    break;
                timeout -= 1;
            }
            return;
        }
    
        //Rx完成
        if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
            rx_done = 1;
    }
    
    //建立 DMA 中断系统函数 setup_intr_system
    //  @param   int_ins_ptr是指向XScuGic实例的指针
    //  @param   AxiDmaPtr是指向DMA引擎实例的指针
    //  @param   tx_intr_id是TX通道中断ID
    //  @param   rx_intr_id是RX通道中断ID
    //  @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
    static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
            u16 tx_intr_id, u16 rx_intr_id)
    {
        int status;
        XScuGic_Config *intc_config;
    
        //1、初始化中断控制器驱动
        intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
        if (NULL == intc_config) {
            return XST_FAILURE;
        }
        status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
                intc_config->CpuBaseAddress);
        if (status != XST_SUCCESS) {
            return XST_FAILURE;
        }
    
        //2、设置DMA的优先级和触发类型
        XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
        XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);
    
        //为中断设置中断处理函数
        status = XScuGic_Connect(int_ins_ptr, tx_intr_id,	//中断设置中断处理函数
                (Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
        if (status != XST_SUCCESS) {
            return status;
        }
    
        status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
                (Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
        if (status != XST_SUCCESS) {
            return status;
        }
    
        XScuGic_Enable(int_ins_ptr, tx_intr_id);//用于使能 DMA 发送中断和 DMA 接收中断源
        XScuGic_Enable(int_ins_ptr, rx_intr_id);
    
        //3/启用来自硬件的中断
        Xil_ExceptionInit();
        Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                (Xil_ExceptionHandler) XScuGic_InterruptHandler,
                (void *) int_ins_ptr);
        Xil_ExceptionEnable();
    
        //使能DMA中断
        XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
        XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
    
        return XST_SUCCESS;
    }
    
    
    
    //此函数禁用DMA引擎的中断
    static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
            u16 rx_intr_id)
    {
        XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
        XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
    }
    
    
    

    4 结果分析

    4.1 实验条件

       定义的读取数据的起始地址:0x01200000和写入到 DDR3 中的起始地址:0x01400000

    #define DDR_BASE_ADDR       XPAR_PS7_DDR_0_S_AXI_BASEADDR   //0x00100000  DDR的基地址
    #define MEM_BASE_ADDR       (DDR_BASE_ADDR + 0x01000000)    //0x01100000
    #define TX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00100000)    //DMA 读取数据的起始地址0x01200000
    #define RX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00300000)    //写入到 DDR3 中的起始地址0x01400000
    

       发送数据的初始值和DMA传输的数据包长度

    #define TEST_START_VALUE        0x0      // 测试起始值
    #define MAX_PKT_LEN             0x100    // DMA传输的数据包的长度(256)
    

    4.2 实验结果

    起始地址:
    发送端
    在这里插入图片描述
    接收端:
    在这里插入图片描述
    终止地址:
    发送端:
    在这里插入图片描述
    接收端:
    在这里插入图片描述

    4.3 程序验证

    一定要按照这个步骤一步一步来,否则ILa调试界面会出现no stream

    4.3.1 选择 Debug Configurations,采用 Debug 模式,点击 Debug

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

    4.3.2 打开 ILA,设置触发条件 axi_dma_0_s2mm_introut 上升沿,点击运行

    在这里插入图片描述

    4.3.3 回到 SDK 的 Debug 界面,不用设置断点,点击 Resume

    在这里插入图片描述

    4.3.4 此时可以看到 ILA 已经触发,可以观察采集到的数据。

    整体图如下:
    在这里插入图片描述
    将仿真结果放大后
    在这里插入图片描述

       从上图我们可以看到,只有当TVAILD和TREADY信号同时为高电平时,此时握手成功,才传输数据。否则,则保持当前数据。传输的第一个数据为03020100。这和4.2所示的实验结果也是对得上的。TKEEP信号:类似于字节修饰符,表示数据是有效的。图中,TKEEP信号为15,转化为二进制为1111,说明一次传输的四个字节都有效。比如,03020100一共四个字节,说明这四个字节传输都有效。
       当TLAST为高时,表明当前数据是最后一次传输。如下图所示。
    在这里插入图片描述

    4.3.5 在串口调试工具中可以看到打印信息,中断了两次,并且测试成功

    在这里插入图片描述

    4.4 实验结论

       完成在DDR端产生数据,读出DDR数据,通过DMA传输到外设fifo,并由外设通过DMA写到DDR中。

    5 本实验的注意点

       1、在程序验证部分,一定要按照步骤一步一步来,否则ILa调试界面会出现no stream。

    展开全文
  • zynq PS侧DMA驱动

    2019-08-30 20:28:01
    linux建议使用DMA API而不是特定总线的DMA API,比如使用dma_map_*()接口而不是pci_map_*()接口,在zynq7000中,pl330 DMA已经实现了直接调用通用的DMA框架API即可以。 DMA开发相关API   首先得包含 ...

    linux中,驱动必然会有驱动对应的设备类型。在linux4.4版本中,其设备是以设备树的形式展现的。

    PS端设备树的devicetree表示如下

     

    324         dmac_s: dmac@f8003000 {  
    325             compatible = "arm,pl330", "arm,primecell";  
    326             reg = <0xf8003000 0x1000>;  
    327             interrupt-parent = <&intc>;  
    328             interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3",  
    329                 "dma4", "dma5", "dma6", "dma7";  
    330             interrupts = <0 13 4>,  
    331                          <0 14 4>, <0 15 4>,  
    332                          <0 16 4>, <0 17 4>,  
    333                          <0 40 4>, <0 41 4>,  
    334                          <0 42 4>, <0 43 4>;  
    335             #dma-cells = <1>;  
    336             #dma-channels = <8>;  
    337             #dma-requests = <4>;  
    338             clocks = <&clkc 27>;  
    339             clock-names = "apb_pclk";  
    340         };  


    < drivers/of/platform.c>这个文件根据设备树信息创建设备信息,在驱动程序注册时就可以找到该设备信息,执行probe函数。

     

    zynq下dma的设备channel如下:

     

     ​root@linaro-ubuntu-desktop:/sys/class/dma# ls
    dma0chan0  dma0chan2  dma0chan4  dma0chan6  dma1chan0
    dma0chan1  dma0chan3  dma0chan5  dma0chan7
    root@linaro-ubuntu-desktop:/sys/class/dma# ll
    total 0
    drwxr-xr-x  2 root root 0 1970-01-01 00:00 ./
    drwxr-xr-x 50 root root 0 1970-01-01 00:00 ../
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan0 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan0/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan1 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan1/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan2 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan2/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan3 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan3/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan4 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan4/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan5 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan5/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan6 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan6/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma0chan7 -> ../../devices/soc0/amba/f8003000.dmac/dma/dma0chan7/
    lrwxrwxrwx  1 root root 0 1970-01-01 00:00 dma1chan0 -> ../../devices/soc0/fpga-axi@0/43000000.axivdma/dma/dma1chan0/


    dma1chan0是xilinx AXI-VDMA IP生成的DMA控制器,其处于PL端,而dma0相关的控制器是ps端的pl330.本篇看pl330这个驱动程序的注册。

     

    查看物理地址分布

     

     ​root@linaro-ubuntu-desktop:~# cat /proc/iomem 
    00000000-1fffffff : System RAM
      00008000-006651a3 : Kernel code
      006a2000-00700867 : Kernel data
    41600000-4160ffff : /fpga-axi@0/i2c@41600000
    43000000-43000fff : /fpga-axi@0/axivdma@43000000
    70e00000-70e0ffff : /fpga-axi@0/axi_hdmi@70e00000
    75c00000-75c00fff : /fpga-axi@0/axi-spdif-tx@0x75c00000
    77600000-77600fff : /fpga-axi@0/axi-i2s@0x77600000
    79000000-7900ffff : /fpga-axi@0/axi-clkgen@79000000
    e0001000-e0001fff : xuartps
    e0002000-e0002fff : /amba/usb@e0002000
      e0002000-e0002fff : /amba/usb@e0002000
    e000a000-e000afff : /amba/gpio@e000a000
    e000b000-e000bfff : /amba/eth@e000b000
    e000d000-e000dfff : /amba/spi@e000d000
    e0100000-e0100fff : mmc0
    f8003000-f8003fff : /amba/dmac@f8003000
      f8003000-f8003fff : /amba/dmac@f8003000
    f8005000-f8005fff : /amba/watchdog@f8005000
    f8007000-f80070ff : /amba/devcfg@f8007000
    f8007100-f800711f : /amba/adc@f8007100
    f800c000-f800cfff : /amba/ocmc@f800c000
    fffc0000-ffffffff : f800c000.ocmc
    root@linaro-ubuntu-desktop:~# 

     

     

    ARM采用统一编址,其访问内存和外设的指令是一样没有差异的,linux内核并不通过物理地址直接访问外设,而是通过虚拟地址,虚拟地址经过MMU转换成物理访问外设。所以外设需要使用ioremap()对将物理地址空间转换成虚拟地址。

    I/O设备使用第三种地址,总线地址。如果一个设备在MMIO(memory mapped IO内存映射地址空间)有寄存器,或者其对系统存储系统执行DMA读写操作,设备使用的就是总线地址。

    在系统枚举阶段,内核知道I/O设备以及他们的MMIO空间。如果一个设备支持DMA方式,驱动通过kmalloc()申请一段内存空间,返回申请空间的首地址,设为X,虚拟地址系统将虚拟地址X映射到物理地址Y。驱动程序可以使用虚拟地址X访问设备地址空间,但是设备本身却不行,这是因为DMA本身并不是通过虚拟地址方式来工作的。

    在zynq7000设备里,DMA可以直接操作物理地址,但另一些处理器使用IOMMU将DMA地址转换物理地址,

     

    linux建议使用DMA API而不是特定总线的DMA API,比如使用dma_map_*()接口而不是pci_map_*()接口,在zynq7000中,pl330 DMA已经实现了直接调用通用的DMA框架API即可以。

    DMA开发相关API

     

    首先得包含

     ​#include <linux/dma-mapping.h>

    其提供了dma_addr_t定义,其可以作为设备使用DMA的源地址或者目的地址,并且可以使用DMA_XXX相关的API。

    1.使用大DMA 一致性buffer

     ​void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)

    分配<size>大小的一致性区域。其返回值<dma_handle>能被强制类型转换成unsigned integer,这样就是总线的宽度

     ​void * dma_zalloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)

    是对dma_alloc_coherent的封装,并且将返回的地址空间内存清零。

     ​void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle)

    释放一致性内存。

    2.使用小DMA一致性buffer

    需要包括如下代码

     ​#include <linux/dmapool.h>

    其工作有点类似kmem_cache(),不过其使用的是DMA一致性分配器,而非__get_free_pages(),并且需要N-byte对齐。

     ​	struct dma_pool *
    	dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t alloc);

    “name”字段用于诊断,dev和size和传递给dma_alloc_coherent()类似,align以字节计,且需要是2的指数。如果设备没有跨界限制,传递0;传递4096则意味着分配的DMA空间不能跨越4KByte空间。

     ​void *dma_pool_zalloc(struct dma_pool *pool, gfp_t mem_flags, dma_addr_t *handle)
     ​	void *dma_pool_alloc(struct dma_pool *pool, gfp_t gfp_flags, dma_addr_t *dma_handle);

    从pool分配内存,返回内存将会满足size和alignment要求。传递GFP_ATOMIC防止阻塞,或者如果允许阻塞,则可以传递GFP_KERNEL,和dma_alloc_coherent()类似,返回两个值,一个是CPU使用的地址,以及一个设备使用的DMA地址。

     ​void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr); 

    释放由dma_pool_alloc()分配的内存空间到pool。

     ​void dma_pool_destroy(struct dma_pool *pool);

    dma_pool_destroy()将内存池的资源释放。

    part1c DMA寻址限制

     ​int dma_set_mask_and_coherent(struct device *dev, u64 mask)

    检查mask是否合法,如果是跟新设备streaming以及DMA mask参数。返回0是正确。

     ​int dma_set_mask(struct device *dev, u64 mask)
    int dma_set_coherent_mask(struct device *dev, u64 mask

    检查mask是否合法,如果是就跟新。

     ​u64 dma_get_required_mask(struct device *dev)

    检查系统合法的DMA掩码。

    part 1D Streaming(流式) DMA 映射

     ​dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, size_t size, enum dma_data_direction direction)

    将处理器的虚拟地址空间进行映射,这样让处理器可以访问外设,返回值是DMA地址。

    其direction参数可选字段即意义如下:

     ​DMA_NONE		no direction (used for debugging) 
    DMA_TO_DEVICE		data is going from the memory to the device 
    DMA_FROM_DEVICE		data is coming from the device to the memory 
    DMA_BIDIRECTIONAL	direction isn't known

    并不是所有的存储空间都能够使用上面的函数进行映射。内核虚拟地址也许是连续的但是其映射的物理地址却可以不一样。由于这个API不提供scatter/gather功能,如果尝试映射非连续物理存储空间。所以由该API进行映射的地址需要确保物理地址连续,如kmalloc。

    设备DMA的地址内存范围必须在dma_mask,需要确保由kmalloc分配的内存空间在dma_mask所能达到的范围之内。驱动程序也许会传递各种flags以限制DMA地址范围(比如X86能够表示的地址范围在前16MB内)。

    对具有IOMMU的平台,物理地址连续性和dma_mask要求也许不再适用。然而,考虑到可移植性,通常会忽略IOMMU的存在。

     

    内存操作粒度被成为cache line 宽度,为了让这里的API映射的DMA操作成功执行,映射的区域必须在cache line边界开始和结束(目的是不发生对一个cache line出现两个独立的映射区域)。

     

    DMA_TO_DEVICE,在软件修改内存块后程序退出控制前,需要进行同步。一个这一原语被使用,由这一原语包括的这一区域被成为只读,如果同时设备想写,需要使用DMA_BIDIRECTIONAL标志。

    DMA_FROM_DEVICE 在驱动程序修改数据前先同步。内存块需要被当成只读对待。如果驱动要写功能,则需要设置DMA_BIDIRECTIONAL。

    DMA_BIDIRECTIONAL需要特殊对待。这意味这驱动程序不能确定内存的改变在是否发生在将存储空间交给设备前,同时不能确定设备是否需要改变这块内存。所以,需要双向同步内存块。一次是在内存控制权移交给设置前,一次是在获取数据前。

     ​void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction) 

    Unmap先前映射的区域,所有传递进来的参数必须和建立映射接口时的参数一致。

     ​dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size,
    		    enum dma_data_direction direction)
    void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, enum dma_data_direction direction)
    

    对页进行映射和逆映射。轻易不要动<size>和<offset>这两个参数。

     ​int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)

    在一些场景下,dma_map_single() and dma_map_page()在创建一个映射时也许会失败。调用上面的接口可以检测出错的原因。

     ​	int
    	dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction)

    返回segment 映射的DMA地址。

    当使用scatterlist方式时,建立映射的过程如下:

     ​	int i, count = dma_map_sg(dev, sglist, nents, direction);
    	struct scatterlist *sg;
    	for_each_sg(sglist, sg, count, i) { hw_address[i] = sg_dma_address(sg);
    		hw_len[i] = sg_dma_len(sg); }

    zynq PS端pl330驱动注册

     

    3002 static struct amba_driver pl330_driver = {
    3003     .drv = {
    3004         .owner = THIS_MODULE,
    3005         .name = "dma-pl330",
    3006         .pm = &pl330_pm,
    3007     },
    3008     .id_table = pl330_ids,
    3009     .probe = pl330_probe,
    3010     .remove = pl330_remove,
    3011 };
    3012 
    3013 module_amba_driver(pl330_driver);
    

    见到此则知道必然是调用probe函数。注册函数调用如下:

     

     

    2775 static int
    2776 pl330_probe(struct amba_device *adev, const struct amba_id *id)
    2777 {
    2778     struct dma_pl330_platdata *pdat;
    2779     struct pl330_config *pcfg;
    2780     struct pl330_dmac *pl330;
    2781     struct dma_pl330_chan *pch, *_p;
    2782     struct dma_device *pd;
    2783     struct resource *res;
    2784     int i, ret, irq;
    2785     int num_chan;
    2786 
    2787     pdat = dev_get_platdata(&adev->dev);

    上面定义了三个重要的数据结构体,它们的关系如下图:

     

     

    2806     pl330->base = devm_ioremap_resource(&adev->dev, res);
    2807     if (IS_ERR(pl330->base))
    2808         return PTR_ERR(pl330->base);
    

    对地址空间进行映射,并且存在了struct pl330_dmac结构体里。

     

     

    2812     for (i = 0; i < AMBA_NR_IRQS; i++) {
    2813         irq = adev->irq[i];
    2814         if (irq) {
    2815             ret = devm_request_irq(&adev->dev, irq,
    2816                            pl330_irq_handler, 0,
    2817                            dev_name(&adev->dev), pl330);
    2818             if (ret)
    2819                 return ret;
    2820         } else {
    2821             break;
    2822         }
    2823     }

    每一个channel对应于一个中断,但是它们的中断服务函数是同一个。

    2825     pcfg = &pl330->pcfg;
    2826
    2827     pcfg->periph_id = adev->periphid;
    2828     ret = pl330_add(pl330);

    设置pl330的configure字段。

     

     

    2832     INIT_LIST_HEAD(&pl330->desc_pool);
    2833     spin_lock_init(&pl330->pool_lock);
    2834 
    2835     /* Create a descriptor pool of default size */
    2836     if (!add_desc(pl330, GFP_KERNEL, NR_DEFAULT_DESC))
    2837         dev_warn(&adev->dev, "unable to allocate desc\n");
    

    设置pl330的描述符池,并且初始化16个描述符。

    2842     if (pdat)
    2843         num_chan = max_t(int, pdat->nr_valid_peri, pcfg->num_chan);
    2844     else
    2845         num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan);
    2846 
    2847     pl330->num_peripherals = num_chan;
    2848 
    2849     pl330->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
    2850     if (!pl330->peripherals) {
    

    初始化channel数,并且为dma channel分配内存空间。

    2856     for (i = 0; i < num_chan; i++) {
    2857         pch = &pl330->peripherals[i];
    2858         if (!adev->dev.of_node)
    2859             pch->chan.private = pdat ? &pdat->peri_id[i] : NULL;
    2860         else
    2861             pch->chan.private = adev->dev.of_node;
    2862 
    2863         INIT_LIST_HEAD(&pch->submitted_list);
    2864         INIT_LIST_HEAD(&pch->work_list);
    2865         INIT_LIST_HEAD(&pch->completed_list);
    2866         spin_lock_init(&pch->lock);
    2867         pch->thread = NULL;
    2868         pch->chan.device = pd;
    2869         pch->dmac = pl330;
    2870 
    2871         /* Add the channel to the DMAC list */
    2872         list_add_tail(&pch->chan.device_node, &pd->channels);
    2873     }
    

    初始化dma_pl330_chan相关字段。

     

     

    2886     pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
    2887     pd->device_free_chan_resources = pl330_free_chan_resources;
    2888     pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
    2889     pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
    2890     pd->device_tx_status = pl330_tx_status;
    2891     pd->device_prep_slave_sg = pl330_prep_slave_sg;
    2892     pd->device_config = pl330_config;
    2893     pd->device_pause = pl330_pause;
    2894     pd->device_terminate_all = pl330_terminate_all;
    2895     pd->device_issue_pending = pl330_issue_pending;
    2896     pd->src_addr_widths = PL330_DMA_BUSWIDTHS;
    2897     pd->dst_addr_widths = PL330_DMA_BUSWIDTHS;
    2898     pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
    2899     pd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
    

    初始化上图中struct dma_device的若干成员和函数。

     

     

    2901     ret = dma_async_device_register(pd);
    2902     if (ret) {
    2903         dev_err(&adev->dev, "unable to register DMAC\n");
    2904         goto probe_err3;
    2905     }
    

    注册dma设备并创建sys/class接口,会有如下显示:

     

     

     ​root@linaro-ubuntu-desktop:/sys/class/dma# ls
    dma0chan0  dma0chan2  dma0chan4  dma0chan6  dma1chan0
    dma0chan1  dma0chan3  dma0chan5  dma0chan7

    将DMA控制器注册到DT DMA helpers。通过of_dma_list就可以找到该DMA控制器。

     

     

    2907     if (adev->dev.of_node) {
    2908         ret = of_dma_controller_register(adev->dev.of_node,
    2909                      of_dma_pl330_xlate, pl330);
    

    至此,DMA注册函数基本流程完毕。但是还留下了一个irq函数。

     

    DMA中断服务函数

     

    2720 static irqreturn_t pl330_irq_handler(int irq, void *data)
    2721 {
    2722     if (pl330_update(data))
    2723         return IRQ_HANDLED;
    2724     else
    2725         return IRQ_NONE;
    2726 }
    

    该函数实际上调用了pl330_update去完成中断服务函数的请求。由于其是在中断函数中调用的,则中断函数的那些注意事项还是要遵从的。

     

     

    1533 static int pl330_update(struct pl330_dmac *pl330)
    1534 {
    1535     struct dma_pl330_desc *descdone, *tmp;
    1536     unsigned long flags;
    1537     void __iomem *regs;
    1538     u32 val;
    1539     int id, ev, ret = 0;
    1540 
    1541     regs = pl330->base;
    

    该函数的base地址是经过ioremap得到的。

    1545     val = readl(regs + FSM) & 0x1;
    1546     if (val)
    1547         pl330->dmac_tbd.reset_mngr = true;
    1548     else
    1549         pl330->dmac_tbd.reset_mngr = false;
    

    首先都FSM寄存器,然后看bit0是否需要复位mngr。

     

     

    1551     val = readl(regs + FSC) & ((1 << pl330->pcfg.num_chan) - 1);
    1552     pl330->dmac_tbd.reset_chan |= val;
    1553     if (val) {
    1554         int i = 0;
    1555         while (i < pl330->pcfg.num_chan) {
    1556             if (val & (1 << i)) {
    1557                 dev_info(pl330->ddma.dev,
    1558                     "Reset Channel-%d\t CS-%x FTC-%x\n",
    1559                         i, readl(regs + CS(i)),
    1560                         readl(regs + FTC(i)));
    1561                 _stop(&pl330->channels[i]);
    1562             }
    1563             i++;
    1564         }
    1565     }
    

    各个通复位。

     

     

    1568     val = readl(regs + ES);
    1569     if (pl330->pcfg.num_events < 32
    1570             && val & ~((1 << pl330->pcfg.num_events) - 1)) {
    1571         pl330->dmac_tbd.reset_dmac = true;
    1572         dev_err(pl330->ddma.dev, "%s:%d Unexpected!\n", __func__,
    1573             __LINE__);
    1574         ret = 1;
    1575         goto updt_exit;
    1576     }
    

    读取事件寄存器,并判断是否出错了。出错则置reset标志。

     

     

    1578     for (ev = 0; ev < pl330->pcfg.num_events; ev++) {
    1579         if (val & (1 << ev)) { /* Event occurred */
    1580             struct pl330_thread *thrd;
    1581             u32 inten = readl(regs + INTEN);
    1582             int active;
    1583 
    1584             /* Clear the event */
    1585             if (inten & (1 << ev))
    1586                 writel(1 << ev, regs + INTCLR);
    1587 
    1588             ret = 1;
    1589 
    1590             id = pl330->events[ev];
    1591 
    1592             thrd = &pl330->channels[id];
    1593 
    1594             active = thrd->req_running;
    1595             if (active == -1) /* Aborted */
    1596                 continue;
    1597 
    1598             /* Detach the req */
    1599             descdone = thrd->req[active].desc;
    1600             thrd->req[active].desc = NULL;
    1601 
    1602             thrd->req_running = -1;
    1603 
    1604             /* Get going again ASAP */
    1605             _start(thrd);
    1606 
    1607             /* For now, just make a list of callbacks to be done */
    1608             list_add_tail(&descdone->rqd, &pl330->req_done);
    1609         }
    1610     }
    

    处理相应事件。

    展开全文
  • ZYNQ PS DDR应用FDMA (AXI4总线方案
  • ZYNQ基础系列(六) DMA基本用法

    万次阅读 多人点赞 2018-03-30 11:00:42
    涉及到高速数据传输时,DMA就显得非常重要了,本文的DMA主要是对PL侧的AXI DMA核进行介绍(不涉及PS侧的DMA控制器)。AXI DMA的用法基本是:PS通过AXI-lite向AXI DMA发送指令,AXI DMA通过HP通路和DDR交换数据,PL...
  • ZYNQ AXIDMA详解

    2020-07-21 10:59:56
    ZYNQ中,AXIDMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。使用其他的IP(也是AXI4-Stream转AXI4-MM)可以不需要ARM管理,但是在SOC开发时是危险的,这是后话了。 如图1所示,AXIDMA IP有6个接口,S_...
  • ZedBorad 嵌入式Linux下的DMA测试(PS+PL), 包含VIVADO工程代码,Linux下DMA测试APP源代码,Makefile文件,亲测可用。
  • ZYNQ DMA linux设备驱动详解

    千次阅读 2020-03-23 00:20:30
    1) 定义struct dma_device 发量并初始化、根据硬件实现必要的回掉函数。 2) 根据 controller 支持的 channel 数,为每个 channel 定义一个 struct dma_chan 发量并初始化后,将每个 channel 都添加到 dma_device 的 ...
  • ZYNQ DMA测试

    2020-01-19 17:26:49
    参考文章: ZYNQ基础系列(七) LWIP数据通路 -- PL数据->PS->PC机(TCP) ... ZYNQ基础系列(六) DMA基本用法 https://blog.csdn.net/long_fly/article/details/79702222 ...
  • ZYNQ DMA IP核使用手册

    2018-04-25 03:29:25
    学习ZYNQ必然会接触到DMA,本手册是Vivado DMA IP核的官方手册。
  • Zynq DMA Linux 驱动程序 该 Linux 驱动程序已开发为可在 Xilinx Zynq FPGA 上运行。 它是一个包装驱动程序,用于与低级 Xilinx 驱动程序 (xilinx_axidma.c) 对话,该驱动程序连接到在 Zynq FPGA 的 PL 部分中实现的...
  • zynq dma手册.zip

    2021-01-14 16:17:34
    zynq axi总线dma手册
  • 内部教程_基于ZYNQDMA与VDMA的应用开发milian教程
  • ZYNQ基础---AXI DMA使用

    2020-12-14 16:56:41
      在ZYNQ中进行PL-PS数据交互的时候,经常会使用到DMA,其实在前面的ZYNQ学习当中,也有学习过DMA的使用,那就是通过使用自定义的IP,完成HP接口向内存写入和读取数据的方式。同样Xilinx官方也提供有一些DMA的IP,...
  • ZYNQ DMA PL-330用户手册

    2018-05-21 23:18:56
    ZYNQ DMA PL-330用户手册 可用于ZYNQ7000 系列SoC 的DMA 开发参考使用,
  • zynq dma linux 配置

    2017-04-02 18:56:52
    该资源是博客中附带的资源下载链接
  • 米尔科技ZYNQ -Linux下的DMA驱动

    千次阅读 2019-08-08 19:15:19
    ZYNQ的AXIDMA有Direct Register Mode和Scatter/Gather Mode,本文使用的是Direct Register Mode。 Vivado上PL端的构造如下图所示,开启了DMA中断(PL-PS中断)。对于AXI-DMA来说,CPU通过S_AXI_LITE得出...
  • zynq DMA控制器

    2018-11-17 20:35:00
    Zynq-7000系列器件PS端的DMA控制器采用ARM的IP核DMA-330(PL-330)实现。 特点: 1.8个独立的通道,4个可用于PL—PS间数据管理,每个通道有1024Byte的MFIFO 2.使用CPU_2x 时钟搬运数据,CPU_2x= (CPU frq/6)*2 ...
  • ZynqMP SOC是xilinx公司推出的面向AI应用(如:云计算、边缘计算)、高度灵活、高性能的处理器系列,我们选择它作为产品的处理器...本文简要说明在基于Zynq MPSOC的产品开发中,如何在Linux的用户面实现AXI DMA传输。
  • zynq axidma原理

    2021-02-18 19:48:11
    ZYNQ中,AXIDMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。使用其他的IP(也是AXI4-Stream转AXI4-MM)可以不需要ARM管理,但是在SOC开发时是危险的,这是后话了。 如图1所示,AXIDMA IP有6个接口,S_...
  • Zynq7020 DMA裸板测试

    2019-03-06 09:45:28
    最近做了有关DMA的裸机测试,在这记录一下我的测试方法。 首先需要将逻辑给做好,用到的是DMA和DATA FIFO,做成一个回路,这样做的目的是比较待会的数据读写是否一致,然后编译得到bit文件,进去SDK。 进到SDK...
  • PL透过bram 向PL传递 0-9的数据,PL进行数据处理后( 在此为了简化,就将所取得的数据 + 100 返回) 透过dma 传回PS https://blog.csdn.net/howard789/article/details/111194482
  • ZYNQ跑系统 系列(四) AXI-DMA的linux下运行

    万次阅读 多人点赞 2018-05-30 10:47:42
    硬件平台PL的搭建同ZYNQ基础系列(六) DMA基本用法,在这个工程的基础上添加SD卡(根据自己的开发板硬件选择相应的引脚) 然后直接生成bit文件,然后记得要导出硬件(包含bit文件)进SDK 二、生成设备树 1....
  • Zynq AXIS:完整的DMA系统 此存储库包含使用Xilinx的Zynq FPGA建立基于DMA的项目所需的所有组件。 首先,有一个称为AXIS的硬件模块,可连接到高性能AXI接口端口。 其次,有一个Linux UIO驱动程序,可将低级AXIS控制...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,524
精华内容 609
关键字:

dmazynq