ZYNQ基础系列(六) DMA基本用法

ZYNQ 基础系列 专栏收录该内容
7 篇文章 8 订阅

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的数据完全一致,本次测试则成功结束

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

打赏

long_fly

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值