EDMA 通道0能工作正常,当换成别的通道,比如说EDMA 通道1,OPT中TCC值保持不变。
传送完成,在IPR(IPRH)中置的不是TCC的那一位(ier对应的那一位),而是IPR(IPRH)别的位。
只有通道0是正常的,试了几个其他通道,发现最后结果都是iprh变成0x04000000,并且无法进入中断服务函数。
请问可能是什么原因造成的?
在网上看到有类似的错误,说是地址映射错误,但没有详细讲,可能是哪个地址映射错误?为什么只有通道0可以正常工作?
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
EDMA 通道0能工作正常,当换成别的通道,比如说EDMA 通道1,OPT中TCC值保持不变。
传送完成,在IPR(IPRH)中置的不是TCC的那一位(ier对应的那一位),而是IPR(IPRH)别的位。
只有通道0是正常的,试了几个其他通道,发现最后结果都是iprh变成0x04000000,并且无法进入中断服务函数。
请问可能是什么原因造成的?
在网上看到有类似的错误,说是地址映射错误,但没有详细讲,可能是哪个地址映射错误?为什么只有通道0可以正常工作?
/******************************************************************************* * @函数名称 fpga_emif16_edma_test * @函数说明 DSP-FPGA EMIF16+DMA通信测试 * @输入参数 无 * @输出参数 无 * @返回参数 无 *******************************************************************************/ unsigned int chType = EDMA3_CHANNEL_TYPE_DMA; unsigned int chNum = 0; //使用通道号 unsigned int tccNum = 0; unsigned int edmaTC = 0; unsigned int syncType = EDMA3_SYNC_A; unsigned int trigMode = EDMA3_TRIG_MODE_MANUAL; unsigned int evtQ = 0; // 使用的事件队列 EDMA3CCPaRAMEntry paramSet; //unsigned int edma_paRAMId1 = 1; //unsigned int edma_paRAMId2 = 2; unsigned int transfer_times = 0; //实际传输次数 volatile char *srcBuff; volatile char *dstBuff; unsigned short emif_wbuffer[EMIF_BUFFER_LENGTH]; unsigned short emif_rbuffer[EMIF_BUFFER_LENGTH]; unsigned short temp_rbuffer[EMIF_BUFFER_LENGTH]; void fpga_emif16_edma_test(void) { volatile unsigned int status = FALSE; printf("EMIF EDMA testing...\r\n"); EDMA3Init(SOC_EDMA30CC_0_REGS, evtQ); EDMA3InterruptInit(); unsigned int i = 0; /*初始化数据缓冲区*/ for (i = 0; i < EMIF_BUFFER_LENGTH; i++) { emif_wbuffer[i] = (unsigned short)(i+15); emif_rbuffer[i] = 0xEFEF; } emif_wbuffer[0] = 0; emif_wbuffer[EMIF_BUFFER_LENGTH-1] = 0; /*向FPGA写数据*/ printf( "Reading %d words from FPGA\r\n", EMIF_BUFFER_LENGTH ); srcBuff = (char *)(SOC_EMIFA_CS1_ADDR); dstBuff = (char *)(emif_rbuffer); edma_test(); volatile unsigned int EMIFA_error_count = 0; /*检查EMIFA传输的数据是否正确 */ printf("Checking data...\n"); for (i = 0; i<EMIF_BUFFER_LENGTH; i++) { if (emif_wbuffer[i] != emif_rbuffer[i]) { EMIFA_error_count++; } } /* 报告通信结果*/ if (EMIFA_error_count) { printf( "EMIFA_error_count=%d\n", EMIFA_error_count); while(1); } else { printf("All data read successful!\n"); printf("EMIFA transfers complete!\n"); } while(1); } /******************************************************************************* * @函数名称 edma_test * @函数说明 edma测试 * @输入参数 无 * @输出参数 无 * @返回参数 无 *******************************************************************************/ unsigned int edma_test(void) { volatile unsigned int index = 0; volatile unsigned int count = 0; unsigned int retVal = 0u; unsigned int numenabled = 0u; unsigned int acnt = MAX_ACOUNT; unsigned int bcnt = MAX_BCOUNT; unsigned int ccnt = MAX_CCOUNT; // 申请 DMA 通道和 TCC retVal = EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, chType, chNum, tccNum, evtQ); // 注册回调函数 cb_Fxn[tccNum] = &callback; if (TRUE == retVal) { /*Set params for channel 0*/ // 给参数 RAM 赋值 paramSet.srcAddr = (unsigned int)(srcBuff); paramSet.destAddr = (unsigned int)(dstBuff); paramSet.aCnt = (unsigned short)acnt; paramSet.bCnt = (unsigned short)bcnt; paramSet.cCnt = (unsigned short)ccnt; // 设置 SRC / DES 索引 paramSet.srcBIdx = (short)acnt; paramSet.destBIdx = (short)acnt; paramSet.srcCIdx = (short)acnt; paramSet.destCIdx = (short)acnt; //paramSet.linkAddr = (unsigned short)EDMA3CC_OPT(edma_paRAMId1); paramSet.linkAddr = (unsigned short)0xFFFFu; paramSet.bCntReload = (unsigned short)0u; paramSet.opt = 0u; // Src 及 Dest 使用 INCR 模式 paramSet.opt &= 0xFFFFFFFCu; // Transfer complete chaining enable // paramSet.opt |= ((1 << EDMA3CC_OPT_TCCHEN_SHIFT) & EDMA3CC_OPT_TCCHEN); // Intermediate transfer completion interrupt enable // paramSet.opt |= ((1 << EDMA3CC_OPT_ITCCHEN_SHIFT) & EDMA3CC_OPT_ITCCHEN); // 编程 TCC paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC); // 使能 Intermediate & Final 传输完成中断 paramSet.opt |= (1 << EDMA3CC_OPT_ITCINTEN_SHIFT); paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT); // A Sync 传输模式 paramSet.opt &= 0xFFFFFFFBu; // 写参数 RAM EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet); /*Set params for edma_paRAMId1*/ // paramSet.linkAddr = (unsigned short)EDMA3CC_OPT(edma_paRAMId1); // EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, edma_paRAMId1, ¶mSet); /*Set params for edma_paRAMId2*/ // paramSet.linkAddr = (unsigned short)EDMA3CC_OPT(edma_paRAMId1); // EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, edma_paRAMId2, ¶mSet); } if (TRUE == retVal) { // A Sync 传输模式 numenabled = bcnt * ccnt; transfer_times = 0; /*EMIFA+EDMA传输速率测试 */ global.last_timer = global.timer; for (index = 0; index < numenabled; index++) { irqRaised = 0; // 按照计算的次数使能传输 retVal = EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_MANUAL); if (TRUE != retVal) { printf("edma3Test: EDMA3EnableTransfer Failed.\r\n"); break; } // 等待中断服务函数执行完成 while (irqRaised == 0u) { } // 检测传输完成状态 if (irqRaised < 0) { // 发生错误时终止 printf("\r\nedma3Test: Event Miss Occured!!!\r\n"); // 清除错误标志位 EDMA3ClearErrorBits(SOC_EDMA30CC_0_REGS, chNum, evtQ); break; } } printf("The rate of emif_edma_rate is %02fM/s.\r\n", (float)EMIF_BUFFER_LENGTH*TEST_TIMES/((global.timer - global.last_timer)*1000)); // EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_MANUAL); } if (TRUE == retVal) { // 释放先前分配的通道 retVal = EDMA3FreeChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA, chNum, EDMA3_TRIG_MODE_MANUAL, tccNum, evtQ); // 取消注册回调函数 cb_Fxn[tccNum] = NULL; } return retVal; }
只有chNum=0和tccNum=0的时候可以正常工作,这两个换成其他通道值均无法正常工作。
另外在C6654手册中看到EDMA完成中断和错误中断中断号分别为22和16:
但是在TI的驱动库中C:\ti\pdk_c665x_2_0_14\packages\ti\csl\src\ip\edma\V1\edma.h中看到定义的中断号有出入,分别为11和12:
是不是定义错了?
初始化及配置相关代码:
/** * \brief EDMA3 Initialization * * This function initializes the EDMA3 Driver * Clears the error specific registers (EMCR/EMCRh, QEMCR, CCERRCLR) & * initialize the Queue Number Registers * * \param baseAdd Memory address of the EDMA instance used.\n * * \param queNum Event Queue Number to which the channel * will be mapped (valid only for the * Master Channel (DMA/QDMA) request).\n * * \return None * * \note The regionId is the shadow region(0 or 1) used and the, * Event Queue used is either (0 or 1). There are only four shadow * regions and only two event Queues */ void EDMA3Init(unsigned int baseAdd, unsigned int queNum) { unsigned int count = 0; unsigned int i = 0; #ifdef _TMS320C6X /* For DSP, regionId is assigned here and used globally in the driver */ regionId = (unsigned int)1u; #else /* FOR ARM, regionId is assigned here and used globally in the driver */ regionId = (unsigned int)0u; #endif /* Clear the Event miss Registers */ HWREG(baseAdd + EDMA3CC_EMCR) = EDMA3_SET_ALL_BITS; HWREG(baseAdd + EDMA3CC_EMCRH) = EDMA3_SET_ALL_BITS; HWREG(baseAdd + EDMA3CC_QEMCR) = EDMA3_SET_ALL_BITS; /* Clear CCERR register */ HWREG(baseAdd + EDMA3CC_CCERRCLR) = EDMA3_SET_ALL_BITS; /* FOR TYPE EDMA*/ /* Enable the DMA (0 - 64) channels in the DRAE and DRAEH register */ HWREG(baseAdd + EDMA3CC_DRAE(regionId)) = EDMA3_SET_ALL_BITS; HWREG(baseAdd + EDMA3CC_DRAEH(regionId)) = EDMA3_SET_ALL_BITS; if((EDMA_REVID_AM335X == EDMAVersionGet())) { for(i = 0; i < 64; i++) { /* All events are one to one mapped with the channels */ HWREG(baseAdd + EDMA3CC_DCHMAP(i)) = i << 5; } } /* Initialize the DMA Queue Number Registers */ for (count = 0;count < SOC_EDMA3_NUM_DMACH; count++) { HWREG(baseAdd + EDMA3CC_DMAQNUM(count >> 3u)) &= EDMA3CC_DMAQNUM_CLR(count); HWREG(baseAdd + EDMA3CC_DMAQNUM(count >> 3u)) |= EDMA3CC_DMAQNUM_SET(count,queNum); } /* FOR TYPE QDMA */ /* Enable the DMA (0 - 64) channels in the DRAE register */ HWREG(baseAdd + EDMA3CC_QRAE(regionId)) = EDMA3_SET_ALL_BITS; /* Initialize the QDMA Queue Number Registers */ for (count = 0;count < SOC_EDMA3_NUM_QDMACH; count++) { HWREG(baseAdd + EDMA3CC_QDMAQNUM) &= EDMA3CC_QDMAQNUM_CLR(count); HWREG(baseAdd + EDMA3CC_QDMAQNUM) |= EDMA3CC_QDMAQNUM_SET(count,queNum); } } /****************************************************************************/ /* */ /* EDMA3 中断初始化 */ /* */ /****************************************************************************/ void EDMA3InterruptInit() { CICDisableGlobalHostInt(SOC_CIC_0_REGS); CICEventMap(SOC_CIC_0_REGS, CIC_INT_EDMA3_0_CC0_INT1, uiCIC_out_num); CICEventMap(SOC_CIC_0_REGS, CIC_INT_EDMA3_0_CC0_ERRINT, uiCIC_out_num+1); CICEnableGlobalHostInt(SOC_CIC_0_REGS); // 传输完成中断 IntRegister(C66X_MASK_INT7, EDMA3CCComplIsr); IntEventMap(C66X_MASK_INT7, SYS_INT_CIC0_OUT0_20); IntEnable(C66X_MASK_INT7); // 传输错误中断 IntRegister(C66X_MASK_INT8, EDMA3CCErrIsr); IntEventMap(C66X_MASK_INT8, SYS_INT_CIC0_OUT1_20); IntEnable(C66X_MASK_INT8); } /** * \brief Request a DMA/QDMA/Link channel. * * Each channel (DMA/QDMA/Link) must be requested before initiating a DMA * transfer on that channel. * * This API is used to allocate a logical channel (DMA/QDMA/Link) along with * the associated resources. For DMA and QDMA channels, TCC and PaRAM Set are * also allocated along with the requested channel. * * User can request a specific logical channel by passing the channel number * in 'chNum'. * * For DMA/QDMA channels, after allocating all the EDMA3 resources, this API * sets the TCC field of the OPT PaRAM Word with the allocated TCC. It also sets * the event queue for the channel allocated. The event queue needs to be * specified by the user. * * For DMA channel, it also sets the DCHMAP register. * * For QDMA channel, it sets the QCHMAP register and CCNT as trigger word and * enables the QDMA channel by writing to the QEESR register. * * \param baseAdd Memory address of the EDMA instance used.\n * * \param chtype (DMA/QDMA) Channel * For Example: For DMA it is * EDMA3_CHANNEL_TYPE_DMA.\n * * \param chNum This is the channel number requested for a * particular event.\n * * \param tccNum The channel number on which the * completion/error interrupt is generated. * Not used if user requested for a Link * channel.\n * * \param evtQNum Event Queue Number to which the channel * will be mapped (valid only for the * Master Channel (DMA/QDMA) request).\n * * \return TRUE if parameters are valid, else FALSE */ unsigned int EDMA3RequestChannel(unsigned int baseAdd, unsigned int chType, unsigned int chNum, unsigned int tccNum, unsigned int evtQNum) { unsigned int retVal = FALSE; if (chNum < SOC_EDMA3_NUM_DMACH) { /* Enable the DMA channel in the enabled in the shadow region * specific register */ EDMA3EnableChInShadowReg(baseAdd, chType, chNum); EDMA3MapChToEvtQ( baseAdd, chType, chNum, evtQNum); if (EDMA3_CHANNEL_TYPE_DMA == chType) { /* Interrupt channel nums are < 32 */ if (tccNum < SOC_EDMA3_NUM_DMACH) { /* Enable the Event Interrupt */ EDMA3EnableEvtIntr(baseAdd, chNum); retVal = TRUE; } HWREG(baseAdd + EDMA3CC_OPT(chNum)) &= EDMA3CC_OPT_TCC_CLR; HWREG(baseAdd + EDMA3CC_OPT(chNum)) |= EDMA3CC_OPT_TCC_SET(chNum); } else if (EDMA3_CHANNEL_TYPE_QDMA== chType) { /* Interrupt channel nums are < 8 */ if (tccNum < SOC_EDMA3_NUM_QDMACH) { /* Enable the Event Interrupt */ EDMA3EnableEvtIntr(baseAdd, chNum); retVal = TRUE; } HWREG(baseAdd + EDMA3CC_OPT(chNum)) &= EDMA3CC_OPT_TCC_CLR; HWREG(baseAdd + EDMA3CC_OPT(chNum)) |= EDMA3CC_OPT_TCC_SET(chNum); } } return retVal; } /** * \brief Copy the user specified PaRAM Set onto the PaRAM Set associated * with the logical channel (DMA/Link). * * This API takes a PaRAM Set as input and copies it onto the actual PaRAM Set * associated with the logical channel. OPT field of the PaRAM Set is written * first and the CCNT field is written last. * * * \param baseAdd Memory address of the EDMA instance used.\n * * \param chNum Logical Channel whose PaRAM set is * requested.\n * * \param newPaRAM Parameter RAM set to be copied onto existing * PaRAM.\n * * \return None */ void EDMA3SetPaRAM(unsigned int baseAdd, unsigned int chNum, EDMA3CCPaRAMEntry* newPaRAM) { unsigned int PaRAMId = chNum; /* PaRAM mapped to channel Number */ unsigned int i = 0; unsigned int *sr = (unsigned int *)newPaRAM; volatile unsigned int *ds; ds = (unsigned int *)(baseAdd + EDMA3CC_OPT(PaRAMId)); for(i=0; i < EDMA3CC_PARAM_ENTRY_FIELDS; i++) { *ds = *sr; ds++; sr++; } } /** * \brief Start EDMA transfer on the specified channel. * * There are multiple ways to trigger an EDMA3 transfer. The triggering mode * option allows choosing from the available triggering modes: Event, * Manual or QDMA. * * In event triggered, a peripheral or an externally generated event triggers * the transfer. This API clears the Event and Event Miss Register and then * enables the DMA channel by writing to the EESR. * * In manual triggered mode, CPU manually triggers a transfer by writing a 1 * in the Event Set Register ESR. This API writes to the ESR to start the * transfer. * * In QDMA triggered mode, a QDMA transfer is triggered when a CPU (or other * EDMA3 programmer) writes to the trigger word of the QDMA channel PaRAM set * (auto-triggered) or when the EDMA3CC performs a link update on a PaRAM set * that has been mapped to a QDMA channel (link triggered). This API enables * the QDMA channel by writing to the QEESR register. * * \param baseAdd Memory address of the EDMA instance used.\n * * \param chNum Channel being used to enable transfer.\n * * \param trigMode Mode of triggering start of transfer (Manual, * QDMA or Event).\n * * trigMode can have values: * EDMA3_TRIG_MODE_MANUAL\n * EDMA3_TRIG_MODE_QDMA\n * EDMA3_TRIG_MODE_EVENT\n * * \return retVal TRUE or FALSE depending on the param passed.\n * */ unsigned int EDMA3EnableTransfer(unsigned int baseAdd, unsigned int chNum, unsigned int trigMode) { unsigned int retVal = FALSE; switch (trigMode) { case EDMA3_TRIG_MODE_MANUAL : if (chNum < SOC_EDMA3_NUM_DMACH) { EDMA3SetEvt(baseAdd, chNum); retVal = TRUE; } break; case EDMA3_TRIG_MODE_QDMA : if (chNum < SOC_EDMA3_NUM_QDMACH) { EDMA3EnableQdmaEvt(baseAdd, chNum); retVal = TRUE; } break; case EDMA3_TRIG_MODE_EVENT : if (chNum < SOC_EDMA3_NUM_DMACH) { /*clear SECR & EMCR to clean any previous NULL request */ EDMA3ClrMissEvt(baseAdd, chNum); /* Set EESR to enable event */ EDMA3EnableDmaEvt(baseAdd, chNum); retVal = TRUE; } break; default : retVal = FALSE; break; } return retVal; }