Learn ZYNC (2)
AXI HP接口的DMA+GIC编程(参照博客)
参考设计代码文件:ug873源码
Vivado 接线图:
地址分配:
AXI-CDMA IP核,是由PL完成的将数据从内存的一个位置搬移到另一个位置,无需CPU来插手。
AXI CDMA 从机接口连接到PS 通用主机接口M_AXI_GP1.用于PS 来配置AXI 寄存器用于数据传输和状态检测。
AXI CDMA 主机接口连接到PS 高性能从机接口S_AXI_HP0.用于CDMA 模块读取DDR 系统内存源缓冲区数据。
AXI CDMA 主机接口连接到PS 高性能从机接口S_AXI_HP2.用于CDMA 模块将数据写回DDR 系统内存目的缓冲区。
AXI CDMA 中断从PL 连接到PS 全局中断控制器(GIC)。当数据传输完成或传输时有错误发生,则产生中断。
standalone代码:
#include <stdio.h> //#include "platform.h" #include "xaxicdma.h" #include "xdebug.h" #include "xil_exception.h" #include "xil_cache.h" #include "xparameters.h" #include "xscugic.h" //包含头文件,不必多言 #define NUMBER_OF_TRANSFERS 2 /* 做两次传输 */ #define DMA_CTRL_DEVICE_ID XPAR_AXICDMA_0_DEVICE_ID //DMA控制设备ID号= #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //GIC控制设备ID号= #define DMA_CTRL_IRPT_INTR XPAR_FABRIC_AXI_CDMA_0_CDMA_INTROUT_INTR //中断号 volatile static int Done = 0; /* Dma transfer is done */ volatile static int Error = 0; /* Dma Bus Error occurs */ static u32 SourceAddr = 0x10000000; // XPAR_PS7_DDR_0_S_AXI_HP0_BASEADDR static u32 DestAddr = 0x18000000; // XPAR_PS7_DDR_0_S_AXI_HP2_BASEADDR static XAxiCdma AxiCdmaInstance; /* XAxiCdma 控制对象*/ static XScuGic IntcController; /* Interrupt Controller 控制对象*/ static int Array_3[32][16]; //阵列 static int Array_4[32][16]; static int Array_1[32][16]; static int Array_2[32][16]; int const input[16] = {0xb504f33, 0xabeb4a0, 0xa267994, 0x987fbfc, 0x8e39d9c, 0x839c3cc, 0x78ad74c, 0x6d743f4, 0x61f78a8, 0x563e6a8, 0x4a5018c, 0x3e33f2c, 0x31f1704, 0x259020c, 0x1917a64, 0xc8fb2c}; /* Length of the buffers for DMA transfer */ static u32 BUFFER_BYTESIZE = (XPAR_AXI_CDMA_0_M_AXI_DATA_WIDTH * XPAR_AXI_CDMA_0_M_AXI_MAX_BURST_LEN); //大小为64*256/8=2KB,起始就是我们在XPS中设置好的 static int CDMATransfer(XAxiCdma *InstancePtr, int Length, int Retries);//执行传输 static void DisableIntrSystem(XScuGic *IntcInstancePtr , u32 IntrId)//禁止中断 { XScuGic_Disable(IntcInstancePtr ,IntrId ); XScuGic_Disconnect(IntcInstancePtr ,IntrId ); } //Multiply and Shift int MUL_SHIFT_30(int x, int y)//实现x*y,但又怕溢出,所以右移30位,除以2^30~1e9 { return ((int) (((long long) (x) * (y)) >> 30)); } void MULT_SHIFT_LOOP(int Value )//生成源数据的函数 { int i, j; for (i = 0; i < 32; i++) { for (j = 0; j < 16; j++) { Array_3[i][j] = (int)((MUL_SHIFT_30(input[j],Array_1[j][i])) + Value); Array_4[i][j] = (int)((MUL_SHIFT_30(input[j],Array_2[j][i])) + Value); } } } void TestPattern_Initialization(void)//生成原始数据 { int i, j; for (i = 0; i < 32; i++) { for (j = 0; j < 16; j++) { Array_1[i][j] = (int ) ((0xA5A5A5A5 >> 1) * i ); Array_2[i][j] = (int ) ((0xA5A5A5A5 << 1) * i ); } } } //下面是中断服务函数 static void Cdma_CallBack(void *CallBackRef, u32 IrqMask, int *IgnorePtr) { if (IrqMask & XAXICDMA_XR_IRQ_ERROR_MASK) { Error = TRUE; printf("\r\n--- Transfer Error --- \r\n");//传输出错 } if (IrqMask & XAXICDMA_XR_IRQ_IOC_MASK) { printf("\r\n--- Transfer Done --- \r\n"); Done = TRUE;//传输成功 } } //设置中断系统,初始化中断控制器 static int SetupIntrSystem(XScuGic *IntcInstancePtr, XAxiCdma *InstancePtr, u32 IntrId) { int Status; /* * Initialize the interrupt controller driver */ XScuGic_Config *IntcConfig; /* * 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(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId, 0xA0, 0x3); /* * 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(IntcInstancePtr, IntrId, (Xil_InterruptHandler)XAxiCdma_IntrHandler, InstancePtr); if (Status != XST_SUCCESS) { return Status; } /* * Enable the interrupt for the DMA device. */ XScuGic_Enable(IntcInstancePtr, IntrId); Xil_ExceptionInit(); /* * Connect the interrupt controller interrupt handler to the hardware * interrupt handling logic in the processor. */ Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstancePtr); /* * Enable interrupts in the Processor. */ Xil_ExceptionEnable(); return XST_SUCCESS; } int XAxiCdma_Interrupt(XScuGic *IntcInstancePtr, XAxiCdma *InstancePtr, u16 DeviceId, u32 IntrId) { { XAxiCdma_Config *CfgPtr; int Status; int SubmitTries = 1; /* Retry to submit */ int Tries = NUMBER_OF_TRANSFERS; int Index; /* Initialize the XAxiCdma device. */ CfgPtr = XAxiCdma_LookupConfig(DeviceId); if (!CfgPtr) { return XST_FAILURE; } Status = XAxiCdma_CfgInitialize(InstancePtr, CfgPtr, CfgPtr->BaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* Setup the interrupt system */ Status = SetupIntrSystem(IntcInstancePtr, InstancePtr, IntrId); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* Enable all (completion/error/delay) interrupts */ XAxiCdma_IntrEnable(InstancePtr, XAXICDMA_XR_IRQ_ALL_MASK); for (Index = 0; Index < Tries; Index++) { Status = CDMATransfer(InstancePtr, BUFFER_BYTESIZE, SubmitTries); if(Status != XST_SUCCESS) { DisableIntrSystem(IntcInstancePtr, IntrId); return XST_FAILURE; } } /* Test finishes successfully, clean up and return */ DisableIntrSystem(IntcInstancePtr, IntrId); return XST_SUCCESS; } } /*****************************************************************************/ /* * * This function does CDMA transfer * * @param InstancePtr is a pointer to the XAxiCdma instance * @param Length is the transfer length * @param Retries is how many times to retry on submission * * @return * - XST_SUCCESS if transfer is successful * - XST_FAILURE if either the transfer fails or the data has * error * * @note None * ******************************************************************************/ static int CDMATransfer(XAxiCdma *InstancePtr, int Length, int Retries) { int Status; Done = 0; Error = 0; printf("Start Transfer \n\r"); /* Try to start the DMA transfer */ Done = 0; Error = 0; /* Flush the SrcBuffer before the DMA transfer, in case the Data Cache * is enabled */ Xil_DCacheFlushRange((u32)SourceAddr, Length); Status = XAxiCdma_SimpleTransfer(InstancePtr, (u32)(u8 *) (SourceAddr ), (u32)(DestAddr), Length, Cdma_CallBack, (void *)InstancePtr); if (Status == XST_FAILURE) { printf("Error in Transfer \n\r"); return 1; } /* Wait until the DMA transfer is done */ while (!Done && !Error) { /* Wait */ } if (Error) { return XST_FAILURE; return 1; } /* Invalidate the DestBuffer before receiving the data, in case the * Data Cache is enabled */ Xil_DCacheInvalidateRange((u32)DestAddr, Length); return XST_SUCCESS; } int main() { int Status; u32 *SrcPtr; u32 *DestPtr; u32 Index; int i, j; printf("\r\n--- Entering main() --- \r\n"); /********************************************************************************* Step : 1 : Intialization of the SOurce Memory with the Specified test pattern Clear Destination memory **********************************************************************************/ TestPattern_Initialization(); /* Initialize the source buffer bytes with a pattern and the * the destination buffer bytes to zero */ SrcPtr = (u32*)SourceAddr; DestPtr = (u32 *)DestAddr; for (Index = 0; Index < (BUFFER_BYTESIZE/1024); Index++) { MULT_SHIFT_LOOP((Index*100)); for (i = 0; i < 32; i++) { for (j = 0; j < 16; j++) { SrcPtr[((i+j))*(Index+1)] = Array_3[i][j]; SrcPtr[((i+j)*(Index+1)) + 1] = Array_4[i][j]; DestPtr[(i+j)*(Index+1)] = 0; DestPtr[((i+j)*(Index+1)) + 1] = 0; } } } /********************************************************************************* Step : 2 : AXI CDMA Intialization Association of the CDMA ISR with the Interrupt Enable the CDMA Interrupt Provide Source and destination location to CDMA Specified Number of byte to be transfer to CDMA register Start the CDMA Wait for the Interrupt and return the status **********************************************************************************/ Status = XAxiCdma_Interrupt( &IntcController, &AxiCdmaInstance, DMA_CTRL_DEVICE_ID, DMA_CTRL_IRPT_INTR ); if (Status != XST_SUCCESS) { printf("XAxiCdma_Interrupt: Failed\r\n"); return XST_FAILURE; } xil_printf("XAxiCdma_Interrupt: Passed\r\n"); /********************************************************************************* Step : 3 : Compare Source memory with Destination memory Return the Status **********************************************************************************/ for (Index = 0; Index < (BUFFER_BYTESIZE/4); Index++) { if ( DestPtr[Index] != SrcPtr[Index]) { printf("Error in Comparison : Index : %x \n\r", Index); return XST_FAILURE; } } printf("DMA Transfer is Successful \n\r"); return XST_SUCCESS; return 0; }
Linux 驱动程序代码参考:xilinx_axicdma.c
Linux DeviceTree参考:axi-cdma.txt
Linux app程序代码(已完成,2014.6.5更新):
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #define CDMA_BASE_ADDRESS 0x80200000 #define GPIO_DATA_OFFSET 0 #define GPIO_DIRECTION_OFFSET 4 //此处要修改为我们的总线地址**************************** #define DDR_BASE_ADDRESS 0x10000000 #define DDR_BASE_WRITE_ADDRESS 0x18000000 #define XGPIO_CHAN_OFFSET 8 #define XAXICDMA_CR_OFFSET 0x00000000 /**< Control register */ #define XAXICDMA_SR_OFFSET 0x00000004 /**< Status register */ #define XAXICDMA_CDESC_OFFSET 0x00000008 /**< Current descriptor pointer */ #define XAXICDMA_TDESC_OFFSET 0x00000010 /**< Tail descriptor pointer */ #define XAXICDMA_SRCADDR_OFFSET 0x00000018 /**< Source address register */ #define XAXICDMA_DSTADDR_OFFSET 0x00000020 /**< Destination address register */ #define XAXICDMA_BTT_OFFSET 0x00000028 /**< Bytes to transfer */ /** @name Bitmasks of XAXICDMA_CR_OFFSET register * @{ */ #define XAXICDMA_CR_RESET_MASK 0x00000004 /**< Reset DMA engine */ #define XAXICDMA_CR_SGMODE_MASK 0x00000008 /**< Scatter gather mode */ /** @name Bitmask for interrupts * These masks are shared by XAXICDMA_CR_OFFSET register and * XAXICDMA_SR_OFFSET register * @{ */ #define XAXICDMA_XR_IRQ_IOC_MASK 0x00001000 /**< Completion interrupt */ #define XAXICDMA_XR_IRQ_DELAY_MASK 0x00002000 /**< Delay interrupt */ #define XAXICDMA_XR_IRQ_ERROR_MASK 0x00004000 /**< Error interrupt */ #define XAXICDMA_XR_IRQ_ALL_MASK 0x00007000 /**< All interrupts */ #define XAXICDMA_XR_IRQ_SIMPLE_ALL_MASK 0x00005000 /**< All interrupts for simple only mode */ /*@}*/ /** @name Bitmasks of XAXICDMA_SR_OFFSET register * This register reports status of a DMA channel, including * idle state, errors, and interrupts * @{ */ #define XAXICDMA_SR_IDLE_MASK 0x00000002 /**< DMA channel idle */ #define XAXICDMA_SR_SGINCLD_MASK 0x00000008 /**< Hybrid build */ #define XAXICDMA_SR_ERR_INTERNAL_MASK 0x00000010 /**< Datamover internal err */ #define XAXICDMA_SR_ERR_SLAVE_MASK 0x00000020 /**< Datamover slave err */ #define XAXICDMA_SR_ERR_DECODE_MASK 0x00000040 /**< Datamover decode err */ #define XAXICDMA_SR_ERR_SG_INT_MASK 0x00000100 /**< SG internal err */ #define XAXICDMA_SR_ERR_SG_SLV_MASK 0x00000200 /**< SG slave err */ #define XAXICDMA_SR_ERR_SG_DEC_MASK 0x00000400 /**< SG decode err */ #define XAXICDMA_SR_ERR_ALL_MASK 0x00000770 /**< All errors */ /*@}*/ #define MAP_SIZE 4096UL #define MAP_MASK (MAP_SIZE - 1) #define DDR_MAP_SIZE 0x10000000 #define DDR_MAP_MASK (DDR_MAP_SIZE - 1) #define DDR_WRITE_OFFSET 0x10000000 #define BUFFER_BYTESIZE 262144 // Length of the buffers for DMA transfer int main() { int memfd; void *mapped_base, *mapped_dev_base; off_t dev_base = CDMA_BASE_ADDRESS; int memfd_1; void *mapped_base_1, *mapped_dev_base_1; off_t dev_base_1 = DDR_BASE_ADDRESS; int memfd_2; void *mapped_base_2, *mapped_dev_base_2; off_t dev_base_2 = DDR_BASE_WRITE_ADDRESS; unsigned int TimeOut =5; unsigned int ResetMask; unsigned int RegValue; unsigned int SrcArray[BUFFER_BYTESIZE ]; unsigned int DestArray[BUFFER_BYTESIZE ]; unsigned int Index; /*STEP 1 : Initialize the source buffer bytes with a pattern and clear the Destination location =====================================*/ for (Index = 0; Index < (BUFFER_BYTESIZE/2); Index++) { SrcArray[Index] = 0x5A5A5A5A/*Index & 0xFF*/; DestArray[Index] = 0; } /*STEP 2 : Map the kernel memory location starting from 0x10000000 to the User layer =======================================*/ memfd_1 = open("/dev/mem", O_RDWR | O_SYNC); if (memfd_1 == -1) { printf("Can't open /dev/mem.\n"); exit(0); } printf("/dev/mem opened.\n"); // Map one page of memory into user space such that the device is in that page, but it may not // be at the start of the page. mapped_base_1 = mmap(0, DDR_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_1, dev_base_1 & ~DDR_MAP_MASK); if (mapped_base_1 == (void *) -1) { printf("Can't map the memory to user space.\n"); exit(0); } printf("Memory mapped at address %p.\n", mapped_base_1); // get the address of the device in user space which will be an offset from the base // that was mapped as memory is mapped at the start of a page mapped_dev_base_1 = mapped_base_1 + (dev_base_1 & DDR_MAP_MASK); /*STEP 3 : Copy the Data to the DDR Memory at location 0x10000000 ========================================*/ memcpy(mapped_dev_base_1, SrcArray, (BUFFER_BYTESIZE)); /* STEP 4 : Un-map the kernel memory from the User layer. ===========================================*/ if (munmap(mapped_base_1, DDR_MAP_SIZE) == -1) { printf("Can't unmap memory from user space.\n"); exit(0); } close(memfd_1); /*==STEP 5 : Map the AXI CDMA Register memory to the User layer Do the Register Setting for DMA transfer =================================================*/ memfd = open("/dev/mem", O_RDWR | O_SYNC); if (memfd == -1) { printf("Can't open /dev/mem.\n"); exit(0); } printf("/dev/mem opened.\n"); // Map one page of memory into user space such that the device is in that page, but it may not // be at the start of the page. mapped_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, dev_base & ~MAP_MASK); if (mapped_base == (void *) -1) { printf("Can't map the memory to user space.\n"); exit(0); } // get the address of the device in user space which will be an offset from the base // that was mapped as memory is mapped at the start of a page mapped_dev_base = mapped_base + (dev_base & MAP_MASK); //Reset CDMA do{ ResetMask = (unsigned long )XAXICDMA_CR_RESET_MASK; *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_CR_OFFSET)) = (unsigned long)ResetMask; /* If the reset bit is still high, then reset is not done */ ResetMask = *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_CR_OFFSET)); if(!(ResetMask & XAXICDMA_CR_RESET_MASK)) { break; } TimeOut -= 1; }while (TimeOut); //enable Interrupt RegValue = *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_CR_OFFSET)); RegValue = (unsigned long)(RegValue | XAXICDMA_XR_IRQ_ALL_MASK ); *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_CR_OFFSET)) = (unsigned long)RegValue; // Checking for the Bus Idle RegValue = *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_SR_OFFSET)); if(!(RegValue & XAXICDMA_SR_IDLE_MASK)) { printf("BUS IS BUSY Error Condition \n\r"); return 1; } // Check the DMA Mode and switch it to simple mode RegValue = *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_CR_OFFSET)); if((RegValue & XAXICDMA_CR_SGMODE_MASK)) { RegValue = (unsigned long)(RegValue & (~XAXICDMA_CR_SGMODE_MASK)); printf("Reading \n \r"); *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_CR_OFFSET)) = (unsigned long)RegValue ; } //Set the Source Address *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_SRCADDR_OFFSET)) = (unsigned long)DDR_BASE_ADDRESS; //Set the Destination Address *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_DSTADDR_OFFSET)) = (unsigned long)DDR_BASE_WRITE_ADDRESS; RegValue = (unsigned long)(BUFFER_BYTESIZE); // write Byte to Transfer *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_BTT_OFFSET)) = (unsigned long)RegValue; /*STEP 6 : Wait for the DMA transfer Status =====================================*/ do { RegValue = *((volatile unsigned long *) (mapped_dev_base + XAXICDMA_SR_OFFSET)); }while(!(RegValue & XAXICDMA_XR_IRQ_ALL_MASK)); if((RegValue & XAXICDMA_XR_IRQ_IOC_MASK)) { printf("Transfer Completed \n\r "); } if((RegValue & XAXICDMA_XR_IRQ_DELAY_MASK)) { printf("IRQ Delay Interrupt\n\r "); } if((RegValue & XAXICDMA_XR_IRQ_ERROR_MASK)) { printf(" Transfer Error Interrupt\n\r "); } /* STEP 7 : Un-map the AXI CDMA memory from the User layer. =======================================*/ if (munmap(mapped_base, MAP_SIZE) == -1) { printf("Can't unmap memory from user space.\n"); exit(0); } close(memfd); /*STEP 8 : Map the kernel memory location starting from 0x18000000 to the User layer =======================================================*/ memfd_2 = open("/dev/mem", O_RDWR | O_SYNC); if (memfd_2 == -1) { printf("Can't open /dev/mem.\n"); exit(0); } printf("/dev/mem opened.\n"); // Map one page of memory into user space such that the device is in that page, but it may not // be at the start of the page. mapped_base_2 = mmap(0, DDR_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_2, dev_base_2 & ~DDR_MAP_MASK); if (mapped_base_2 == (void *) -1) { printf("Can't map the memory to user space.\n"); exit(0); } printf("Memory mapped at address %p.\n", mapped_base_2); // get the address of the device in user space which will be an offset from the base // that was mapped as memory is mapped at the start of a page mapped_dev_base_2 = mapped_base_2 + (dev_base_2 & DDR_MAP_MASK); /*STEP 9 : Copy the Data from DDR Memory location 0x10000000 to Destination Buffer ==========================================================*/ memcpy(DestArray, mapped_dev_base_2, (BUFFER_BYTESIZE )); /*==STEP 10 : Un-map the Kernel memory from the User layer.=*/ if (munmap(mapped_base_2, DDR_MAP_SIZE) == -1) { printf("Can't unmap memory from user space.\n"); exit(0); } close(memfd_2); /*STEP 11 : Compare Source Buffer with Destination Buffer. ==================================================*/ for (Index = 0; Index < (BUFFER_BYTESIZE/4); Index++) { if (SrcArray[Index] != DestArray[Index]) { printf("Error in the Data comparison \n \r"); return 1; } } printf("DATA Transfer is Successfull \n\r"); return 0; }
linux下运行成功图: