ZYNQ DMA

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。

6 完整工程链接

PL数据通过DMA传输到PS端

  • 16
    点赞
  • 134
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Zynq DMA(Direct Memory Access)数据回环是指将数据从一个片内存储器(例如PS端内存)传输到另一个片内存储器(例如PL端内存),然后再将数据从PL端内存传回到PS端内存的过程。 在Zynq SoC中,DMA是用于高效地进行数据传输的关键组件。它可以减轻处理器(PS)的负载,并且能够实现高速数据传输和并行处理。 进行Zynq DMA数据回环的步骤如下: 1. 配置DMA控制器:通过软件配置DMA控制器,定义数据传输的源地址、目的地址、传输大小等参数。可以通过PS端的DMA控制寄存器来设置这些参数。 2. 启动DMA传输:向DMA控制寄存器写入启动传输的命令,将数据从PS端内存传输到PL端内存。 3. PL端处理:在PL端接收到数据后进行处理,可以进行算法运算、滤波器操作等等。 4. 数据传输回PS端:将处理后的数据从PL端内存传输回PS端内存。同样,通过配置DMA控制器并启动传输命令来完成数据传输。 5. PS端处理:在PS端接收到数据后,可以对数据进行后续处理操作,例如进一步的分析、显示、存储等操作。 利用Zynq DMA数据回环,可以实现高效的数据处理和传输。数据回环常用于验证硬件设计的正确性和性能测试,也可以应用于各种需要高速数据传输和处理的应用中,例如图像和视频处理、信号处理、音频处理等领域。 ### 回答2: Zynq DMA数据回环是一种在Zynq芯片上使用DMA(Direct Memory Access)模块实现数据传输的方法。DMA是一种可以直接在外部设备和内存之间传输数据的技术,可以降低CPU的负载,提高系统性能。 在Zynq芯片上,数据回环是将数据从输入端口传输到输出端口,再将传输的数据通过DMA模块回写到内存中。整个过程通过DMA进行数据传输,减少了CPU的参与,提高了效率。该过程可以通过设置DMA控制寄存器和配置寄存器来实现。 首先,需要配置DMA的通道和传输模式,选择合适的通道和传输模式来满足要求。然后,设置源地址和目的地址,确定数据传输的起始位置和终止位置。接着,设置传输长度,确定要传输的数据长度。最后,启动DMA传输,等待传输完成。 在数据回环过程中,可以对传输的数据进行处理和转换,以满足具体需求。比如,可以对传输的数据进行加密和解密、数据压缩和解压缩等操作。同时,可以通过DMA传输数据的速度和优先级进行调整,以满足不同应用场景的需求。 总之,Zynq DMA数据回环是一种通过使用DMA模块实现数据传输的技术,可以有效降低CPU负载,提高系统性能。通过合理配置DMA通道和传输模式,并对传输的数据进行处理和转换,可以满足不同应用场景的需求。 ### 回答3: Zynq DMA数据回环是指使用Zynq芯片内部的DMA模块实现数据的循环传输。DMA(Direct Memory Access)是一种数据传输方式,可以通过DMA控制器直接在外设和内存之间进行数据传输,而不需要CPU的干预。 在Zynq芯片中,具有DMA模块的PS(Processing System)可以与PL(Programmable Logic)进行数据传输。实现DMA数据回环需要以下步骤: 1. 配置DMA控制器:首先,需要在PS中配置DMA控制器。可以通过设置寄存器来配置传输模式、传输方向、传输长度等参数。还可以设置中断使能来实现数据传输完成后的中断功能。 2. 分配内存空间:在PS中,分配一块内存空间作为DMA传输的缓冲区。可以通过动态内存分配函数malloc()来申请一块连续的内存空间。 3. 设置DMA传输:将DMA控制器的源地址设置为PS的内存缓冲区地址,目的地址设置为PL的内存地址,设置传输长度和传输方向。 4. 启动DMA传输:通过写入DMA控制器的寄存器来启动数据传输。 5. 等待传输完成:等待DMA传输完成的中断或者轮询DMA控制器的寄存器来判断传输是否完成。 6. 检查数据:通过比较PS内存缓冲区和PL内存之间的数据,确认数据回环是否成功。 需要注意的是,对于Zynq DMA数据回环,PL开发人员需要实现对PL内存的访问,将传输的数据写入PL内存,并且在数据回环完成后将数据读取出来。此外,还需要确保DMA的传输速率满足需求,避免数据丢失或延迟。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值