前言
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例程里的,僅對多余的接收完成標志位做了補充(多余的應該沒了,也能在導入一個工程比較一下)
初始化部分是結合了之前章節講的GPIO中斷,沒有用例程中的中斷方式
先寫這么多了,目前只了解到這些,SG模式的用法后續有機會更新吧
學習zynq時間較短,認知有限,代碼也比較簡陋。如有錯誤歡迎批評指正。影響閱讀的圖片水印也已去掉歡迎大家收藏
參考:芯片手冊 原子開發手冊 CSDN Xilinx社區等網絡資源