ZYNQ裸板簡單實戰-AXIDMA篇(簡單模式)


前言

  DMA(Direct Memory Access,直接存儲器訪問)是計算機科學中的一種內存訪問技術。它允許某些計算機內部的硬件子系統可以獨立地直接讀寫系統內存,而不需中央處理器( CPU)介入處理。 DMA 是一種快速的數據傳送方式, 通常用來傳送數據量較多的數據塊
  使用 DMA時, CPU 向 DMA 控制器發出一個存儲傳輸請求, 這樣當 DMA 控制器在傳輸的時候, CPU 執行其它操作,傳輸操作完成時 DMA 以中斷的方式通知 CPU。
  為了發起傳輸事務, DMA 控制器必須得到以下數據:
• 源地址 — 數據被讀出的地址
• 目的地址 — 數據被寫入的地址
• 傳輸長度 — 應被傳輸的字節數
在這里插入圖片描述
DMA 存儲傳輸的過程如下:

  1. 為了配置用 DMA 傳輸數據到存儲器,處理器發出一條 DMA 命令
  2. DMA 控制器把數據從外設傳輸到存儲器或從存儲器到存儲器,而讓 CPU 騰出手來做其它操作。
  3. 數據傳輸完成后,向 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例程里的,僅對多余的接收完成標志位做了補充(多余的應該沒了,也能在導入一個工程比較一下)
  初始化部分是結合了之前章節講的GPIO中斷,沒有用例程中的中斷方式

先寫這么多了,目前只了解到這些,SG模式的用法后續有機會更新吧
學習zynq時間較短,認知有限,代碼也比較簡陋。如有錯誤歡迎批評指正。影響閱讀的圖片水印也已去掉歡迎大家收藏
參考:芯片手冊 原子開發手冊 CSDN Xilinx社區等網絡資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM