前言
DMA(Direct Memory Access,直接存储器访问)是计算机科学中的一种内存访问技术。它允许某些计算机内部的硬件子系统可以独立地直接读写系统内存,而不需中央处理器( CPU)介入处理。 DMA 是一种快速的数据传送方式, 通常用来传送数据量较多的数据块
使用 DMA时, CPU 向 DMA 控制器发出一个存储传输请求, 这样当 DMA 控制器在传输的时候, CPU 执行其它操作,传输操作完成时 DMA 以中断的方式通知 CPU。
为了发起传输事务, DMA 控制器必须得到以下数据:
• 源地址 — 数据被读出的地址
• 目的地址 — 数据被写入的地址
• 传输长度 — 应被传输的字节数
DMA 存储传输的过程如下:
为了配置用 DMA 传输数据到存储器,处理器发出一条 DMA 命令
DMA 控制器把数据从外设传输到存储器或从存储器到存储器,而让 CPU 腾出手来做其它操作。
数据传输完成后,向 CPU 发出一个中断来通知它 DMA 传输可以关闭了。ZYNQ 提供了两种 DMA,一种是集成在 PS 中的硬核 DMA,另一种是 PL 中使用的软核 AXI DMA IP。
在 ARM CPU 设计的过程中,已经考虑到了大量数据搬移的情况,因此在 CPU 中自带了一个 DMA 控制器 DAMC,这个 DAMC 驻留在 PS 内,而且必须通过驻留在内存中的 DMA 指令编程,这些程序往往由CPU 准备,因此需要部分的 CPU 参与。 DMAC 支持高达 8 个通道,所以多个 DMA 结构的核可以挂在单个DMAC 上。 DAMC 与 PL 的连接是通过 AXI_GP 接口,这个接口最高支持到 32 位宽度,这也限制了这种模式下的传输速率,理论最高速率为 600MB/s。这种模式不占用 PL 资源,但需要对 DMA 指令编程,会增加软件的复杂性。
为了获取更高的传输速率,可以以空间换时间,在 PL 中添加 AXI DMA IP 核,并利用 AXI_HP 接口完成高速的数据传输。原子将各个接口方式的比较做了表格,很是详细
可见通过 PL 的 DMA 和 AXI_HP 接口的传输适用于大块数据的高性能传输,带宽高。
下面我们简单的介绍下 PL 的 DMA,即 AXI DMA IP 核。
AXI Direct Memory Access( AXI DMA) IP 内核在 AXI4 内存映射和 AXI4-Stream IP 接口之间提供高带宽直接储存访问。其可选的 scatter gather 功能还可以从基于处理器的系统中的中央处理单元( CPU)卸载数据移动任务。初始化、 状态和管理寄存器通过 AXI4-Lite 从接口访问。核心的功能组成如下图所示:
AXI DMA 用到了三种总线, AXI4-Lite 用于对寄存器进行配置, AXI4 Memory Map 用于与内存交互,又分为 AXI4 Memory Map Read 和 AXI4 Memory Map Write 两个接口,一个是读一个是写。 AXI4 Stream 接口用于对外设的读写,其中 AXI4 Stream Master( MM2S, Memory Map to Stream)用于对外设写, AXI4-Stream Slave(S2MM, Stream to Memory Map)用于对外设读。总之,在以后的使用中需要知道 AXI_MM2S 和AXI_S2MM 是存储器端映射的 AXI4 总线,提供对存储器( DDR3)的访问。 AXIS_MM2S 和 AXIS_S2MM是 AXI4-streaming 总线,可以发送和接收连续的数据流,无需地址。
AXI DMA 提供 3 种模式,分别是 Direct Register 模式、 Scatter/Gather 模式和 Cyclic DMA 模式,这里我们简单的介绍下常用的 Direct Register 模式和 Scatter/Gather 模式。
Direct Register DMA 模式也就是 Simple DMA。 Direct Register 模式提供了一种配置,用于在 MM2S 和S2MM 通道上执行简单的 DMA 传输,这需要更少的 FPGA 资源。 Simple DMA 允许应用程序在 DMA 和Device 之间定义单个事务。它有两个通道:一个从 DMA 到 Device,另一个从 Device 到 DMA。应用程序必须设置缓冲区地址和长度字段以启动相应通道中的传输。
Scatter/Gather DMA 模式允许在单个 DMA 事务中将数据传输到多个存储区域或从多个存储区域传输数据。它相当于将多个 Simple DMA 请求链接在一起。 SGDMA 允许应用程序在内存中定义事务列表,硬件将在应用程序没有进一步干预的情况下处理这些事务。在此期间,应用程序可以继续添加更多工作以保持硬件工作。用户可以通过轮询或中断来检查事务是否完成。 SGDMA 处理整个数据包( 被定义为表示消息的一系列数据字节)并允许将数据包分解为一个或多个事务。例如,采用以太网 IP 数据包,该数据包由 14 字节的报头后跟 1 个或多个字节的有效负载组成。使用 SGDMA,应用程序可以将 BD( Buffer Descriptor, 用于描述事务的对象) 指向报头,将另一个 BD 指向有效负载,然后将它们作为单个消息传输。这种策略可以使TCP / IP 堆栈更有效,它允许将数据包标头和数据保存在不同的内存区域,而不是将数据包组装成连续的内存块。
在此次的工程中暂时用不到SG模式,如果用sata这些的话估计得用到了,正常情况下简单模式还是够用的
下面做一个简单的AXIDMA初始化和收发demo测试
头文件如下
#ifndef SRC_XDMA_DRIVER_H_ #define SRC_XDMA_DRIVER_H_ #include "xaxidma.h" #include "xparameters.h" #include "xil_exception.h" #include "xdebug.h" #ifdef XPAR_UARTNS550_0_BASEADDR #include "xuartns550_l.h" /* to use uartns550 */ #endif #ifdef XPAR_INTC_0_DEVICE_ID #include "xintc.h" #else #include "xscugic.h" #endif #include "xgpiops.h" /************************** Constant Definitions *****************************/ /* * Device hardware build related constants. */ #define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID #ifdef XPAR_AXI_7SDDR_0_S_AXI_BASEADDR #define DDR_BASE_ADDR XPAR_AXI_7SDDR_0_S_AXI_BASEADDR #elif XPAR_MIG7SERIES_0_BASEADDR #define DDR_BASE_ADDR XPAR_MIG7SERIES_0_BASEADDR #elif XPAR_MIG_0_BASEADDR #define DDR_BASE_ADDR XPAR_MIG_0_BASEADDR #elif XPAR_PSU_DDR_0_S_AXI_BASEADDR #define DDR_BASE_ADDR XPAR_PSU_DDR_0_S_AXI_BASEADDR #endif #ifndef DDR_BASE_ADDR #warning CHECK FOR THE VALID DDR ADDRESS IN XPARAMETERS.H, \ DEFAULT SET TO 0x01000000 #define MEM_BASE_ADDR 0x01000000 #else #define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) #endif #ifdef XPAR_INTC_0_DEVICE_ID #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 #else //#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID //#define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID #endif #define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) #define TX_BUFFER_BASE1 (MEM_BASE_ADDR + 0x00101000) #define TX_BUFFER_BASE2 (MEM_BASE_ADDR + 0x00102000) #define TX_BUFFER_BASE3 (MEM_BASE_ADDR + 0x00103000) #define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) #define RX_BUFFER_BASE1 (MEM_BASE_ADDR + 0x00301000) #define RX_BUFFER_BASE2 (MEM_BASE_ADDR + 0x00302000) #define RX_BUFFER_BASE3 (MEM_BASE_ADDR + 0x00303000) #define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF) #ifdef XPAR_INTC_0_DEVICE_ID #define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID #else #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID #endif #ifdef XPAR_INTC_0_DEVICE_ID #define INTC XIntc #define INTC_HANDLER XIntc_InterruptHandler #else #define INTC XScuGic #define INTC_HANDLER XScuGic_InterruptHandler #endif extern XAxiDma AxiDma; /* Instance of the XAxiDma */ extern XAxiDma AxiDma1; /* Instance of the XAxiDma */ extern XAxiDma AxiDma2; /* Instance of the XAxiDma */ extern XAxiDma AxiDma3; /* Instance of the XAxiDma */ /* Timeout loop counter for reset */ #define RESET_TIMEOUT_COUNTER 10000 #define TEST_START_VALUE 0xC /* * Buffer and Buffer Descriptor related constant definition */ #define MAX_PKT_LEN 0x100 #define NUMBER_OF_TRANSFERS 10 static XGpioPs Gpio; /* The Instance of the GPIO Driver */ /* The interrupt coalescing threshold and delay timer threshold * Valid range is 1 to 255 * * We set the coalescing threshold to be the total number of packets. * The receive side will only get one completion interrupt for this example. */ /************* Type Definitions ******************/ /***** Macros (Inline Functions) Definitions **********/ /************* Function Prototypes **************/ #ifndef DEBUG extern void xil_printf(const char *format, ...); #endif #ifdef XPAR_UARTNS550_0_BASEADDR void Uart550_Setup(void); #endif static int CheckData(int Length, u8 StartValue); //static void TxIntrHandler(void *Callback); //static void RxIntrHandler(void *Callback); int dma_init(void); int dma_8_10b_send(XAxiDma* AxiDmaIns,u8 *wBuffer , unsigned int length); unsigned int dma_8_10b_recv(XAxiDma* AxiDmaIns,u8 *rBuffer); static int SetupIntrSystem(INTC * IntcInstancePtr, XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId); static void DisableIntrSystem(INTC * IntcInstancePtr, u16 TxIntrId, u16 RxIntrId); int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId); static void TxIntrHandler(AxiDma); static void RxIntrHandler(AxiDma); /************** Variable Definitions ****************/ /* * Device instance definitions */ static INTC Intc; /* Instance of the Interrupt Controller */ /* * Flags interrupt handlers use to notify the application context the events. */ volatile int TxDone; volatile int DMA0RxDone; volatile int DMA1RxDone; volatile int DMA2RxDone; volatile int DMA3RxDone; volatile int Error; #endif /* SRC_XDMA_DRIVER_H_ */
驱动文件如下
#include "xdma_driver.h" XAxiDma AxiDma; /* Instance of the XAxiDma */ XAxiDma AxiDma1; /* Instance of the XAxiDma */ XAxiDma AxiDma2; /* Instance of the XAxiDma */ XAxiDma AxiDma3; /* Instance of the XAxiDma */ #ifdef XPAR_UARTNS550_0_BASEADDR /*****************************************************************************/ /* * * Uart16550 setup routine, need to set baudrate to 9600 and data bits to 8 * * @param None * * @return None * * @note None. * ******************************************************************************/ extern void Uart550_Setup(void) { XUartNs550_SetBaud(XPAR_UARTNS550_0_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ, 9600); XUartNs550_SetLineControlReg(XPAR_UARTNS550_0_BASEADDR, XUN_LCR_8_DATA_BITS); } #endif /*****************************************************************************/ /* * * 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 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((UINTPTR)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; } /*****************************************************************************/ /* * * 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 TxIntrHandler(void *Callback) static void TxIntrHandler(XAxiDma *AxiDma) { u32 IrqStatus; int TimeOut; //XAxiDma *AxiDmaInst = (XAxiDma *)Callback; /* Read pending interrupts */ IrqStatus = XAxiDma_IntrGetIrq(AxiDma, XAXIDMA_DMA_TO_DEVICE); /* Acknowledge pending interrupts */ XAxiDma_IntrAckIrq(AxiDma, 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(AxiDma); TimeOut = RESET_TIMEOUT_COUNTER; while (TimeOut) { if (XAxiDma_ResetIsDone(AxiDma)) { 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 RxIntrHandler(void *Callback) static void RxIntrHandler(XAxiDma *AxiDmaIns ) { u32 IrqStatus; int TimeOut; //XAxiDma *AxiDmaInst = (XAxiDma *)Callback; /* Read pending interrupts */ IrqStatus = XAxiDma_IntrGetIrq(AxiDmaIns, XAXIDMA_DEVICE_TO_DMA); /* Acknowledge pending interrupts */ XAxiDma_IntrAckIrq(AxiDmaIns, 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(AxiDmaIns); TimeOut = RESET_TIMEOUT_COUNTER; while (TimeOut) { if(XAxiDma_ResetIsDone(AxiDmaIns)) { break; } TimeOut -= 1; } return; } /* * If completion interrupt is asserted, then set RxDone flag */ if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) { if(AxiDmaIns == &AxiDma) { DMA0RxDone = 1; } else if(AxiDmaIns == &AxiDma1) { DMA1RxDone = 1; } else if(AxiDmaIns == &AxiDma2) { DMA2RxDone = 1; } else if(AxiDmaIns == &AxiDma3) { DMA3RxDone = 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. * ******************************************************************************/ static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status) { XGpioPs *Gpio = (XGpioPs *)CallBackRef; u32 DataRead = 0;; /* Push the switch button */ //DataRead = XGpioPs_ReadPin(Gpio, Input_Pin); DataRead = XGpioPs_Read(Gpio, 2); xil_printf("0x%x IntrHandler\r\n",DataRead); if ((DataRead & 0x1) == 0x01) { TxIntrHandler(&AxiDma); } if((DataRead & 0x2) == 0x02) { RxIntrHandler(&AxiDma); } if ((DataRead & 0x4) == 0x04) { TxIntrHandler(&AxiDma1); // xil_printf("data 4t\r\n"); } if((DataRead & 0x8) == 0x08) { RxIntrHandler(&AxiDma1); // xil_printf("data 4r\r\n"); } if ((DataRead & 0x10) == 0x010) { TxIntrHandler(&AxiDma2); } if((DataRead & 0x20) == 0x20) { RxIntrHandler(&AxiDma2); } if ((DataRead & 0x40) == 0x40) { TxIntrHandler(&AxiDma3); } if((DataRead & 0x80) == 0x080) { RxIntrHandler(&AxiDma3); } else { } } /*****************************************************************************/ /** * * This function sets up the interrupt system for the example. It enables falling * edge interrupts for all the pins of bank 0 in the GPIO device. * * @param GicInstancePtr is a pointer to the XScuGic driver Instance. * @param GpioInstancePtr contains a pointer to the instance of the GPIO * component which is going to be connected to the interrupt * controller. * @param GpioIntrId is the interrupt Id and is typically * XPAR_<GICPS>_<GPIOPS_instance>_VEC_ID value from * xparameters.h. * * @return XST_SUCCESS if successful, otherwise XST_FAILURE. * * @note None. * ****************************************************************************/ int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio, u16 GpioIntrId) { xil_printf("SetupInterruptSystem\r\n"); int Status; XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */ Xil_ExceptionInit(); /* * Initialize the interrupt controller driver so that it is ready to * use. */ IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); if (NULL == IntcConfig) { return XST_FAILURE; } Status = XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* * Connect the interrupt controller interrupt handler to the hardware * interrupt handling logic in the processor. */ Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstancePtr); /* * Connect the device driver handler that will be called when an * interrupt for the device occurs, the handler defined above performs * the specific interrupt processing for the device. */ Status = XScuGic_Connect(GicInstancePtr, GpioIntrId, (Xil_ExceptionHandler)XGpioPs_IntrHandler, (void *)Gpio); if (Status != XST_SUCCESS) { return Status; } /* Enable falling edge interrupts for all the pins in bank 0. */ XGpioPs_SetIntrType(Gpio, 2, 0x00, 0xFFFFFFFF, 0x00); /* Set the handler for gpio interrupts. */ XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler); /* Enable the GPIO interrupts of Bank 2. */ XGpioPs_IntrEnable(Gpio, 2, 0xFF); /* Enable the interrupt for the GPIO device. */ XScuGic_Enable(GicInstancePtr, GpioIntrId); /* Enable interrupts in the Processor. */ Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); return XST_SUCCESS; } /*****************************************************************************/ /** * * 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. * ******************************************************************************/ static void DisableIntrSystem(INTC * 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 } int dma_init(void) { int Status; XAxiDma_Config *Config; XAxiDma_Config *Config1; XAxiDma_Config *Config2; XAxiDma_Config *Config3; XGpioPs_Config *ConfigPtr; u8 *RxBufferPtr = (u8 *)RX_BUFFER_BASE; u8 *RxBufferPtr1 = (u8 *)RX_BUFFER_BASE1; u8 *RxBufferPtr2 = (u8 *)RX_BUFFER_BASE2; u8 *RxBufferPtr3 = (u8 *)RX_BUFFER_BASE3; memset(RxBufferPtr,0,MAX_PKT_LEN); memset(RxBufferPtr1,0,MAX_PKT_LEN); memset(RxBufferPtr2,0,MAX_PKT_LEN); memset(RxBufferPtr3,0,MAX_PKT_LEN); xil_printf("\r\n--- Entering dma init() --- \r\n"); Config = XAxiDma_LookupConfig(0); if (!Config) { xil_printf("No config found for 0\r\n"); return XST_FAILURE; } Config1 = XAxiDma_LookupConfig(1); if (!Config1) { xil_printf("No config found for 1\r\n"); return XST_FAILURE; } Config2 = XAxiDma_LookupConfig(2); if (!Config2) { xil_printf("No config found for 2\r\n"); return XST_FAILURE; } Config3 = XAxiDma_LookupConfig(3); if (!Config3) { xil_printf("No config found for 3\r\n"); return XST_FAILURE; } /* Initialize DMA engine */ Status = XAxiDma_CfgInitialize(&AxiDma, Config); if (Status != XST_SUCCESS) { xil_printf("Initialization 0 failed %d\r\n", Status); return XST_FAILURE; } Status = XAxiDma_CfgInitialize(&AxiDma1, Config1); if (Status != XST_SUCCESS) { xil_printf("Initialization 1 failed %d\r\n", Status); return XST_FAILURE; } Status = XAxiDma_CfgInitialize(&AxiDma2, Config2); if (Status != XST_SUCCESS) { xil_printf("Initialization 2 failed %d\r\n", Status); return XST_FAILURE; } Status = XAxiDma_CfgInitialize(&AxiDma3, Config3); if (Status != XST_SUCCESS) { xil_printf("Initialization 3 failed %d\r\n", Status); return XST_FAILURE; } /* Initialize the Gpio driver. */ ConfigPtr = XGpioPs_LookupConfig(0); if (ConfigPtr == NULL) { return XST_FAILURE; } XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr); /* Set the direction for the specified pin to be input */ XGpioPs_SetDirectionPin(&Gpio,54,0); XGpioPs_SetDirectionPin(&Gpio,55,0); XGpioPs_SetDirectionPin(&Gpio,56,0); XGpioPs_SetDirectionPin(&Gpio,57,0); XGpioPs_SetDirectionPin(&Gpio,58,0); XGpioPs_SetDirectionPin(&Gpio,59,0); XGpioPs_SetDirectionPin(&Gpio,60,0); XGpioPs_SetDirectionPin(&Gpio,61,0); /* * Setup the interrupts such that interrupt processing can occur. If * an error occurs then exit. */ Status = SetupInterruptSystem(&Intc, &Gpio, XPAR_XGPIOPS_0_INTR); if (Status != XST_SUCCESS) { return XST_FAILURE; } XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrDisable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrDisable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrEnable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(&AxiDma1, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrDisable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrDisable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrEnable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(&AxiDma2, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrDisable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrDisable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrEnable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(&AxiDma3, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); /**************************************************************************/ // Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x58); // xil_printf(" RX length is 0x%x\r\n",Status); // Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x28); // xil_printf("TX length is 0x%x\r\n",Status); /**************************************************************************/ Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); if (Status != XST_SUCCESS) { return XST_FAILURE; } Status = XAxiDma_SimpleTransfer(&AxiDma1,(UINTPTR) RxBufferPtr1, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); if (Status != XST_SUCCESS) { return XST_FAILURE; } Status = XAxiDma_SimpleTransfer(&AxiDma2,(UINTPTR) RxBufferPtr2, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); if (Status != XST_SUCCESS) { return XST_FAILURE; } Status = XAxiDma_SimpleTransfer(&AxiDma3,(UINTPTR) RxBufferPtr3, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* Initialize flags before start transfer test */ TxDone = 0; DMA0RxDone = 0; DMA1RxDone = 0; DMA2RxDone = 0; DMA3RxDone = 0; Error = 0; } int dma_8_10b_send(XAxiDma* AxiDmaIns,u8 *wBuffer , unsigned int length) { int Status; u8 *TxBufferPtr; if((AxiDmaIns == NULL)||(wBuffer == NULL)) { xil_printf("send Pointer err\r\n"); return XST_FAILURE; } if (AxiDmaIns == &AxiDma) TxBufferPtr = (u8 *)TX_BUFFER_BASE ; else if(AxiDmaIns == &AxiDma1) TxBufferPtr = (u8 *)TX_BUFFER_BASE1 ; else if(AxiDmaIns == &AxiDma2) TxBufferPtr = (u8 *)TX_BUFFER_BASE2 ; else if(AxiDmaIns == &AxiDma3) TxBufferPtr = (u8 *)TX_BUFFER_BASE3 ; else { xil_printf("para err\r\n"); return XST_FAILURE; } memset(TxBufferPtr,0,length); memcpy(TxBufferPtr,wBuffer,length); Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN); Status = XAxiDma_SimpleTransfer(AxiDmaIns,(UINTPTR) TxBufferPtr, length, XAXIDMA_DMA_TO_DEVICE); // xil_printf("status is %d",Status); if (Status != XST_SUCCESS) { return XST_FAILURE; } return XST_SUCCESS; } unsigned int dma_8_10b_recv(XAxiDma* AxiDmaIns,u8 *rBuffer) { int Status; int i; int Length = 0 ; u8 *RxBufferPtr; Length = XAxiDma_ReadReg(AxiDmaIns->RegBase,0x58); if((AxiDmaIns == NULL)||(rBuffer == NULL)) { xil_printf("recv Pointer err\r\n"); return XST_FAILURE; } if (AxiDmaIns == &AxiDma) RxBufferPtr = (u8 *)RX_BUFFER_BASE ; else if(AxiDmaIns == &AxiDma1) RxBufferPtr = (u8 *)RX_BUFFER_BASE1 ; else if(AxiDmaIns == &AxiDma2) RxBufferPtr = (u8 *)RX_BUFFER_BASE2 ; else if(AxiDmaIns == &AxiDma3) RxBufferPtr = (u8 *)RX_BUFFER_BASE3 ; else { xil_printf("para err\r\n"); return XST_FAILURE; } Xil_DCacheInvalidateRange(RxBufferPtr,MAX_PKT_LEN); for(i = 0; i < Length; i++) { rBuffer[i] = RxBufferPtr[i]; } Status = XAxiDma_SimpleTransfer(AxiDmaIns,(UINTPTR) RxBufferPtr, MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA); if (Status != XST_SUCCESS) { return XST_FAILURE; } return Length; }
Demo部分如下
void Xdma_demo() { int Status; int Length; u8 rBuffer[256] = {0}; u8 wBuffer[256] = {0}; int i; dma_init(); for(i=0;i<256;i++) { wBuffer[i]=i+1; } dma_8_10b_send(&AxiDma,wBuffer,4); for(i=0;i<256;i++) { wBuffer[i]=i+2; } dma_8_10b_send(&AxiDma1,wBuffer , 16); for(i=0;i<256;i++) { wBuffer[i]=i+3; } dma_8_10b_send(&AxiDma2,wBuffer , 32); for(i=0;i<256;i++) { wBuffer[i]=i+4; } dma_8_10b_send(&AxiDma3,wBuffer , 32); while(1) { if(DMA0RxDone == 1) { // Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x58); // xil_printf("length2 is 0x%x\r\n",Status); // Status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR,0x28); // xil_printf("TX length2 is 0x%x\r\n",Status); Length = dma_8_10b_recv(&AxiDma,rBuffer); xil_printf("length DMA0 is 0x%x\r\n",Length); xil_printf("DATE: "); for(i=0; i<33; i++) { xil_printf("%x ",rBuffer[i]); } xil_printf("\r\n"); DMA0RxDone = 0; } if(DMA1RxDone == 1) { Length = dma_8_10b_recv(&AxiDma1,rBuffer); xil_printf("length DMA1 is 0x%x\r\n",Length); xil_printf("DATE: "); for(i=0; i<33; i++) { xil_printf("%x ",rBuffer[i]); } xil_printf("\r\n"); DMA1RxDone = 0; } if(DMA2RxDone == 1) { dma_8_10b_recv(&AxiDma2,rBuffer); xil_printf("DATE: "); for(i=0; i<33; i++) { xil_printf("%x ",rBuffer[i]); } xil_printf("\r\n"); DMA2RxDone = 0; } if(DMA3RxDone == 1) { dma_8_10b_recv(&AxiDma3,rBuffer); xil_printf("DATE: "); for(i=0; i<33; i++) { xil_printf("%x ",rBuffer[i]); } xil_printf("\r\n"); DMA3RxDone = 0; } } }
dma_8_10b_send()为封装后的发送函数,将宏定义的四路TXBUFFER和DMA匹配,实现DMA向设备发送数据的功能,并通过Xil_DCacheFlushRange()函数清理缓存
dma_8_10b_recv()为封装后的接收函数,将宏定义的四路RXBUFFER和DMA匹配,实现DMA接收设备发送的数据的功能,数据长度通过读取DMA长度寄存器获得,这样的话更具有普遍性,同样通过Xil_DCacheFlushRange()函数清理缓存
XAxiDma_SimpleTransfer是其原型,最后一个参数表示了传输方向收发的处理函数基本上是AXIDMA例程里的,仅对多余的接收完成标志位做了补充(多余的应该没了,也能在导入一个工程比较一下)哦对了很奇怪的一点是初始化的时候需要提前做一次接受的transfer,还没发现原因,在之后的挂载操作系统时也有一次类似的操作,当然linux下的transfer不是官方提供的,可能和裸板有些许差异,而且不提前做也没法从0x58的长度寄存器读取实际搬移数据的长度,另说了
初始化部分是结合了之前章节讲的GPIO中断,没有用例程中的中断方式
先写这么多了,目前只了解到这些,SG模式的用法后续有机会更新吧
参考:芯片手册 原子开发手册 CSDN Xilinx社区等网络资源