` 之前在第三篇中使用的DMA中断是将ZYNQ7000中的代码移植过来使用的,然而可能这并不是官方对于UltraScale系列的官方例程,而对于DMA的中断传输方式,官方也有其例程。
本实验中使用的软件版本:vivado2019.1;SDK版本:Release Version: 2019.1
使用的为官方的例程xcsudma_intr_examples,而有这个例子可以扩展出我们想要实现的工程。但首先我们要弄清楚官方的这个例程的含义,才能进一步得到我们想要的功能。
首先,我们依旧先看一下硬件图,这次我找到一个旧的下载器,用作调试使用,硬件实物图如下所示:
接着就是.bd文件(即Block Design),其设计和第三篇中的相似如下图所示:
接着打开SDK界面,我们找到官方的例程并打开之,如下所示的那样:
我们可以看到,这个例程还是比较简单的,仅仅只有一个.c文件,所以代码并不很多,我在这里只保留了主要代码,便于大家理解:
- /**
- * [url=home.php?mod=space&uid=1455510]@file[/url] xcsudma_intr_example.c
- * This file contains a design example using the XCsuDma driver in interrupt
- * mode. It sends data and expects to receive the same data through the device
- * using the local loop back mode.
- *
- * @note
- * The example contains an infinite loop such that if interrupts are not
- * working it may hang.
- *
- ******************************************************************************/
- #include "xcsudma.h"
- #include "xparameters.h"
- #include "xil_exception.h"
- #ifdef XPAR_INTC_0_DEVICE_ID
- #include "xintc.h"
- /*
- * The following constants map to the XPAR parameters created in the
- * xparameters.h file. They are defined here such that a user can easily
- * change all the needed parameters in one place.
- */
- #define CSUDMA_DEVICE_ID XPAR_XCSUDMA_0_DEVICE_ID /* CSU DMA device Id */
- #ifdef XPAR_INTC_0_DEVICE_ID
- #define INTC XIntc
- #define INTG_INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID
- #define INTG_CSUDMA_INTR_DEVICE_ID XPAR_INTC_0_CSUDMA_0_VEC_ID /**< ZDMA Interrupt Id */
- #else
- #define INTC XScuGic
- #define INTG_INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
- #if defined (versal)
- #define INTG_CSUDMA_INTR_DEVICE_ID XPAR_PSU_PMCDMA_0_INTR /**< Interrupt device ID
- * of PMC DMA 0 device ID */
- #else
- #define INTG_CSUDMA_INTR_DEVICE_ID XPAR_XCSUDMA_INTR /**< Interrupt device ID
- * of CSU DMA device ID */
- #endif
- #endif
- #define CSU_SSS_CONFIG_OFFSET 0x008 /**< CSU SSS_CFG Offset */
- #define CSUDMA_LOOPBACK_CFG 0x00000050 /**< LOOP BACK configuration
- * macro */
- #define PMC_SSS_CONFIG_OFFSET 0x500 /**< CSU SSS_CFG Offset */
- #define PMCDMA0_LOOPBACK_CFG 0x0000000D /**< LOOP BACK configuration
- * macro for PMCDMA0*/
- #define PMCDMA1_LOOPBACK_CFG 0x00000090 /**< LOOP BACK configuration
- * macro for PMCDMA1*/
- #define SIZE 0x100 /**< Size of the data to be
- * transfered */
- u32 DstBuf[SIZE] __attribute__ ((aligned (64))); /**< Destination buffer */
- u32 SrcBuf[SIZE] __attribute__ ((aligned (64))); /**< Source buffer */
- int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
- u16 DeviceId, u16 IntrId);
- static int SetupInterruptSystem(INTC *IntcInstancePtr,
- XCsuDma *CsuDmaInstance,
- u16 CsuDmaIntrId);
- void IntrHandler(void *CallBackRef);
- static void SrcHandler(void *CallBackRef, u32 Event);
- static void DstHandler(void *CallBackRef, u32 Event);
- XCsuDma CsuDma; /**
- static INTC Intc; /* Instance of the Interrupt Controller */
- u32 DstDone = 0;
- int main(void)
- {
- int Status;
- /* Run the selftest example */
- Status = XCsuDma_IntrExample(&Intc, &CsuDma, (u16)CSUDMA_DEVICE_ID,
- INTG_CSUDMA_INTR_DEVICE_ID);
- if (Status != XST_SUCCESS) {
- xil_printf("CSU_DMA Interrupt Example Failed
- ");
- return XST_FAILURE;
- }
- xil_printf("Successfully ran CSU_DMA Interrupt Example
- ");
- return XST_SUCCESS;
- }
- /*****************************************************************************/
- /**
- *
- * This function performs data transfer in loop back mode in interrupt mode
- * and verify the data.
- *
- * @param DeviceId is the XPAR__DEVICE_ID value from
- * xparameters.h.
- *
- * @return
- * - XST_SUCCESS if successful.
- * - XST_FAILURE if failed.
- *
- * @note None.
- *
- ******************************************************************************/
- int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
- u16 DeviceId, u16 IntrId)
- {
- int Status;
- XCsuDma_Config *Config;
- u32 Index = 0;
- u32 *SrcPtr = SrcBuf;
- u32 *DstPtr = DstBuf;
- u32 Test_Data = 0xABCD1234;
- u32 *Ptr = SrcBuf;
- u32 EnLast = 0;
- /*
- * Initialize the CsuDma driver so that it's ready to use
- * look up the configuration in the config table,
- * then initialize it.
- */
- Config = XCsuDma_LookupConfig(DeviceId);
- if (NULL == Config) {
- return XST_FAILURE;
- }
- Status = XCsuDma_CfgInitialize(CsuDmaInstance, Config, Config->BaseAddress);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- #if defined (versal)
- if (Config->DmaType != XCSUDMA_DMATYPEIS_CSUDMA)
- XCsuDma_PmcReset(Config->DmaType);
- #endif
- /*
- * Performs the self-test to check hardware build.
- */
- Status = XCsuDma_SelfTest(CsuDmaInstance);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /*
- * Connect to the interrupt controller.
- */
- Status = SetupInterruptSystem(IntcInstancePtr, CsuDmaInstance,
- IntrId);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /* Enable interrupts */
- XCsuDma_EnableIntr(CsuDmaInstance, XCSUDMA_DST_CHANNEL,
- XCSUDMA_IXR_DONE_MASK);
- /*
- * Setting CSU_DMA in loop back mode.
- */
- if (Config->DmaType == XCSUDMA_DMATYPEIS_CSUDMA) {
- Xil_Out32(XCSU_BASEADDRESS + CSU_SSS_CONFIG_OFFSET,
- ((Xil_In32(XCSU_BASEADDRESS + CSU_SSS_CONFIG_OFFSET) & 0xF0000) |
- CSUDMA_LOOPBACK_CFG));
- #if defined (versal)
- } else if(Config->DmaType == XCSUDMA_DMATYPEIS_PMCDMA0) {
- Xil_Out32(XPS_PMC_GLOBAL_BASEADDRESS + PMC_SSS_CONFIG_OFFSET,
- ((Xil_In32(XPS_PMC_GLOBAL_BASEADDRESS + PMC_SSS_CONFIG_OFFSET) & 0xFF000000) |
- PMCDMA0_LOOPBACK_CFG));
- } else {
- Xil_Out32(XPS_PMC_GLOBAL_BASEADDRESS + PMC_SSS_CONFIG_OFFSET,
- ((Xil_In32(XPS_PMC_GLOBAL_BASEADDRESS + PMC_SSS_CONFIG_OFFSET) & 0xFF000000) |
- PMCDMA1_LOOPBACK_CFG));
- #endif
- }
- /* Data writing at source address location */
- for(Index = 0; Index < SIZE; Index++)
- {
- *Ptr = Test_Data;
- Test_Data += 0x1;
- Ptr++;
- }
- /* Data transfer in loop back mode */
- XCsuDma_Transfer(CsuDmaInstance, XCSUDMA_DST_CHANNEL, (UINTPTR)DstBuf, SIZE, EnLast);
- XCsuDma_Transfer(CsuDmaInstance, XCSUDMA_SRC_CHANNEL, (UINTPTR)SrcBuf, SIZE, EnLast);
- /* Wait for generation of destination work is done */
- while(DstDone == 0);
- /* Disable interrupts */
- XCsuDma_DisableIntr(CsuDmaInstance, XCSUDMA_DST_CHANNEL,
- XCSUDMA_IXR_DONE_MASK);
- /* To acknowledge the transfer has completed */
- XCsuDma_IntrClear(CsuDmaInstance, XCSUDMA_SRC_CHANNEL, XCSUDMA_IXR_DONE_MASK);
- XCsuDma_IntrClear(CsuDmaInstance, XCSUDMA_DST_CHANNEL, XCSUDMA_IXR_DONE_MASK);
- /*
- * Verifying data of transfered by comparing data at
- * source and address locations.
- */
- for (Index = 0; Index < SIZE; Index++) {
- if (*SrcPtr != *DstPtr) {
- return XST_FAILURE;
- }
- else {
- SrcPtr++;
- DstPtr++;
- }
- }
- return XST_SUCCESS;
- }
- /*****************************************************************************/
- /**
- * This function sets up the interrupt system so interrupts can occur for the
- * CSU DMA. This function is application-specific. The user should modify this
- * function to fit the application.
- *
- * @param IntcInstancePtr is a pointer to the instance of the INTC.
- * @param InstancePtr contains a pointer to the instance of the CSU DMA
- * driver which is going to be connected to the interrupt
- * controller.
- * @param IntrId is the interrupt Id and is typically
- * XPAR__INTR value from xparameters.h.
- *
- * @return
- * - XST_SUCCESS if successful
- * - XST_FAILURE if failed
- *
- * @note None.
- ****************************************************************************/
- static int SetupInterruptSystem(INTC *IntcInstancePtr,
- XCsuDma *InstancePtr,
- u16 IntrId)
- {
- int Status;
- #ifdef XPAR_INTC_0_DEVICE_ID
- #ifndef TESTAPP_GEN
- /* Initialize the interrupt controller and connect the ISRs */
- Status = XIntc_Initialize(IntcInstancePtr, INTG_INTC_DEVICE_ID);
- if (Status != XST_SUCCESS)
- {
- xil_printf("Failed init intc
- ");
- return XST_FAILURE;
- }
- #endif
- /*
- * Connect the driver interrupt handler
- */
- Status = XIntc_Connect(IntcInstancePtr, IntrId,
- (XInterruptHandler)IntrHandler, InstancePtr);
- if (Status != XST_SUCCESS)
- {
- xil_printf("Failed connect intc
- ");
- return XST_FAILURE;
- }
- #ifndef TESTAPP_GEN
- /*
- * Start the interrupt controller such that interrupts are enabled for
- * all devices that cause interrupts.
- */
- Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
- if (Status != XST_SUCCESS)
- {
- return XST_FAILURE;
- }
- #endif
- /*
- * Enable the interrupt for the CSUDMA device.
- */
- XIntc_Enable(IntcInstancePtr, IntrId);
- #ifndef TESTAPP_GEN
- Xil_ExceptionInit();
- Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
- (Xil_ExceptionHandler)XIntc_InterruptHandler,
- (void *)IntcInstancePtr);
- #endif
- #else
- #ifndef TESTAPP_GEN
- XScuGic_Config *IntcConfig; /* Config for interrupt controller */
- /*
- * Initialize the interrupt controller driver
- */
- IntcConfig = XScuGic_LookupConfig(INTG_INTC_DEVICE_ID);
- if (NULL == IntcConfig) {
- return XST_FAILURE;
- }
- Status = XScuGic_CfgInitialize(IntcInstancePtr, 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,
- IntcInstancePtr);
- #endif
- /*
- * Connect a device driver handler that will be called when an
- * interrupt for the device occurs, the device driver handler
- * performs the specific interrupt processing for the device
- */
- Status = XScuGic_Connect(IntcInstancePtr, IntrId,
- (Xil_ExceptionHandler) IntrHandler,
- (void *) InstancePtr);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /*
- * Enable the interrupt for the device
- */
- XScuGic_Enable(IntcInstancePtr, IntrId);
- #endif
- #ifndef TESTAPP_GEN
- /*
- * Enable interrupts
- */
- Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
- #endif
- return XST_SUCCESS;
- }
- /*****************************************************************************/
- /**
- *
- * This function is the interrupt handler for the CSU_DMA driver.
- *
- * This handler reads the interrupt status from the Status register, determines
- * the source of the interrupts, calls according callbacks, and finally clears
- * the interrupts.
- *
- * @param CallBackRef is the callback reference passed from the interrupt
- * handler, which in our case is a pointer to the driver instance.
- *
- * @return None.
- *
- * @note None.
- *
- ******************************************************************************/
- void IntrHandler(void *CallBackRef)
- {
- u32 SrcPending;
- u32 DstPending;
- XCsuDma *XCsuDmaPtr = NULL;
- XCsuDmaPtr = (XCsuDma *)((void *)CallBackRef);
- /* Handling interrupt */
- /* Getting pending interrupts of source */
- SrcPending = XCsuDma_IntrGetStatus(XCsuDmaPtr, XCSUDMA_SRC_CHANNEL);
- XCsuDma_IntrClear(XCsuDmaPtr, XCSUDMA_SRC_CHANNEL, SrcPending);
- SrcPending &= (~XCsuDma_GetIntrMask(XCsuDmaPtr, XCSUDMA_SRC_CHANNEL));
- /* Getting pending interrupts of destination */
- DstPending = XCsuDma_IntrGetStatus(XCsuDmaPtr, XCSUDMA_DST_CHANNEL);
- XCsuDma_IntrClear(XCsuDmaPtr, XCSUDMA_DST_CHANNEL, DstPending);
- DstPending &= (~XCsuDma_GetIntrMask(XCsuDmaPtr, XCSUDMA_DST_CHANNEL));
- if (SrcPending != 0x00) {
- SrcHandler(XCsuDmaPtr, SrcPending);
- }
- if (DstPending != 0x00) {
- DstHandler(XCsuDmaPtr, DstPending);
- }
- }
- /*****************************************************************************/
- /**
- * This is static function which handlers source channel interrupts.
- *
- * @param CallBackRef is the callback reference passed from the interrupt
- * handler, which in our case is a pointer to the driver instance.
- * @param Event specifies which interrupts were occured.
- *
- * @return None.
- * @note None.
- *
- ******************************************************************************/
- static void SrcHandler(void *CallBackRef, u32 Event)
- {
- //该中断函数在本例中没有用到,在这里就是空函数,
- //如果需要,我会在附上该段代码
- }
- /*****************************************************************************/
- /**
- * This static function handles destination channel interrupts.
- *
- * @param CallBackRef is the callback reference passed from the interrupt
- * handler, which in our case is a pointer to the driver instance.
- * @param Event specifies which interrupts were occured.
- *
- * @return None.
- * @note None.
- *
- ******************************************************************************/
- static void DstHandler(void *CallBackRef, u32 Event){
- //由于代码太多,省略一些条件判断。
- //只保留了本实验中需要的代码
- if (Event & XCSUDMA_IXR_DONE_MASK) {
- DstDone = 1;
- }
- }
复制代码
其中,传输的核心函数即为:XCsuDma_Transfer( ),这个函数负责向DMA发送或接收DMA传来的数据,这个函数的代码为:
- /*****************************************************************************/
- /**
- *
- * This function sets the starting address and amount(size) of the data to be
- * transfered from/to the memory through the AXI interface.
- *
- * @param InstancePtr is a pointer to XCsuDma instance to be worked on.
- * @param Channel represents the type of channel either it is Source or
- * Destination.
- * Source channel - XCSUDMA_SRC_CHANNEL
- * Destination Channel - XCSUDMA_DST_CHANNEL
- * @param Addr is a 64 bit variable which holds the starting address of
- * data which needs to write into the memory(DST) (or read from
- * the memory(SRC)).
- * @param Size is a 32 bit variable which represents the number of 4 byte
- * words needs to be transfered from starting address.
- * @param EnDataLast is to trigger an end of message. It will enable or
- * disable data_inp_last signal to stream interface when current
- * command is completed. It is applicable only to source channel
- * and neglected for destination channel.
- * - 1 - Asserts data_inp_last signal.
- * - 0 - data_inp_last will not be asserted.
- *
- * @return None.
- *
- * @note Data_inp_last signal is asserted simultaneously with the
- * data_inp_valid signal associated with the final 32-bit word
- * transfer.
- *
- ******************************************************************************/
- void XCsuDma_Transfer(XCsuDma *InstancePtr, XCsuDma_Channel Channel,
- UINTPTR Addr, u32 Size, u8 EnDataLast)
- {
- /* Verify arguments */
- Xil_AssertVoid(InstancePtr != NULL);
- Xil_AssertVoid(((Addr) & (u64)(XCSUDMA_ADDR_LSB_MASK)) == (u64)0x00);
- Xil_AssertVoid((Channel == (XCSUDMA_SRC_CHANNEL)) ||
- (Channel == (XCSUDMA_DST_CHANNEL)));
- Xil_AssertVoid(Size <= (u32)(XCSUDMA_SIZE_MAX));
- Xil_AssertVoid(InstancePtr->IsReady == (u32)(XIL_COMPONENT_IS_READY));
- #if !defined(PSU_PMU)
- /* Flushing cache memory */
- if (Channel == (XCSUDMA_SRC_CHANNEL)) {
- Xil_DCacheFlushRange(Addr, Size << (u32)(XCSUDMA_SIZE_SHIFT));
- }
- /* Invalidating cache memory */
- else {
- #if defined(__aarch64__)
- Xil_DCacheInvalidateRange(Addr, Size <<
- (u32)(XCSUDMA_SIZE_SHIFT));
- #else
- Xil_DCacheFlushRange(Addr, Size << (u32)(XCSUDMA_SIZE_SHIFT));
- #endif
- }
- #endif
- XCsuDma_WriteReg(InstancePtr->Config.BaseAddress,
- ((u32)(XCSUDMA_ADDR_OFFSET) +
- ((u32)Channel * (u32)(XCSUDMA_OFFSET_DIFF))),
- ((u32)(Addr) & (u32)(XCSUDMA_ADDR_MASK)));
- XCsuDma_WriteReg(InstancePtr->Config.BaseAddress,
- ((u32)(XCSUDMA_ADDR_MSB_OFFSET) +
- ((u32)Channel * (u32)(XCSUDMA_OFFSET_DIFF))),
- ((u32)((Addr & ULONG64_HI_MASK) >> XCSUDMA_MSB_ADDR_SHIFT) &
- (u32)(XCSUDMA_MSB_ADDR_MASK)));
- if (EnDataLast == (u8)(XCSUDMA_LAST_WORD_MASK)) {
- XCsuDma_WriteReg(InstancePtr->Config.BaseAddress,
- ((u32)(XCSUDMA_SIZE_OFFSET) +
- ((u32)Channel * (u32)(XCSUDMA_OFFSET_DIFF))),
- ((Size << (u32)(XCSUDMA_SIZE_SHIFT)) |
- (u32)(XCSUDMA_LAST_WORD_MASK)));
- }
- else {
- XCsuDma_WriteReg(InstancePtr->Config.BaseAddress,
- ((u32)(XCSUDMA_SIZE_OFFSET) +
- ((u32)Channel * (u32)(XCSUDMA_OFFSET_DIFF))),
- (Size << (u32)(XCSUDMA_SIZE_SHIFT)));
- }
- }
复制代码
这个函数主要对两个寄存器进行操作,即为:
而接收完成或是发送完成则由中断通知CPU,这个中断函数即为:IntrHandler(void *CallBackRef),而这个中断函数又分为接收和发送完成两部分,其代码为:
- void IntrHandler(void *CallBackRef)
- {
- u32 SrcPending;
- u32 DstPending;
- XCsuDma *XCsuDmaPtr = NULL;
- XCsuDmaPtr = (XCsuDma *)((void *)CallBackRef);
- /* Handling interrupt */
- /* Getting pending interrupts of source */
- SrcPending = XCsuDma_IntrGetStatus(XCsuDmaPtr, XCSUDMA_SRC_CHANNEL);
- XCsuDma_IntrClear(XCsuDmaPtr, XCSUDMA_SRC_CHANNEL, SrcPending);
- SrcPending &= (~XCsuDma_GetIntrMask(XCsuDmaPtr, XCSUDMA_SRC_CHANNEL));
- /* Getting pending interrupts of destination */
- DstPending = XCsuDma_IntrGetStatus(XCsuDmaPtr, XCSUDMA_DST_CHANNEL);
- XCsuDma_IntrClear(XCsuDmaPtr, XCSUDMA_DST_CHANNEL, DstPending);
- DstPending &= (~XCsuDma_GetIntrMask(XCsuDmaPtr, XCSUDMA_DST_CHANNEL));
- if (SrcPending != 0x00) {
- SrcHandler(XCsuDmaPtr, SrcPending);
- }
- if (DstPending != 0x00) {
- DstHandler(XCsuDmaPtr, DstPending);
- }
- }
复制代码
即在XCsuDma_Transfer( )接收到设定数量的数据完成后,会进入到中断函数中去,从而执行DstHandler(XCsuDmaPtr, DstPending);
调试过程也是如此的,在DEBUG模式下,可以设置断点观察,如下所示:
可以看到,执行过程中DstDone置位,主函数可以继续执行下去。
最终的结果:
这就是这个例程,大家可以在这个例程的基础上进行就该就可以了。
`
|