[X]关闭
2

(基础篇)S05-CH11_AXI_DMA_LOOP环路测试

摘要: 本章节开始将为大家介绍MicroBlaze中高速接口的设计,这一章将首先为大家介绍DMA的使用,本章是后面DMA课程的基础,读者务必认真先阅读和学习。 本课程是设计一个最基本的DMA环路,实现DMA的环路测试,在SD ...

软件版本:VIVADO2017.4

操作系统:WIN10

硬件平台: ARTIX-7 系列开发板

米联客(MSXBO)论坛www.osrc.cn答疑解惑专栏开通,欢迎大家给我提供!!!

11.1 概述

      本章节开始将为大家介绍MicroBlaze中高速接口的设计,这一章将首先为大家介绍DMA的使用,本章是后面DMA课程的基础,读者务必认真先阅读和学习。

      本课程是设计一个最基本的DMA环路,实现DMA的环路测试,在SDK里面发送数据到DMA然后DMA在把数据发回到DDR里面,SDK读取内存地址里面的数据,对比接收的数据是否和发送出去的一致。DMA的接口部分使用了data_fifo IP链接。本课程会详细介绍创建工程的每个步骤,后面的课程将不再详细介绍创建工程的步骤。

11.2 硬件电路搭建

Step1:创建一个新的vivado工程,命令为System.

Step2:将第一章生成的tcl文件复制到当前文件目录中来,然后使用tcl创建一个BD文件。

Step3:点击IP图标,输入关键字DMA,添加一个DMA

Step4:双击DMA的IP图标,然后如下图所示配置:

Step5:将DMA的驱动时钟信号s_axi_lite_aclk(S_AXI_LITE驱动时钟),m_axi_s2mm_aclk(S_AXIS_S2MM驱动时钟),m_axi_mm2s_aclk(M_AXIS_MM2S驱动时钟)全部与clk_wiz_1的clk_out1连接。

Step6:如下图所示连接DMA的axi_resetn引脚:

Step7:点击Run Connect Automation,然后在弹出来的窗口中勾选所有复选框,单击OK。

 

Step8:点击IP添加图标,输入关键字intc,添加一个intc.

Step9:点击Run connection Automation,然后直接单击OK。

Step10:连接intc的Interrupt和MicroBlaze的INTERRUPT。

Step11:点击IP添加图标,搜索concat,添加一个Concat。

Step12:双击Concat图标,如下图所示设置:

Step13:将Concat的dout引脚与INTC的intr引脚连接。

Step14:将DMA的中断输出引脚与Concat的In0和In1连接。

Step15:单击IP添加图标,搜索FIFO,添加一个AXI4_Stream Data FIFO.

Step16:如下图所示连接Data FIFO.

Step17:给DMA的输入、输出和中断引脚添加Debug信号,添加方法是选中要添加的信号连线,然后右单击,选择Debug命令。

Step18:点击Run Connection Automation

Step19:选中调试组,点击OK。系统会自动添加调试核。

Step20:双击打开IP,可以采用深度进行设置。这里选择默认选项。

Step21:添加ila核,如下设置,点击OK。

Step22:将ila的clk与clk_wiz_1的clk_out1连接。

将ila的probe0、probe1与axi_dma_0的mm2s_introut、s2mm_introut连接。

Step23:选中top.bd,右单击然后选择Generate Output Products。

Step24:选中top.bd,右单击然后选择Create HDL Wrapper,在弹出来的窗口中直接点击OK。

Step25:添加一个名为XDC约束文件。

Step26:点击Generate Bitstream,生成BIT文件。

Step27:生成Bit文件之后,单击File-Export-Export Hardware。

Step28:单击File-Launch SDK,加载SDK。

11.3软件设计

Step1:新建一个名为AXI_DMA_LOOP的SDK工程。

Step2:在我们提供的源代码中,找到sdk_src文件夹,然后复制里面的文件到src目录下。

Step3:选中SDK工程文件,右单击选择Debug as-Debug configuration。

Step4:在弹出来的新窗口中,双击下图圈出部分,然后勾选箭头所示参数

Step5:单击Apply,然后单击Debug(进行这一步之前,先给开发板上电)。

Step6:在下图所示区域找到SDK Terminal,然后单击加号图标

Step7:单击加号图标之后,再新弹出来的窗口中设置好对应的端口号和波特率,然后单击OK。

11.4 程序分析

11.4.1 main.c源码的分析

函数名称

功能

init_intr_sys()

对中断资源的初始化,使能中断资源。这个函数里面调用的函数是笔者封装好的初始化函数,使用起来比较方便。一般只要给出中断对象,中断号,就可以对中断进行初始化。

DMA_Intr_Init(&AxiDma,0)

DMA初始化,第一参数是DMA的对象,第二参数是硬件ID。

Init_Intr_System(&Intc)

对象是中断对象

DMA_Setup_Intr_System

(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID)

注册中断函数,最后2个参数是中断号

DMA_Intr_Enable(&Intc,&AxiDma)

启动DMA传输

XAxiDma_SimpleTransfer

启动一次DMA发送传输

DMA_CheckData

对接收的数据进行校验和对比

表11-4-1-1 init_intr_sys函数

int init_intr_sys(void)

{

DMA_Intr_Init(&AxiDma,0);//initial interrupt system

Init_Intr_System(&Intc); // initial DMA interrupt system

Setup_Intr_Exception(&Intc);

DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system

DMA_Intr_Enable(&Intc,&AxiDma);

}


     为了发送的数据是已知是确定数据,先对TxBufferPtr 发送缓冲进行初始化,初始化后用Xil_DCacheFlushRange 函数把数据全部刷到DDR中。XAxiDma_SimpleTransfer 函数为启动一次DMA接收传输。

表11-4-1-2 main.c主函数

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);


//sprintf(oled_str,"PKT_LEN=%d",MAX_PKT_LEN);

//print_message(oled_str,1);//oled print


//while(1)

for(i = 0; i < Tries; i ++)

{

Value = TEST_START_VALUE + (i & 0xFF);

for(Index = 0; Index < MAX_PKT_LEN; Index ++) {

TxBufferPtr[Index] = Value;


Value = (Value + 1) & 0xFF;

}


/* Flush the SrcBuffer before the DMA transfer, in case the Data Cache

 * is enabled

 */

Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);


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;

}


/*

 * Wait TX done and RX done

 */

while (!TxDone || !RxDone) {

/* NOP */

}


success++;

TxDone = 0;

RxDone = 0;


if (Error) {

xil_printf("Failed test transmit%s done, "

"receive%s done\r\n", TxDone? "":" not",

RxDone? "":" not");

goto Done;

}

/*

 * Test finished, check data

 */

Status = DMA_CheckData(MAX_PKT_LEN, (TEST_START_VALUE + (i & 0xFF)));

if (Status != XST_SUCCESS) {

xil_printf("Data check failed\r\n");

goto Done;

}


}

xil_printf("AXI DMA interrupt example test passed\r\n");

xil_printf("success=%d\r\n",success);

//sprintf(oled_str,"success=%d",success);

//print_message(oled_str,2);

/* Disable TX and RX Ring interrupts and return success */

DMA_DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);

Done:

xil_printf("--- Exiting Test --- \r\n");

//print_message("--Exiting Test---",3);

return XST_SUCCESS;


}


int init_intr_sys(void)

{

DMA_Intr_Init(&AxiDma,0);//initial interrupt system

Init_Intr_System(&Intc); // initial DMA interrupt system

Setup_Intr_Exception(&Intc);

DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system

DMA_Intr_Enable(&Intc,&AxiDma);


return XST_SUCCESS;

}


int main(void)

{

xil_printf("---DMA Test Start-----");

init_intr_sys();

//oled_fresh_en();// enable oled

axi_dma_test();


while(1);


}


11.4.2 dma_intr.c 源码分析

语句1XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

分析:这句代码是为了获取当前中断的对象。void *Callback是一个无符号的指针,传递进来的阐述可以强制转换成其他任何的对象,这里就是强制转换成 XAxiDma 对象了。

语句2:XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

分析:IrqStatus =XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE)这个函数获取当前中断号;

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE),这个函数是响应当前中断,通知CPU 当前中断已经被接收,并且清除中断标志位。如果中断全部正确,TxDone将被置为1表示发送中断完成。如果有错误,则复位DMA,并且设置超时参数。

表11-4-2-1 DMA_TxIntrHandler函数

/*****************************************************************************/

/*

*

* 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;

}

}

接收中断函数的原理和发送一样

语句3: XAxiDma *AxiDmaInst = (XAxiDma *)Callback

分析:这句代码是为了获取当前中断的对象。void *Callback是一个无符号的指针,传递进来的阐述可以强制转换成其他任何的对象,这里就是强制转换成 XAxiDma 对象了。

语句4:IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA)

分析:IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA),这个函数是获取当前中断号。

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA),这个函数是响应当前中断,通知CPU 当前中断已经被接收,并且清除中断标志位。如果中断全部正确,RxDone将被置为1表示接收中断完成。

如果有错误,则复位DMA,并且设置超时参数

表11-4-2-2  DMA_RxIntrHandler函数

/*****************************************************************************/

/*

*

* 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;

}

}


表11-4-2-3 DMA_CheckData函数

/*****************************************************************************/

/*

*

* This function checks data buffer after the DMA transfer is finished.

*

* We use the static tx/rx buffers.

*

* @param Length is the length to check

* @param StartValue is the starting value of the first byte

*

* @return

* - XST_SUCCESS if validation is successful

* - XST_FAILURE if validation is failure.

*

* @note None.

*

******************************************************************************/

 int DMA_CheckData(int Length, u8 StartValue)

{

u8 *RxPacket;

int Index = 0;

u8 Value;


RxPacket = (u8 *) RX_BUFFER_BASE;

Value = StartValue;


/* Invalidate the DestBuffer before receiving the data, in case the

 * Data Cache is enabled

 */

#ifndef __aarch64__

Xil_DCacheInvalidateRange((u32)RxPacket, Length);

#endif


for(Index = 0; Index < Length; Index++) {

if (RxPacket[Index] != Value) {

xil_printf("Data error %d: %x/%x\r\n",

    Index, RxPacket[Index], Value);


return XST_FAILURE;

}

Value = (Value + 1) & 0xFF;

}


return XST_SUCCESS;

}

11.4.3 dam_intr.h 文件分析

一般把DMA相关变量、常量、函数的声明或者定义放到头文件中,dam_intr.h比较关键的参数有

TX_BUFFER_BASE :定义了DMA发送缓存的基地址

RX_BUFFER_BASE :定义了DMA接收缓存的基地址

MAX_PKT_LEN :表示每一包数据传输的长度

NUMBER_OF_TRANSFERS :用在连续测试的时候的测试次数

TEST_START_VALUE :用于测试的起始参数


int  DMA_CheckData(int Length, u8 StartValue);

int  DMA_Setup_Intr_System(XIntc * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);

int  DMA_Intr_Enable(XIntc * IntcInstancePtr,XAxiDma *DMAPtr);

int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);

表11-4-3-1 dam_intr.h

/*

 *

 * www.osrc.cn

 * www.milinker.com

 * copyright by liyang mi lian dian zi www.osrc.cn

*/

#ifndef DMA_INTR_H

#define DMA_INTR_H

#include "xaxidma.h"

#include "xparameters.h"

#include "xil_exception.h"

#include "xdebug.h"

#include "xintc.h"


/************************** Constant Definitions *****************************/

/*

 * Device hardware build related constants.

 */

#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID

#define INTC_DEV_ID XPAR_AXI_INTC_0_DEVICE_ID

#define DDR_BASE_ADDR 0x80000000


#define MEM_BASE_ADDR (DDR_BASE_ADDR+0x01000000)



#define RX_INTR_ID XPAR_INTC_0_AXIDMA_0_S2MM_INTROUT_VEC_ID

#define TX_INTR_ID XPAR_INTC_0_AXIDMA_0_MM2S_INTROUT_VEC_ID



#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000)

#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000)

#define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF)



/* Timeout loop counter for reset

 */

#define RESET_TIMEOUT_COUNTER 10000

/* test start value

 */

#define TEST_START_VALUE 0xC

/*

 * Buffer and Buffer Descriptor related constant definition

 */

#define MAX_PKT_LEN 2047//4MB

/*

 * transfer times

 */

#define NUMBER_OF_TRANSFERS 5


extern volatile int TxDone;

extern volatile int RxDone;

extern volatile int Error;


int  DMA_CheckData(int Length, u8 StartValue);

int  DMA_Setup_Intr_System(XIntc * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);

int  DMA_Intr_Enable(XIntc * IntcInstancePtr,XAxiDma *DMAPtr);

int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);

#endif

11.5 测试结果

Step1:在VIVADO工程中点击Open Target 然后点击Auto Connect(前面必须先启动SDK)

Step2:连接成功后入下图

Step4: 点击添加接收内存部分地址用于观察内存中的数据,地址为 0X81100000

       为了观察数据,需要设置断点,如图所示:

      然后重新下载SDK程序,让收发程序先跑一次,可以看到第一个数据是0X0C 后面是依次加1


路过

雷人

握手

鲜花

鸡蛋
发表评论

最新评论

引用 林桐 2022-10-14 08:44
我非常想知道xilinx的axi dma和axi central dma这两个ip core的区别。。。求教!!!
引用 田1766 2020-4-30 11:14
DMA的M_AXI_MM2S,M_AXI_S2MM接口怎么连接?

查看全部评论(2)

本文作者
2019-11-7 16:58
  • 1
    粉丝
  • 6419
    阅读
  • 2
    回复

关注uisrc网络

扫描关注,了解最新资讯

电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼
热门评论
排行榜