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.

AM6412: GPIO Trigger DMA

Part Number: AM6412

Tool/software:

Follow the example project,   trigger DMA with the help of GPIO on AM64x devices.

During the debugging process, it was found that a single change in GPIO level triggers two DMA interrupts.

After completing one conversion, refreshing the cache for DMA-related variate temporarily resolves this issue.

However, I cannot confirm that this approach addresses the issue comprehensively.

  • Follow the example project,   trigger DMA with the help of GPIO on AM64x devices.

    Assuming the example project means the steps in https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1378150/faq-how-to-trigger-dma-with-the-help-of-gpio-on-am64x-am243-and-am62x-devices

    refreshing the cache for DMA-related variate temporarily resolves this issue.

    What do you mean with refreshing the cache? Can you elaborate with for example what the code to do this looks like?

  • Hello Ryan,

    During the debugging process, it was found that a single change in GPIO level triggers two DMA interrupts.

    In the FAQ example, I am routing GPIO Bank interrupt to GPIO MUX Router.

    So, it might be a chance on your customs board. There are some bank pins that are toggling due to these DMA triggers many times.

    To fix this issue, we need to route Pin interrupt rather than Bank interrupt.

    We need to change the below highlighted line to route Pin num to the GPIO Mux router.

    I am suspecting issue with the above one.

    Please confirm after changing the below fix  still you have the issue ?

    After completing one conversion, refreshing the cache for DMA-related variate temporarily resolves this issue.

    Yes, this needs to be done for every DMA completion event.

    If we don't do every DMA completion event, then the CPU may read the old data.

    So, based on the above observations, I have added two changes to the example.

    Please let me know if you still find any issues.

    Regards,

    Anil.

  • After completing one conversion, refreshing the cache for DMA-related variate temporarily resolves this issue.

    Yes, this needs to be done for every DMA completion event.

    If we don't do every DMA completion event, then the CPU may read the old data.

    This is cache coherency (invalidate on variables read, writeback on variables written). Yes it is a must for logical correctness. The R5 cache is not IO coherent, it is the responsibility of the programmer to manage coherency.

  • I have tried the method you provided, but unfortunately, it hasn't resolved my issue. Below is my code. Could you please  help review it?

  • #include <kernel/dpl/DebugP.h>
    #include <kernel/dpl/AddrTranslateP.h>
    #include <kernel/dpl/CacheP.h>
    #include "ti_drivers_config.h"
    #include "ti_drivers_open_close.h"
    #include "ti_board_open_close.h"
    #include <drivers/sciclient.h>
    
    #include <kernel/nortos/dpl/a53/HwiP_armv8_gic.h>
    
    #include <stdio.h>
    #include <string.h>
    
    void GPIO_TIRG_DMA_CB(Udma_EventHandle eventHandle, uint32_t eventType, void *appData);
    
    void __asm_flush_dcache_range(uint64_t start, uint64_t end);
    
    /*
     * GPIO INTRTR bank interrupt source index base
     * See https://software-dl.ti.com/tisci/esd/latest/5_soc_doc/am64x/interrupt_cfg.html#mcu-mcu-gpiomux-introuter0-interrupt-router-input-sources
     */
    //#define TISCI_GPIO_BANK_INT_SRC_IDX_BASE_GPIO1      ( 90U )
    //#define TISCI_GPIO1_BANK_INT_SRC_IDX(x)             ( TISCI_GPIO_BANK_INT_SRC_IDX_BASE_GPIO1 + x )
    
    /*
     * GPIO INTRTR destination index
     * See https://software-dl.ti.com/tisci/esd/latest/5_soc_doc/am64x/interrupt_cfg.html#main-gpiomux-introuter0-interrupt-router-output-destinations
     */
    #define L2G_EVENT_ID0                               ( 24U )
    
    /* Number of bytes to do memcpy */
    #define UDMA_TEST_NUM_BYTES             (1024U)
    /* UDMA TR packet descriptor memory size - with one TR */
    #define UDMA_TEST_TRPD_SIZE             (UDMA_GET_TRPD_TR15_SIZE(1U))
    
    
    uint32_t gGpioBaseAddr = GPIO_TIRG_DMA_BASE_ADDR;
    //uint32_t gGpioBaseAddr8 = GPIO_35_BASE_ADDR;
    
    /* UDMA TRPD Memory */
    uint8_t gUdmaTestTrpdMem[UDMA_TEST_TRPD_SIZE] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    
    /* Application Buffers */
    uint8_t gUdmaTestSrcBuf[UDMA_ALIGN_SIZE(UDMA_TEST_NUM_BYTES)] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    uint8_t gUdmaTestDestBuf[UDMA_ALIGN_SIZE(UDMA_TEST_NUM_BYTES)] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    
    Udma_EventObject gConfigUdma0BlkCopyCqEventObj[CONFIG_UDMA0_NUM_BLKCOPY_CH];
    Udma_EventCallback gConfigUdma0BlkCopyCqEventCb[1]  = {&GPIO_TIRG_DMA_CB,};
    
    uint32_t globalEvent0;
    
    uint32_t gXferErrCnt = 0U;
    
    volatile uint8_t gRunFlag = 1;
    volatile uint8_t gCbCount = 0;
    
    void Sciclient_gpioIrqSet(void)
    {
        int32_t                             retVal;
        struct tisci_msg_rm_irq_set_req     rmIrqReq;
        struct tisci_msg_rm_irq_set_resp    rmIrqResp;
    
    
        rmIrqReq.valid_params           = 0U;
        rmIrqReq.valid_params          |= TISCI_MSG_VALUE_RM_DST_ID_VALID;
        rmIrqReq.valid_params          |= TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
        rmIrqReq.global_event           = 0U;
    
    
        rmIrqReq.src_id                 = TISCI_DEV_GPIO1;
        rmIrqReq.src_index              = 43;
        rmIrqReq.dst_id                 = TISCI_DEV_DMASS0_INTAGGR_0;
        rmIrqReq.dst_host_irq           = L2G_EVENT_ID0;
    
    
        rmIrqReq.ia_id                  = 0U;
        rmIrqReq.vint                   = 0U;
        rmIrqReq.vint_status_bit_index  = 0U;
        rmIrqReq.secondary_host         = TISCI_MSG_VALUE_RM_UNUSED_SECONDARY_HOST;
    
        retVal = Sciclient_rmIrqSet(&rmIrqReq, &rmIrqResp, SystemP_WAIT_FOREVER);
        if(0 != retVal)
        {
            DebugP_log("[Error] Sciclient event config failed!!!\r\n");
            DebugP_assert(FALSE);
        }
    
        return;
    }
    
    static void configure_intaggrL2G(uint32_t localEvent, uint32_t globalEvent)
    {
        uint64_t eventRegOffset = CSL_DMASS0_INTAGGR_L2G_BASE + (localEvent * 0x20U);
        CSL_REG32_WR(eventRegOffset, ( (0U << 31U) | (globalEvent & 0xFFFFU) ) ); /* pulse event */
    }
    
    int32_t PsramDma_udmaUpdateSubmitTR(Udma_ChHandle chHandle,
                                        uint8_t *trpdMem,
                                        const void *destBuf,
                                        const void *srcBuf,
                                        uint32_t length,uint64_t trpdMemPhy)
    {
        int32_t status = UDMA_SOK;
        uint32_t        cqRingNum;
    
        cqRingNum =  Udma_chGetCqRingNum(chHandle);
    
        /* Make TRPD with TR15 TR type */
        UdmaUtils_makeTrpdTr15(trpdMem, 1U, cqRingNum);
    
    
        /* Set TR descriptor reload information */
        CSL_udmapCppi5TrSetReload((CSL_UdmapCppi5TRPD *)trpdMem, 0x1FFU, 0U);
    
    
        /* Update TRPD */
        CSL_UdmapTR15  *pTr;
        pTr = UdmaUtils_getTrpdTr15Pointer(trpdMem, 0U);
        pTr->flags    = CSL_FMK(UDMAP_TR_FLAGS_TYPE, CSL_UDMAP_TR_FLAGS_TYPE_4D_BLOCK_MOVE_REPACKING_INDIRECTION);
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_STATIC, 0U);
    
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_EOL, CSL_UDMAP_TR_FLAGS_EOL_MATCH_SOL_EOL);
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_EVENT_SIZE, CSL_UDMAP_TR_FLAGS_EVENT_SIZE_COMPLETION);
    
    
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_TRIGGER0, CSL_UDMAP_TR_FLAGS_TRIGGER_GLOBAL0);
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_TRIGGER0_TYPE, CSL_UDMAP_TR_FLAGS_TRIGGER_TYPE_ICNT1_DEC);
    
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_TRIGGER1, CSL_UDMAP_TR_FLAGS_TRIGGER_NONE);
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_TRIGGER1_TYPE, CSL_UDMAP_TR_FLAGS_TRIGGER_TYPE_ALL);
    
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_CMD_ID, 0x25U);  /* This will come back in TR response */
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_SA_INDIRECT, 0U);
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_DA_INDIRECT, 0U);
    
        pTr->flags   |= CSL_FMK(UDMAP_TR_FLAGS_EOP, 1U);
    
        pTr->icnt0 = (length);
        pTr->icnt1 = 1U;
        pTr->icnt2 = 1U;
        pTr->icnt3 = 1U;
    
        pTr->dim1     = (int32_t)pTr->icnt0;
        pTr->dim2     = (int32_t)pTr->icnt0 * (int32_t)pTr->icnt1;
        pTr->dim3     = (int32_t)pTr->icnt0 * (int32_t)pTr->icnt1 * (int32_t)pTr->icnt2;
    
        pTr->dicnt0 = pTr->icnt0;
        pTr->dicnt1 = pTr->icnt1;
        pTr->dicnt2 = pTr->icnt2;
        pTr->dicnt3 = pTr->icnt3;
    
        pTr->ddim1    = (int32_t)pTr->dicnt0;
        pTr->ddim2    = (int32_t)pTr->dicnt0 * (int32_t)pTr->dicnt1;
        pTr->ddim3    = (int32_t)pTr->dicnt0 * (int32_t)pTr->dicnt1 * (int32_t)pTr->dicnt2;
    
        pTr->addr     = (uint64_t) Udma_defaultVirtToPhyFxn(srcBuf, 0U, NULL);
        pTr->daddr    = (uint64_t) Udma_defaultVirtToPhyFxn(destBuf, 0U, NULL);
    
        /* Perform cache writeback */
        CacheP_wb(trpdMem, UDMA_TEST_TRPD_SIZE, CacheP_TYPE_ALL);
        CacheP_wbInv( (uint8_t *)destBuf, length, CacheP_TYPE_ALL);
    
        /* Channel enable */
        status = Udma_chEnable(chHandle);
        DebugP_assert(UDMA_SOK == status);
    
        /* Get UDMA event trigger */
        globalEvent0 = Udma_chGetTriggerEvent(chHandle, CSL_UDMAP_TR_FLAGS_TRIGGER_GLOBAL0);
    
        /* Configure Interrupt Aggregator, L2G */
        configure_intaggrL2G(L2G_EVENT_ID0, globalEvent0);
    
        /* Submit TRPD to channel */
        status = Udma_ringQueueRaw(Udma_chGetFqRingHandle(chHandle), trpdMemPhy);
    
        return status;
    }
    
    void udma_event_register_settings(void)
    {
        Udma_EventHandle    cqEventHandle;
        Udma_EventPrms      cqEventPrms;
        Udma_DrvHandle      drvHandle = &gUdmaDrvObj[CONFIG_UDMA0];
    
        int32_t    retVal;
    
        cqEventHandle = &gConfigUdma0BlkCopyCqEventObj[0U];
        gConfigUdma0BlkCopyCqEventHandle[0U] = cqEventHandle;
        UdmaEventPrms_init(&cqEventPrms);
    
        cqEventPrms.eventType         = UDMA_EVENT_TYPE_TR;
        cqEventPrms.eventMode         = UDMA_EVENT_MODE_SHARED;
        cqEventPrms.chHandle          = gConfigUdma0BlkCopyChHandle[0];
        cqEventPrms.controllerEventHandle = Udma_eventGetGlobalHandle(drvHandle);
        cqEventPrms.eventCb           = gConfigUdma0BlkCopyCqEventCb[0];
    
        retVal = Udma_eventRegister(drvHandle, cqEventHandle, &cqEventPrms);
        DebugP_assert(UDMA_SOK == retVal);
    }
    
    void GPIO_TIRG_DMA_CB(Udma_EventHandle eventHandle, uint32_t eventType, void *appData)
    {
        uint32_t pinMask;
        uint32_t gpioIntrStatus;
        int32_t status = SystemP_SUCCESS;
    
        uint32_t        gpioBankNum;
    
        gpioBankNum = GPIO_GET_BANK_INDEX(GPIO_TIRG_DMA_PIN);
    
        if(UDMA_EVENT_TYPE_TR == eventType)
        {
            gpioIntrStatus = GPIO_getBankIntrStatus(gGpioBaseAddr, gpioBankNum);
            pinMask = GPIO_GET_BANK_BIT_MASK(GPIO_TIRG_DMA_PIN);
    
            if(gpioIntrStatus & pinMask)
            {
                GPIO_clearBankIntrStatus(gGpioBaseAddr, gpioBankNum, gpioIntrStatus);
            }
        }
    
        gCbCount++;
    }
    
    static void App_udmaInitBuf(uint8_t *srcBuf, uint8_t *destBuf, uint32_t length)
    {
        uint32_t        i;
    
        for(i = 0U; i < length; i++)
        {
            srcBuf[i] = i;
            destBuf[i] = 0xA5U;
        }
        /* Writeback source and destination buffer */
        CacheP_wb(srcBuf, length, CacheP_TYPE_ALLD);
        CacheP_wb(destBuf, length, CacheP_TYPE_ALLD);
    
        return;
    }
    
    static void App_udmaCompareBuf(uint8_t *srcBuf, uint8_t *destBuf, uint32_t length)
    {
        uint32_t        i, flag=0;
    
        /* Invalidate destination buffer */
        CacheP_inv(destBuf, length, CacheP_TYPE_ALLD);
        for(i = 0U; i < length; i++)
        {
            if(srcBuf[i] != destBuf[i])
            {
                flag = 1U;
                break;
            }
        }
    
        for(i = 0U; i < length; i++)
        {
            destBuf[i] = 0U;
        }
        CacheP_wb(destBuf, length, CacheP_TYPE_ALLD);
    
        if (flag)
        {
            gXferErrCnt++;
            DebugP_log("Error count %d: gXferErrCnt");
        }
    
        return;
    }
    
    void *gpio_trigger_bcdma_main(void *args)
    {
        int32_t         retVal = UDMA_SOK;
        Udma_ChHandle   chHandle;
        uint8_t        *srcBuf = &gUdmaTestSrcBuf[0U];
        uint8_t        *destBuf = &gUdmaTestDestBuf[0U];
        uint32_t        length = UDMA_TEST_NUM_BYTES;
        uint8_t        *trpdMem = &gUdmaTestTrpdMem[0U];
        uint64_t        trpdMemPhy;
        uint32_t        globalEvent0;
        uint32_t        gpioBankNum;
    
        /* Initialize MAIN domain GPIO IR routes */
        /* Configure GPIO interrupt route to interrupt aggregator */
        Sciclient_gpioIrqSet();
    
        DebugP_log("GPIO Trigger BCDMA Test Started ...\r\n");
    
        /*
         *  Configure GPIO
         */
    
        /* Address translate for GPIO base address */
        gGpioBaseAddr = (uint32_t) AddrTranslateP_getLocalAddr(gGpioBaseAddr);
        /* Setup GPIO for interrupt generation */
    //    GPIO_setDirMode(gGpioBaseAddr, GPIO_TIRG_DMA_PIN, GPIO_TIRG_DMA_DIR);
    //    GPIO_setTrigType(gGpioBaseAddr, GPIO_TIRG_DMA_PIN, GPIO_TIRG_DMA_TRIG_TYPE);
    
        /* Enable GPIO Bank interrupts */
        gpioBankNum = GPIO_GET_BANK_INDEX(GPIO_TIRG_DMA_PIN);
        GPIO_bankIntrEnable(gGpioBaseAddr, gpioBankNum);
    
        /*
         *  Configure UDMA / BCDMA
         */
    
        /* Get UDMA handle */
        chHandle = gConfigUdma0BlkCopyChHandle[0];  /* Has to be done after driver open */
    
        udma_event_register_settings();
    
        /* Init buffers and TR packet descriptor */
        App_udmaInitBuf(srcBuf, destBuf, length);
    
        /* Get physical memory address for TRPD */
        trpdMemPhy = (uint64_t) Udma_defaultVirtToPhyFxn(trpdMem, 0U, NULL);
    
        PsramDma_udmaUpdateSubmitTR(chHandle, trpdMem, destBuf, srcBuf, length,trpdMemPhy);
    
        uint8_t last_gCbCount = 0;
    
        CycleCounterP_reset();
    
        while (gRunFlag == 1)
        {
            last_gCbCount = gCbCount;
    
            /*
             * A second trigger will lead to a scenario where one trigger enters the interrupt routine twice.
             * However, executing CacheP_wbInvAll(CacheP_TYPE_ALL) can prevent this situation from occurring.
             * */
            // CacheP_wbInvAll(CacheP_TYPE_ALL);
    
            while(last_gCbCount == gCbCount)
            {
    
            }
    
            DebugP_log("\r\n");
    
            App_udmaCompareBuf(srcBuf, destBuf, length);
    
            if (gCbCount - last_gCbCount > 1)
            {
                DebugP_log("Error: Consecutive entry into interrupt twice!\r\n");
            }
    
            DebugP_log("gCbCount: %d\r\n", gCbCount);
        }
    
        return NULL;
    }
    

  • Using a button to trigger (GPIO_43 on the EVM board), after the second activation, the behavior becomes abnormal.

  • Hello Ryan,

    Which type of GPIO trigger you are using ?

    Either it is rising or falling or both edges ?

    I assumed that you are running this code on A53 . Please confirm it .

    Can you please check on R5F core as well?

    Regards,

    Anil.

  • 1. The GPIO configuration is as shown in the following diagram:

    2. Yes, it is currently running on the A53. I will provide the test results on the R5 later.

  • Hi Anil:

    When running on the R5, the observed behavior is normal and does not exhibit the issues encountered on the A53.

    The requirement of our project is to implement this feature on the A53.

    Regards

    Ryan

  • Hello Ryan,

    Yes, your observation is correct, and I have also not seen this issue on R5F core and for that reason I have asked you to do tests on R5F core .

    I suspect the issue with the interrupt clear is not proper on the A53 core side and I need to reproduce the issue at my side and let you know the status.

    Regards,

    Anil.

  • Hello Ryan,

    I am able to reproduce  the issue on my side, and I am also getting this issue on A53 and not in R5F core.

    Do you get any Root cause on this ?

    I suspect this issue comes in 3 ways .

    1. GPIO interrupt is not clearing 

    2. GPIO debounce

    3. Level Interrupt is not Clearing 

    I have verified with the 1st one and there is no problem with GPIO interrupt clearing.

    I suspect that this issue might come to 2 and 3 points.

    On, R5F side we are clearing Level interrupts in the master ISR routine, but we are not clearing Level interrupts on A53 core .

     Do you have any suggestions ?

    Regards,

    Anil.

  • Hi Anil:

    I have not found the root cause. 

    I think your analysis makes a lot of sense.

    We truly appreciate your support. Regarding this issue, we would be grateful if you could give it extra attention.

    Regards

    Ryan

     

  • I am able to reproduce  the issue on my side, and I am also getting this issue on A53 and not in R5F core.

    On, R5F side we are clearing Level interrupts in the master ISR routine, but we are not clearing Level interrupts on A53 core .

     Do you have any suggestions ?

    Looking at the MCU+ release notes https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/RELEASE_NOTES_09_02_01_PAGE.html  we state (copied below and see in red)

    GPIO R5F, M4F, A53 YES No Basic input/output, GPIO as interrupt GPIO as interrupt is not tested for A53.

    Based on this thread it looks like it is not working. But I can see this is tested on the sister device (AM62x) A53's.

    https://software-dl.ti.com/mcu-plus-sdk/esd/AM62X/latest/exports/docs/api_guide_am62x/RELEASE_NOTES_09_02_01_PAGE.html (no mention of not tested on A53)

    https://software-dl.ti.com/mcu-plus-sdk/esd/AM62X/latest/exports/docs/api_guide_am62x/DRIVERS_GPIO_PAGE.html#autotoc_md522 

    Short term I'd copy this over.

  • Hi Pekka:

    Can it be understood that there is no such issue on AM62?

    What is the follow-up solution on AM64 ?

    Regards

    Ryan

  • Can it be understood that there is no such issue on AM62?

    Based on the AM62x MCU+ SDK release this looks like a tested and supported feature in 9.2.1. I have not tested it myself.

    What is the follow-up solution on AM64 ?

    This feature is not supported on in 9.2.1. Based on your testing it does not work on AM64x. Because it works in AM62x A53 it should not be a large effort, I will file a marketing requirement to add the feature to AM64x. Earliest it will be in the MCU+ SDK is probably 10.1 which is November 2024.

  • Hi Pekka:

    The implementation of this feature is of paramount importance to our project, which is currently on a tight schedule with a July deadline. Given the urgency of the situation, would it be possible to expedite the resolution of the outstanding bug?

    Regards

    Ryan

  • Hello Ryan,

    I have verified GPIO interrupt on A53 and which is working fine .

    Coming to our test code GPIO with the DMA , I have noticed that DMA triggers for correctly for 3 times and after that DMA  interrupt triggers extra times .

    I can check with the internal team on this and will come back ..

    Regards,

    Anil.

  • Hello Ryan,

    You can try to replace configure_intaggrL2G API in your application with the change below and see if this works. 


    static void configure_intaggrL2G(uint32_t localEvent, uint32_t globalEvent)
    {
        uint64_t eventRegOffset = CSL_DMASS0_INTAGGR_L2G_BASE + (localEvent * 0x20U);
        CSL_REG64_WR(eventRegOffset, ( (1U << 31U) | (globalEvent & 0xFFFFU) ) ); /* pulse event */
    }

    Regards,

    Anil.

  • Hello Ryan, 

    Any update? 

    I assume that with the above change you are getting an interrupt only once. 

    Please let us know if you still have issues. 

    Regards, 

    Anil. 

  • Hi Anil:

     

    Your solution has been validated as viable.

    A sincere thank you to the TI team for their support.

     

    Regards

    Ryan