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;
}