学习内容
前文对AXI DMA IP进行了简介,本文使用AXI DMA IP进行环路测试。
开发环境
vivado 18.3&SDK,PYNQ-Z2开发板。
系统框图
本次工程使用ZYNQ开发板上的AXI DMA IP核从DDR3中读取数据,并写回DDR3中。在实际应用中, DMA 一般与产生数据或需求数据的 IP 核相连接,在本次实验中,我们使用 AXI4 Stream Data FIFO IP 核来充当这类 IP 进行 DMA 环回实验。
PS 开启 HP0 和 GP0 接口。 AXI DMA 和 AXI4 Stream Data FIFO 在 PL 中实现。处理器(ARM9)通过M_AXI_HP0接口和AXI DMA通信,用于设置、启动、监控数据传输。数据传输通过S_AXI_HP0接口。AXI DMA通过S_AXI_HP0接口DDR3中读取数据后发送给 AXI4 Stream Data FIFO,AXI DMA 读取 AXI4 Stream Data FIFO 中的数据后通过 S_AXI_HP0接口写入 DDR3。
硬件平台搭建
新建工程,创建 block design。添加AXI DMA IP,这里我们可以根据自己的需要对IP进行配置。
这里我们要使用DMA的直接寄存器模式进行传输。所以如下图配置即可。
添加ZYNQ7 IP核,勾选相应的GP和HP接口。
勾选复位引脚和时钟,
打开中断,完成对zynq IP核的配置。
添加AXI-Stream Data FIFO ,配置保持默认。
为了把AXI DMA IP 的中断管脚连接到ZYNQ7的IP核上,需要添加concat IP进行接口的转接。
完成添加后,可以使用软件进行自动互联。
自动连接完成如图所示,
然后手动连接FIFO IP核和concat IP核如下:
然后我们进行generate output product 然后生成HDL封装。这里只用到了UART,是MIO引脚,所以不需要进行管脚分配。接着进行综合布局布线,等待生成bit流文件。bit文件生成后在FILE处,点击导出硬件资源(包含bit流文件),接着launch SDK。
SDK软件部分
打开SDK后,新建application project。在main.c中输入以下代码:
#include "stdio.h" #include "xaxidma.h" #include "xparameters.h" #include "xscugic.h" //------------------定义ID-------------------- #define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID #define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID #define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //-----------------定义地址变量---------------- #define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR #define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) #define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) #define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //--------------------------------------------- #define RESET_TIMEOUT_COUNTER 10000 //复位时间 #define TEST_START_VALUE 0x0 //测试的初始值 #define MAX_PKT_LEN 0x100 //发送包的长度 //---------------------定义实例----------------- static XAxiDma AxiDma;/* Instance of the XAxiDma */ XAxiDma_Config *DmaConfig; static XScuGic Intc; /* Instance of the Interrupt Controller */ //--------------------发送标志------------------ volatile int TxDone; volatile int RxDone; volatile int Error; //--------------------函数定义------------------ void init_dma(); static int CheckData(int Length, u8 StartValue); static void TxIntrHandler(void *Callback); static void RxIntrHandler(void *Callback); static int SetupIntrSystem(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId); static void DisableIntrSystem(XScuGic * IntcInstancePtr,u16 TxIntrId, u16 RxIntrId); int main(){ int i; u8 Value; u8 *TxBufferPtr; u8 *RxBufferPtr; TxBufferPtr = (u8 *)TX_BUFFER_BASE; RxBufferPtr = (u8 *)RX_BUFFER_BASE; printf("dma init!\n\r"); init_dma(); printf("setup interrupt!\n\r"); SetupIntrSystem(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID); //初始化标志位, TxDone=0; RxDone=0; Error=0; Value = TEST_START_VALUE; for(i = 0; i < MAX_PKT_LEN; i ++) { TxBufferPtr[i] = Value; Value = (Value + 1) & 0xFF; } Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);//刷新cache //开启传输 XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE); Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);//刷新cache //检查是否完成、是否出错 while (!TxDone && !RxDone && !Error); if (Error) { printf("Failed test transmit%s done, " "receive%s done\r\n", TxDone? "":" not", RxDone? "":" not"); goto Done; } printf("CheckData!\r\n"); CheckData(MAX_PKT_LEN,TEST_START_VALUE); printf("CheckData successfully!\r\n"); DisableIntrSystem(&Intc,TX_INTR_ID,RX_INTR_ID); Done:printf("finish\r\n"); } void init_dma(){ DmaConfig = XAxiDma_LookupConfig(DMA_DEV_ID); XAxiDma_CfgInitialize(&AxiDma, DmaConfig); } static int SetupIntrSystem(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId){ //初始化中断控制器 XScuGic_Config *scugicCfg; scugicCfg = XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(IntcInstancePtr,scugicCfg,scugicCfg->CpuBaseAddress); //初始化异常处理 Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)IntcInstancePtr); Xil_ExceptionEnable(); //中断引脚类型设置和优先级 XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3); XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3); //链接中断信号 XScuGic_Connect(IntcInstancePtr, RxIntrId,(Xil_InterruptHandler)RxIntrHandler, AxiDmaPtr); XScuGic_Connect(IntcInstancePtr, TxIntrId,(Xil_InterruptHandler)TxIntrHandler, AxiDmaPtr); //使能中断 XScuGic_Enable(IntcInstancePtr, TxIntrId); XScuGic_Enable(IntcInstancePtr, RxIntrId); XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); } static void DisableIntrSystem(XScuGic * IntcInstancePtr,u16 TxIntrId, u16 RxIntrId){ XScuGic_Disconnect(IntcInstancePtr, TxIntrId); XScuGic_Disconnect(IntcInstancePtr, RxIntrId); } static int CheckData(int Length, u8 StartValue){ u8 *RxPacket; int i= 0; u8 Value; RxPacket = (u8 *) RX_BUFFER_BASE; Value = StartValue; for(i = 0; i < Length; i++) { if (RxPacket[i] != Value) { printf("Data error %d: %x/%x\r\n",i, RxPacket[i], Value); } Value = (Value + 1) & 0xFF; } } static void TxIntrHandler(void *Callback){ int TimeOut; u32 IrqStatus; XAxiDma *AxiDmaInst = (XAxiDma *)Callback; //读取待处理的中断 IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE); //确认待处理的中断 XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE); //TX出错 if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) { Error = 1; XAxiDma_Reset(AxiDmaInst); TimeOut = RESET_TIMEOUT_COUNTER; while (TimeOut) { if (XAxiDma_ResetIsDone(AxiDmaInst)) break; TimeOut -= 1; } return; } //TX完成 if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) TxDone = 1; } static void RxIntrHandler(void *Callback){ u32 IrqStatus; int TimeOut; XAxiDma *AxiDmaInst = (XAxiDma *)Callback; IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA); //rx出错 if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) { Error = 1; XAxiDma_Reset(AxiDmaInst); TimeOut = RESET_TIMEOUT_COUNTER; while (TimeOut) { if(XAxiDma_ResetIsDone(AxiDmaInst)) { break;} TimeOut -= 1; } return; } //RX完成 if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) { RxDone = 1; } }
部分代码讲解
这里的实现的是DMA的读写环路测试,在对应的位置首先写入测试数据,然后将写入的数据读取,和测试数据进行比对,如果比对结果一致,则说明代码测试通过。
这里的代码整理来自xilinx公司提供的模板,依次点击即可导入测试工程。
代码的整体逻辑为:
- 初始化DMA
- 初始化DMA的读写的中断功能
- 写入测试数据
- 刷新发送cache
- 开启读写传输
- 刷新接收cache
- 进行数据校验比对
- 比对一致返回测试完成的提示信息,不一致打印测试和比对信息