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.

C6654 EDMA 其他通道ipr异常,无法进入中断函数

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, &paramSet);
    
            /*Set params for edma_paRAMId1*/
    //        paramSet.linkAddr = (unsigned short)EDMA3CC_OPT(edma_paRAMId1);
    //        EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, edma_paRAMId1, &paramSet);
    
            /*Set params for edma_paRAMId2*/
            //        paramSet.linkAddr = (unsigned short)EDMA3CC_OPT(edma_paRAMId1);
            //        EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, edma_paRAMId2, &paramSet);
        }
    
        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:

    0181.edma.h

    是不是定义错了?

  • 初始化及配置相关代码:

    /**
     *  \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;
    }

  • 改变通道之后,数据搬移成功了吗?可以通过查看param中参数是否更新来确定。
  • 数据没有搬运成功,param中参数也没有更新。
    只有chNum = 0 & tccNum = 0时可以搬运成功,这两个值任何一个更改之后都无法搬运成功。
  • CICEventMap(SOC_CIC_0_REGS, CIC_INT_EDMA3_0_CC0_ERRINT, uiCIC_out_num+1)中CIC_INT_EDMA3_0_CC0_ERRINT的值是16吗?
    不清楚头文件中定义的变量是指什么?在程序中能查到此变量应用在哪里吗?可以参考看一下。
  • 找到问题了,是因为EDMA在初始化的时候没有清除各个通道的事件和中断造成的,在EDMA3Init函数中添加相应代码就可以了。