ZYNQ之DMA基本用法

DMA 环路测试
涉及到高速数据传输时,DMA就显得非常重要了,本文的DMA主要是对PL侧的AXI DMA核进行介绍(不涉及PS侧的DMA控制器)。AXI DMA的用法基本是:PS通过AXI-lite向AXI DMA发送指令,AXI DMA通过HP通路和DDR交换数据,PL通过AXI-S读写DMA的数据。

实验思路
首先设计一个最基本的DMA环路

实验思路是:首先PS通过AXI-lite配置DMA的工作模式,然后,PS将数据写入DDR,再然后,PS控制DMA读出之前写入的数据,将数据流写入FIFO(读完后DMA会向PS发送中断),再然后,PS控制DMA将FIFO的数据流再通过AXI总线写回DDR(写完后DMA会向PS发送中断),PS判断写入的数据和读出的数据是否一致,完成实验

搭建硬件环境
1.新建工程、原理图文件等
2.加入实验需要的IP核

3.配置PS核相应的参数

设置PS板上输入时钟,设置PS输出时钟为100M

设置板上DDR型号

添加HP高速接口,用于DMA和DDR控制器间通信

添加中断
最后添加串口设备(用于调试)、设置正确的BANK电压

4.配置DMA核相关参数

不使能高性能模式和精简模式
5.点击自动生成模块和自动布线
6.将中断信号合并送到PS中

7.将FIFO加入到数据环路中,完成后效果如下

红色标记为数据的流向,其余两个标记线分别为100M时钟和复位信号
8.进行以下操作后,生成bit文件
Gerate Output products、Create wrappers、Generate Bitstream

进入SDK环境
main.c文件:
定义Tries为测试次数,TxBufferPtr的地址为DMA发送的数据到设备的地址,RxBufferPtr地址则是设备发给DMA的存储地址,MAX_PKT_LEN为一次测试的测试长度,数据从0x55开始递增。
一次测试的过程:
PS的 for 循环产生数据—>PS的数据写入DDR—>PS配置DMA接收通道—>PS配置DMA发送通道(数据开始发送)—>发送的数据经过FIFO回到DMA—>DMA中断到达后观察数据—>进行下一次测试

#include "dma_intr.h"
#include "sys_intr.h"

static  XScuGic Intc; //GIC中断控制器
static  XAxiDma AxiDma;

int Tries = 6;
int i;
int Index;
u8 *TxBufferPtr= (u8 *)TX_BUFFER_BASE;
u8 *RxBufferPtr= (u8 *)RX_BUFFER_BASE;
u8 Value;

int axi_dma_test()
{
    int Status;
    TxDone = 0;
    RxDone = 0;
    Error = 0;

    xil_printf("\r\n----DMA Test----\r\n");
    xil_printf("PKT_LEN=%d\r\n",MAX_PKT_LEN);

    for(i = 0; i < Tries; i ++)
    {
        Value = 0x55 + (i & 0xFF);
        for(Index = 0; Index < MAX_PKT_LEN; Index ++) {
                TxBufferPtr[Index] = Value;

                Value = (Value + 1) & 0xFF;
        }

        Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);//数据刷新到DDR中

        Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) RxBufferPtr,//配置接收通道
                    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
        if (Status != XST_SUCCESS) {return XST_FAILURE;}

        Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) TxBufferPtr,//配置发送通道
                    MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
        if (Status != XST_SUCCESS) {return XST_FAILURE;}

        while (!TxDone || !RxDone) { }//等待收发中断都到达

        TxDone = 0;
        RxDone = 0;
        if (Error) {return XST_FAILURE;}

        Xil_DCacheInvalidateRange((u32)RxBufferPtr, MAX_PKT_LEN);//刷新cache,观察DDR的最新数据
    }

    DMA_DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);//失能DMA中断
    xil_printf("--- Exiting Test --- \r\n");
    return XST_SUCCESS;
}

int main(void)
{
    DMA_Intr_Init(&AxiDma,0);//DMA初始化
    Init_Intr_System(&Intc); //系统初始化
    Setup_Intr_Exception(&Intc);//使能硬件中断
    DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//注册DMA收发中断
    DMA_Intr_Enable(&Intc,&AxiDma);//使能系统中断

    axi_dma_test();
}

dma_intr.h文件:
在该头文件中主要设置收发的存储地址、超时时间、一次测试的长度

#ifndef DMA_INTR_H
#define DMA_INTR_H
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"
/************************** Constant Definitions *****************************/
/*
 * Device hardware build related constants.
 */
#define DMA_DEV_ID      XPAR_AXIDMA_0_DEVICE_ID
#define MEM_BASE_ADDR       0x01000000

#define RX_INTR_ID      XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR
#define TX_INTR_ID      XPAR_FABRIC_AXI_DMA_0_MM2S_INTROUT_INTR

#define TX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE      (MEM_BASE_ADDR + 0x00300000)

#define RESET_TIMEOUT_COUNTER   10000//超时计数值
#define MAX_PKT_LEN     2047 //一次测试的长度

extern volatile int TxDone;
extern volatile int RxDone;
extern volatile int Error;

int  DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);
int  DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr);
int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);
void DMA_DisableIntrSystem(XScuGic * IntcInstancePtr,u16 TxIntrId, u16 RxIntrId);
#endif

dma_intr.c文件:
DMA配置的相关函数

#include "dma_intr.h"

volatile int TxDone;
volatile int RxDone;
volatile int Error;

/*****************************************************************************/
/**
*
* This function disables the interrupts for DMA engine.
*
* @param    IntcInstancePtr is the pointer to the INTC component instance
* @param    TxIntrId is interrupt ID associated w/ DMA TX channel
* @param    RxIntrId is interrupt ID associated w/ DMA RX channel
*
* @return   None.
*
* @note     None.
*
******************************************************************************/
 void DMA_DisableIntrSystem(XScuGic * IntcInstancePtr,
                    u16 TxIntrId, u16 RxIntrId)
{
#ifdef XPAR_INTC_0_DEVICE_ID
    /* Disconnect the interrupts for the DMA TX and RX channels */
    XIntc_Disconnect(IntcInstancePtr, TxIntrId);
    XIntc_Disconnect(IntcInstancePtr, RxIntrId);
#else
    XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
    XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
#endif
}
/*****************************************************************************/
/*
*
* This is the DMA TX Interrupt handler function.
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then sets the TxDone.flag
*
* @param    Callback is a pointer to TX channel of the DMA engine.
*
* @return   None.
*
* @note     None.
*
******************************************************************************/
static void DMA_TxIntrHandler(void *Callback)
{
    u32 IrqStatus;
    int TimeOut;
    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

    /* Read pending interrupts */
    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

    /* Acknowledge pending interrupts */

    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

    /*
     * If no interrupt is asserted, we do not do anything
     */
    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
        return;
    }

    /*
     * If error interrupt is asserted, raise error flag, reset the
     * hardware to recover from the error, and return with no further
     * processing.
     */
    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
        Error = 1;
        /*
         * Reset should never fail for transmit channel
         */
        XAxiDma_Reset(AxiDmaInst);

        TimeOut = RESET_TIMEOUT_COUNTER;

        while (TimeOut) {
            if (XAxiDma_ResetIsDone(AxiDmaInst)) {
                break;
            }
            TimeOut -= 1;
        }
        return;
    }
    /*
     * If Completion interrupt is asserted, then set the TxDone flag
     */
    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
        TxDone = 1;
    }
}

/*****************************************************************************/
/*
*
* This is the DMA RX interrupt handler function
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then it sets the RxDone flag.
*
* @param    Callback is a pointer to RX channel of the DMA engine.
*
* @return   None.
*
* @note     None.
*
******************************************************************************/
static void DMA_RxIntrHandler(void *Callback)
{
    u32 IrqStatus;
    int TimeOut;
    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

    /* Read pending interrupts */
    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

    /* Acknowledge pending interrupts */
    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);

    /*
     * If no interrupt is asserted, we do not do anything
     */
    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
        return;
    }

    /*
     * If error interrupt is asserted, raise error flag, reset the
     * hardware to recover from the error, and return with no further
     * processing.
     */
    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
        Error = 1;
        /* Reset could fail and hang
         * NEED a way to handle this or do not call it??
         */
        XAxiDma_Reset(AxiDmaInst);

        TimeOut = RESET_TIMEOUT_COUNTER;

        while (TimeOut) {
            if(XAxiDma_ResetIsDone(AxiDmaInst)) {
                break;
            }
            TimeOut -= 1;
        }
        return;
    }
    /*
     * If completion interrupt is asserted, then set RxDone flag
     */
    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
        RxDone = 1;
    }
}

/*****************************************************************************/
/*
*
* This function setups the interrupt system so interrupts can occur for the
* DMA, it assumes INTC component exists in the hardware system.
*
* @param    IntcInstancePtr is a pointer to the instance of the INTC.
* @param    AxiDmaPtr is a pointer to the instance of the DMA engine
* @param    TxIntrId is the TX channel Interrupt ID.
* @param    RxIntrId is the RX channel Interrupt ID.
*
* @return
*       - XST_SUCCESS if successful,
*       - XST_FAILURE.if not succesful
*
* @note     None.
*
******************************************************************************/
int DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
    int Status;
    XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);

    Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
                (Xil_InterruptHandler)DMA_TxIntrHandler,
                AxiDmaPtr);
    if (Status != XST_SUCCESS) {
        return Status;
    }

    Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
                (Xil_InterruptHandler)DMA_RxIntrHandler,
                AxiDmaPtr);
    if (Status != XST_SUCCESS) {
        return Status;
    }
    XScuGic_Enable(IntcInstancePtr, TxIntrId);
    XScuGic_Enable(IntcInstancePtr, RxIntrId);
    return XST_SUCCESS;
}

int DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr)
{
    /* Disable all interrupts before setup */
    XAxiDma_IntrDisable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrDisable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    /* Enable all interrupts */
    XAxiDma_IntrEnable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    return XST_SUCCESS;
}

int DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId)
{
    int Status;
    XAxiDma_Config *Config=NULL;

    Config = XAxiDma_LookupConfig(DeviceId);
    if (!Config) {
        xil_printf("No config found for %d\r\n", DeviceId);
        return XST_FAILURE;
    }

    Status = XAxiDma_CfgInitialize(DMAPtr, Config);
    if (Status != XST_SUCCESS) {
        xil_printf("Initialization failed %d\r\n", Status);
        return XST_FAILURE;
    }
    if(XAxiDma_HasSg(DMAPtr)){
        xil_printf("Device configured as SG mode \r\n");
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}

sys_intr.h文件:
系统中断的相关函数

#ifndef SYS_INTR_H_
#define SYS_INTR_H_

#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"

#define INTC_DEVICE_ID          XPAR_SCUGIC_SINGLE_DEVICE_ID

int Init_Intr_System(XScuGic * IntcInstancePtr);
void Setup_Intr_Exception(XScuGic * IntcInstancePtr);

#endif /* SYS_INTR_H_ */

sys_intr.c文件:

#include "sys_intr.h"
void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
{
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler)XScuGic_InterruptHandler,
            (void *)IntcInstancePtr);

    Xil_ExceptionEnable();
}

int Init_Intr_System(XScuGic * IntcInstancePtr)
{
    int Status;
    XScuGic_Config *IntcConfig;
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == IntcConfig) {
        return XST_FAILURE;
    }
    Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) {return XST_FAILURE;}
    return XST_SUCCESS;
}

调试观察现象

此处设置断点,此时收发中断均已到达,可以直接观察TXBUFFER里的数据的变化,从右侧表格可以看出TXBUFFER里的数据已经更新为0x55开头

虽然接收的数据在上一个断点就以及存在DDR中了,但是要经过一个刷新cache的函数Xil_DCacheInvalidateRange,才能直接观察到数据的变化,在图中可以看到RXBUFFER的数据和TXBUFFER的数据完全一致,本次测试则成功结束

————————————————
版权声明:本文为CSDN博主「long_fly」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/long_fly/article/details/79702222

### ZynqDMA 模块的使用教程 #### 1. 实验环境准备 为了在 Zynq 平台上成功运行 DMA 模块,需要搭建合适的开发环境。具体包括 VMware 虚拟机、Ubuntu 16.04 版本 Linux 系统以及 Xilinx SDK 和 PetaLinux 工具链[^2]。 #### 2. 下载并解压 axi_dma 模块文件包 从 GitHub 上获取 `axi_dma` 的参考实现代码库,地址为 https://github.com/bperez77/xilinx_axidma/tree/master 。此模块提供了基础框架用于测试和验证 DMA 功能。 #### 3. 创建 Petalinux 工程 通过命令行工具创建一个新的 Petalinux 工程,并指定目标硬件平台的设计文件路径。例如: ```bash petalinux-create -t project --template zynq --name my_zynq_project cd my_zynq_project/ petalinux-config --get-hw-description=<path_to_hardware_design> ``` #### 4. 配置 Linux 内核支持 AXI_DMA 进入内核配置界面,启用对 AXI_DMA 设备的支持选项。这一步确保操作系统能够识别并加载相应的驱动程序。 ```bash petalinux-config -c kernel ``` 在弹出的菜单中导航至 **Device Drivers → Staging drivers → Xilinx drivers**, 启用 AXI-DMA 支持。 #### 5. 修改设备树 (DTS) 文件 编辑设备树源码 (.dts), 描述板级资源分配情况,特别是针对 AXI_DMA 控制器的部分参数设置。比如定义其寄存器基址范围、中断号等属性。以下是片段示例: ```dts dma@<address> { compatible = "xlnx,axi-dma-1.00.a"; reg = <...>; interrupts = <45>; /* 对应 DMAC ABORT IRQ */ }; ``` 注意这里的中断编号 `<45>` 是指当发生传输错误时触发的特定信号线编码[^1]^。 #### 6. 添加自定义模块到项目构建流程 如果计划扩展标准功能或者修正现有行为,则需编写专属 C/C++ 编写逻辑单元作为独立插件加入整体解决方案之中。完成之后记得更新 Makefile 构建脚本来反映新增加的内容位置关系。 #### 7. 执行编译过程 利用 petalinux-build 命令启动整个系统的交叉编译工作流直至生成最终镜像成果物为止。 ```bash petalinux-build ``` #### 8. 测试示范应用案例 最后部署生成好的根文件系统映象至实际物理电路板上执行预设好演示应用程序来观察预期效果表现是否正常无误。 --- ### 提供一段简单快速排序算法 Python 实现帮助理解递归概念: ```python def quick_sort(arr): """Perform QuickSort on the input array.""" if len(arr) <= 1: return arr else: pivot = arr[len(arr)//2] left = [x for x in arr if x<pivot ] middle=[pivot]*arr.count(pivot) right =[x for x in arr if x>pivot] return quick_sort(left)+middle+quick_sort(right) print(quick_sort([3,6,8,10,1,2,1])) ``` 以上代码展示了经典的分治法思想应用于数组排序场景下的经典实例之一—Quick Sort 方法论的实际运用形式[^3]. ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值