前言
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模式的用法后续有机会更新吧
学习zynq时间较短,认知有限,代码也比较简陋。如有错误欢迎批评指正。影响阅读的图片水印也已去掉欢迎大家收藏
参考:芯片手册 原子开发手册 CSDN Xilinx社区等网络资源