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.

AM2634-Q1: Questions about Flash_read API's parameter

Part Number: AM2634-Q1

Hi experts, 

The Flash_read API has three critical parameters: offset, buf pointer and length. I want to know the constraints for offset and length. From my test, if the length/offset is not a multiple of 4, the read-out data will be wrong. 

When offset = 0 and the length = 105: 

The actual Flash data is: 

When offset = 2 and the length = 104. First 62-byte all wrong: 

And the last 10 bytes are also wrong: 

CPU directly read doesn't have this problem. Please give your comments on this problem. 

Best Regards, 

Will 

  • In above test, the EDMA is enabled. 

  • Hi Will,

    Thanks for reaching out to us. I will get back you on this.

    Thanks and Regards,
    Aakash

  • Hi Will,

    I don't think it has anything to do with flash configurations. This is more related to qspi_edma implementation and how the edma is reading the data. I see that the driver is implemented with A-Synchronized Transfers.

    ACnt is 1 as len 105 < 32K, BCnt is 105 and CCnt is and this is a single dimensional transfer.

    There is no limitation in the edma hardware with respect to alignments as well. So I will replicate the issue on my end and then reach out to you, incase I have any discovery.

    Best Regards,
    Aakash

  • Hi Aakash, 

    Thanks for your comments. Maybe SAM and DAM also should be checked. BTW, if A-synchronized transfer is used in SDK driver, multiple manual trigger should be added but in the qspi_edmaTransfer API I can only see one time calling, a little confused for me. Wait for your test, thanks again. 

    Regards, 

    Will 

  • Hi Aakash, 

    How's your test going? According to my test, the src address and length should be multiple of 4 bytes. In order to support arbitrary length and src address, I modified the qspi_edma.c file, as attached here: 

    qspi_edma.c
    /*
     *  Copyright (C) 2021 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /**
     *  \file qspi_edma.c
     *
     *  \brief File containing EDMA Driver APIs implementation for QSPI.
     *
     */
    
    /* ========================================================================== */
    /*                             Include Files                                  */
    /* ========================================================================== */
    
    #include <drivers/qspi/v0/edma/qspi_edma.h>
    #include <drivers/soc.h>
    #include <kernel/dpl/CacheP.h>
    
    /* ========================================================================== */
    /*                           Macros & Typedefs                                */
    /* ========================================================================== */
    
    /* Value for A count*/
    #define EDMA_QSPI_A_COUNT           (1U)
    /* Max Value for EDMA count */
    #define MAX_EDMA_COUNT              (31*1024)
    /* Value for C count */
    #define EDMA_QSPI_C_COUNT           (1U)
    /* Event queue to be used  */
    #define EDMA_QSPI_EVT_QUEUE_NO      (0U)
    
    /* ========================================================================== */
    /*                 Internal Function Declarations                             */
    /* ========================================================================== */
    
    static void QSPI_edmaIsrFxn(Edma_IntrHandle intrHandle, void *args);
    
    /* ========================================================================== */
    /*                          Function Definitions                              */
    /* ========================================================================== */
    
    void QSPI_EdmaParams_init(QSPI_EdmaParams *edmaParams)
    {
        if( edmaParams != NULL)
        {
            edmaParams->edmaTcc = EDMA_RESOURCE_ALLOC_ANY;
            edmaParams->edmaChId = EDMA_RESOURCE_ALLOC_ANY;
            edmaParams->edmaParam = EDMA_RESOURCE_ALLOC_ANY;
            edmaParams->edmaRegionId = 0U;
            edmaParams->edmaBaseAddr = 0U;
            edmaParams->isIntEnabled = 0U;
        }
    }
    
    int32_t QSPI_edmaChannelConfig(QSPI_Handle qspiHandle, uint32_t edmaInst)
    {
        uint32_t            baseAddr, regionId, dmaCh, tcc, param;
        int32_t             status = SystemP_FAILURE;
        uint32_t            isEdmaInterruptEnabled;
        QSPI_Object         *object = ((QSPI_Config *)qspiHandle)->object;
        QSPI_EdmaParams     *edmaParams = &object->qspiEdmaParams;
        Edma_IntrObject     *edmaIntrObject = &edmaParams->edmaIntrObj;
        EDMA_Handle         qspiEdmaHandle = EDMA_getHandle(edmaInst);
        SemaphoreP_Object   *gEdmaTransferDoneSem = &edmaParams->gEdmaTransferDoneSem;
    
        QSPI_EdmaParams_init(edmaParams);
        /* Read base address of allocated EDMA instance */
        baseAddr = EDMA_getBaseAddr(qspiEdmaHandle);
    
        if(baseAddr != 0)
        {
            status = SystemP_SUCCESS;
    
            /* Check if interrupt is enabled */
            isEdmaInterruptEnabled = EDMA_isInterruptEnabled(qspiEdmaHandle);
            /* Read the region ID of the EDMA instance */
            regionId = EDMA_getRegionId(qspiEdmaHandle);
    
            if(regionId < SOC_EDMA_NUM_REGIONS)
            {
                /* Allocate EDMA channel for QSPI transfer */
                dmaCh = EDMA_RESOURCE_ALLOC_ANY;
                status += EDMA_allocDmaChannel(qspiEdmaHandle, &dmaCh);
    
                /* Allocate EDMA TCC for QSPI transfer */
                tcc = EDMA_RESOURCE_ALLOC_ANY;
                status += EDMA_allocTcc(qspiEdmaHandle, &tcc);
    
                /* Allocate a Param ID for QSPI transfer */
                param = EDMA_RESOURCE_ALLOC_ANY;
                status += EDMA_allocParam(qspiEdmaHandle, &param);
                if(status == SystemP_SUCCESS)
                {
                    EDMA_configureChannelRegion(baseAddr, regionId, EDMA_CHANNEL_TYPE_DMA,
                        dmaCh, tcc, param, EDMA_QSPI_EVT_QUEUE_NO);
    
                    if(isEdmaInterruptEnabled == TRUE)
                    {
                        status = SemaphoreP_constructBinary(gEdmaTransferDoneSem, 0);
                        DebugP_assert(SystemP_SUCCESS == status);
    
                        /* Register interrupt */
                        edmaIntrObject->tccNum = tcc;
                        edmaIntrObject->cbFxn  = &QSPI_edmaIsrFxn;
                        edmaIntrObject->appData = (void *) gEdmaTransferDoneSem;
                        status = EDMA_registerIntr(qspiEdmaHandle, edmaIntrObject);
                        DebugP_assert(status == SystemP_SUCCESS);
                    }
    
                    /* Store the EDMA paramters and handle*/
                    object->qspiEdmaHandle  = qspiEdmaHandle;
                    edmaParams->edmaBaseAddr = baseAddr;
                    edmaParams->edmaRegionId = regionId;
                    edmaParams->edmaParam = param;
                    edmaParams->edmaChId = dmaCh;
                    edmaParams->edmaTcc = tcc;
                    edmaParams->isIntEnabled = isEdmaInterruptEnabled;
                }
                if(status != SystemP_SUCCESS)
                {
                    if(dmaCh != EDMA_RESOURCE_ALLOC_ANY)
                    {
                        EDMA_freeDmaChannel(qspiEdmaHandle, &dmaCh);
                    }
                    if(tcc != EDMA_RESOURCE_ALLOC_ANY)
                    {
                        EDMA_freeTcc(qspiEdmaHandle, &tcc);
                    }
                    if(param != EDMA_RESOURCE_ALLOC_ANY)
                    {
                        EDMA_freeParam(qspiEdmaHandle, &param);
                    }
                }
            }
            else
            {
                status = SystemP_FAILURE;
            }
        }
        return status;
    }
    
    void QSPI_edmaTransfer(void* dst, void* src, uint32_t length,
                           QSPI_Handle qspiHandle)
    {
        uint32_t            baseAddr, regionId, dmaCh, tcc, param;
        QSPI_Object         *object = ((QSPI_Config *)qspiHandle)->object;
        QSPI_EdmaParams     *edmaParams = &object->qspiEdmaParams;
        EDMACCPaRAMEntry   edmaParam;
    
        /* Fetch the EDMA paramters for QSPI transfer */
        baseAddr = edmaParams->edmaBaseAddr;
        regionId = edmaParams->edmaRegionId;
        dmaCh    = edmaParams->edmaChId;
        tcc      = edmaParams->edmaTcc;
        param    = edmaParams->edmaParam;
    
        CacheP_wb(src, length*EDMA_QSPI_A_COUNT, CacheP_TYPE_ALL);
        CacheP_wb(dst, length*EDMA_QSPI_A_COUNT, CacheP_TYPE_ALL);
    
        /* Program Param Set */
        EDMA_ccPaRAMEntry_init(&edmaParam);
    
        uint32_t tmp_src = ( 0x4u - (uint32_t)src & 0x3u ) % 0x4u ;
        if ( tmp_src != 0u )
        {
            uint8_t * psrc = (uint8_t *) src ;
            uint8_t * pdst = (uint8_t *) dst ;
            uint8_t i ;
            for( i = 0u ; i < tmp_src ; i++ )
            {
                * (pdst + i) = * (psrc + i) ;
            }
        }
    
        CacheP_wb(src, length*EDMA_QSPI_A_COUNT, CacheP_TYPE_ALL);
        CacheP_wb(dst, length*EDMA_QSPI_A_COUNT, CacheP_TYPE_ALL);
    
        edmaParam.srcAddr       = (uint32_t) SOC_virtToPhy(src + tmp_src);
        edmaParam.destAddr      = (uint32_t) SOC_virtToPhy(dst + tmp_src);
        length -= tmp_src ;
    
        uint32_t tmp = length % 4u ;
        if ( tmp != 0u )
        {
            edmaParam.aCnt          = (uint16_t) (length - tmp)  ;
            edmaParam.bCnt          = 1u ;
            edmaParam.cCnt          = (uint16_t) EDMA_QSPI_C_COUNT;
            edmaParam.bCntReload    = edmaParam.bCnt ;
            edmaParam.srcBIdx       = (int16_t) edmaParam.aCnt;
            edmaParam.destBIdx      = (int16_t) edmaParam.aCnt;
            edmaParam.srcCIdx       = 0u ;
            edmaParam.destCIdx      = 0u ;
            edmaParam.linkAddr      = 0xFFFFU;
            edmaParam.opt          |=
                (EDMA_OPT_TCINTEN_MASK | EDMA_OPT_ITCINTEN_MASK |
                 (((tcc) << EDMA_OPT_TCC_SHIFT) & EDMA_OPT_TCC_MASK));
    
            EDMA_setPaRAM(baseAddr, param, &edmaParam);
            EDMA_enableTransferRegion(baseAddr, regionId, dmaCh,
                     EDMA_TRIG_MODE_MANUAL);
            if(edmaParams->isIntEnabled == true)
            {
                SemaphoreP_pend(&edmaParams->gEdmaTransferDoneSem, SystemP_WAIT_FOREVER);
            }
            else
            {
                /* Poll for transfer completion */
                while(EDMA_readIntrStatusRegion(baseAddr, regionId, tcc) != 1);
    
                EDMA_clrIntrRegion(baseAddr, regionId, tcc);
            }
    
            edmaParam.srcAddr       = (uint32_t) SOC_virtToPhy((void *)edmaParam.srcAddr + length - tmp);
            edmaParam.destAddr      = (uint32_t) SOC_virtToPhy((void *)edmaParam.destAddr + length - tmp);
            edmaParam.aCnt          = 1u ;
            edmaParam.bCnt          = (uint16_t) tmp ;
            edmaParam.bCntReload    = edmaParam.bCnt ;
            edmaParam.srcBIdx       = (int16_t) edmaParam.aCnt;
            edmaParam.destBIdx      = (int16_t) edmaParam.aCnt;
            EDMA_setPaRAM(baseAddr, param, &edmaParam);
            uint32_t i ;
            for(i = 0 ; i<edmaParam.bCnt ; i++)
            {
                  EDMA_enableTransferRegion(baseAddr, regionId, dmaCh,
                           EDMA_TRIG_MODE_MANUAL);
            }
            if(edmaParams->isIntEnabled == true)
            {
                SemaphoreP_pend(&edmaParams->gEdmaTransferDoneSem, SystemP_WAIT_FOREVER);
            }
            else
            {
                /* Poll for transfer completion */
                while(EDMA_readIntrStatusRegion(baseAddr, regionId, tcc) != 1);
    
                EDMA_clrIntrRegion(baseAddr, regionId, tcc);
            }
        }
        else
        {
            edmaParam.aCnt          = (uint16_t) (length) ;
            edmaParam.bCnt          = 1u ;
            edmaParam.cCnt          = (uint16_t) EDMA_QSPI_C_COUNT;
            edmaParam.bCntReload    = edmaParam.bCnt ;
            edmaParam.srcBIdx       = (int16_t) edmaParam.aCnt;
            edmaParam.destBIdx      = (int16_t) edmaParam.aCnt;
            edmaParam.srcCIdx       = 0u ;
            edmaParam.destCIdx      = 0u ;
            edmaParam.linkAddr      = 0xFFFFU;
            edmaParam.opt          |=
                (EDMA_OPT_TCINTEN_MASK | EDMA_OPT_ITCINTEN_MASK |
                 (((tcc) << EDMA_OPT_TCC_SHIFT) & EDMA_OPT_TCC_MASK));
    
            EDMA_setPaRAM(baseAddr, param, &edmaParam);
    
            EDMA_enableTransferRegion(baseAddr, regionId, dmaCh,
                     EDMA_TRIG_MODE_MANUAL);
    
            if(edmaParams->isIntEnabled == true)
            {
                SemaphoreP_pend(&edmaParams->gEdmaTransferDoneSem, SystemP_WAIT_FOREVER);
            }
            else
            {
                /* Poll for transfer completion */
                while(EDMA_readIntrStatusRegion(baseAddr, regionId, tcc) != 1);
    
                EDMA_clrIntrRegion(baseAddr, regionId, tcc);
            }
        }
    
    //    edmaParam.bCnt          = (uint16_t) (length/edmaParam.aCnt);
    
    //    edmaParam.cCnt          = (uint16_t) EDMA_QSPI_C_COUNT;
    //    edmaParam.bCntReload    = (uint16_t) (length/edmaParam.aCnt);
    //    edmaParam.bCntReload    = edmaParam.bCnt ;
    
    //    edmaParam.srcBIdx       = (int16_t) edmaParam.aCnt;
    //    edmaParam.destBIdx      = (int16_t) edmaParam.aCnt;
    //    edmaParam.srcBIdx       = 1u ; // 0u ;
    //    edmaParam.destBIdx      = 1u ; // 0u ;
    
    //    edmaParam.srcCIdx       = (int16_t) EDMA_QSPI_A_COUNT;
    //    edmaParam.destCIdx      = (int16_t) EDMA_QSPI_A_COUNT;
    //    edmaParam.srcCIdx       = 0u ;
    //    edmaParam.destCIdx      = 0u ;
    
    //    edmaParam.linkAddr      = 0xFFFFU;
    //    edmaParam.opt          |=
    //        (EDMA_OPT_TCINTEN_MASK | EDMA_OPT_ITCINTEN_MASK | EDMA_OPT_SYNCDIM_MASK |
    //         (((tcc) << EDMA_OPT_TCC_SHIFT) & EDMA_OPT_TCC_MASK));
    
    //    edmaParam.opt          |=
    //        (EDMA_OPT_TCINTEN_MASK | EDMA_OPT_ITCINTEN_MASK |
    //         (((tcc) << EDMA_OPT_TCC_SHIFT) & EDMA_OPT_TCC_MASK));
    //
    //    EDMA_setPaRAM(baseAddr, param, &edmaParam);
    //
    //    uint32_t i ;
    //    for(i = 0 ; i<edmaParam.bCnt ; i++)
    //    {
    //            EDMA_enableTransferRegion(baseAddr, regionId, dmaCh,
    //                     EDMA_TRIG_MODE_MANUAL);
    //    }
    
        /* Set manual trigger to start QSPI transfer */
    //    EDMA_enableTransferRegion(baseAddr, regionId, dmaCh,
    //             EDMA_TRIG_MODE_MANUAL);
    
    //    if(edmaParams->isIntEnabled == true)
    //    {
    //        SemaphoreP_pend(&edmaParams->gEdmaTransferDoneSem, SystemP_WAIT_FOREVER);
    //    }
    //    else
    //    {
    //        /* Poll for transfer completion */
    //        while(EDMA_readIntrStatusRegion(baseAddr, regionId, tcc) != 1);
    //
    //        EDMA_clrIntrRegion(baseAddr, regionId, tcc);
    //    }
    
        length += tmp_src ;
    
        if((length > MAX_EDMA_COUNT) && (length % MAX_EDMA_COUNT != 0))
        {
            edmaParam.srcAddr       = (uint32_t) SOC_virtToPhy(src) + (MAX_EDMA_COUNT * (length / MAX_EDMA_COUNT));
            edmaParam.destAddr      = (uint32_t) SOC_virtToPhy(dst) + (MAX_EDMA_COUNT * (length / MAX_EDMA_COUNT));
            edmaParam.aCnt      = (uint16_t) EDMA_QSPI_A_COUNT;
            edmaParam.bCnt      = (uint16_t) (length % MAX_EDMA_COUNT);
            edmaParam.srcBIdx   = (int16_t) EDMA_QSPI_A_COUNT;
            edmaParam.destBIdx  = (int16_t) EDMA_QSPI_A_COUNT;
    
            EDMA_setPaRAM(baseAddr, param, &edmaParam);
    
            /* Set manual trigger to start QSPI transfer */
            EDMA_enableTransferRegion(baseAddr, regionId, dmaCh,
                    EDMA_TRIG_MODE_MANUAL);
    
            if(edmaParams->isIntEnabled == TRUE)
            {
                SemaphoreP_pend(&edmaParams->gEdmaTransferDoneSem, SystemP_WAIT_FOREVER);
            }
            else
            {
                /* Poll for transfer completion */
                while(EDMA_readIntrStatusRegion(baseAddr, regionId, tcc) != 1);
    
                EDMA_clrIntrRegion(baseAddr, regionId, tcc);
            }
        }
    
        CacheP_inv(dst, length*EDMA_QSPI_A_COUNT, CacheP_TYPE_ALL);
    }
    
    int32_t QSPI_edmaChannelFree(QSPI_Handle qspiHandle)
    {
        int32_t             status = SystemP_SUCCESS;
        uint32_t            baseAddr, regionId, dmaCh, tcc, param;
        QSPI_Object         *object = ((QSPI_Config *)qspiHandle)->object;
        QSPI_EdmaParams     *edmaParams = &object->qspiEdmaParams;
    
        /* Fetch the EDMA paramters */
        baseAddr = edmaParams->edmaBaseAddr;
        regionId = edmaParams->edmaRegionId;
        dmaCh    = edmaParams->edmaChId;
        tcc      = edmaParams->edmaTcc;
        param    = edmaParams->edmaParam;
    
        /* Free channel */
        EDMA_freeChannelRegion(baseAddr, regionId, EDMA_CHANNEL_TYPE_DMA,
             dmaCh, EDMA_TRIG_MODE_MANUAL, tcc, EDMA_QSPI_EVT_QUEUE_NO);
    
        if(edmaParams->isIntEnabled == TRUE)
        {
            status = EDMA_unregisterIntr(object->qspiEdmaHandle, &edmaParams->edmaIntrObj);
            SemaphoreP_destruct(&edmaParams->gEdmaTransferDoneSem);
        }
        /* Free the EDMA resources managed by driver. */
        status += EDMA_freeDmaChannel(object->qspiEdmaHandle, &dmaCh);
        status += EDMA_freeTcc(object->qspiEdmaHandle, &tcc);
        status += EDMA_freeParam(object->qspiEdmaHandle, &param);
    
        return status;
    }
    
    static void QSPI_edmaIsrFxn(Edma_IntrHandle intrHandle, void *args)
    {
        SemaphoreP_Object *semObjPtr = (SemaphoreP_Object *)args;
        DebugP_assert(semObjPtr != NULL);
        SemaphoreP_post(semObjPtr);
    }
    

    And now I can read the Flash at any address and length. Please help assessing it if possible and also wait for your test results. 

    Regards, 

    Will 

  • Hi Will,

    Let me share my observation with you.

    Case I -

    For Flash_write, offset = 0x00040000 and len = 32768
    For Flash_read, offset = 0x00040000 and len = 105

    I see corruptions at 2 locations.

    Case II -

    For Flash_write, offset = 0x00040000 and len = 32768
    For Flash_read, offset = 0x00040002 and len = 105

    1. Observation states that even though the offset is 0x00040002 and the data should start from 2, it starts from 4.

    2. Some repetition is observed in between. But this somehow fixes the problem.

    3. There are some major corruptions being seen at the end of the read cycles which is 10 bytes.

    I will raise an internal ticket for this observation and let the expert comment on the same. Also I will take a look at your implementations to access the same.

    Thanks and Regards,
    Aakash

  • Hi Aakash, 

    Thanks. Further test from my side shows that if using EDMA transfer data from SRAM to another space of SRAM, the src address and length doesn't matter and always work. I think it shows that the EDMA TPTC's read/write controller are both ok? 

    Regards, 

    Will 

  • Hi Will,

    I have raised an internal ticket and the issue seems to be genuine. I will let the experts take a look at this issue.

    In the mean time, can you work with the workaround that you have created ?

    Thanks and Regards,
    Aakash

  • Hi Aakash, 

    I will let the experts take a look at this issue.

    Thanks for your help.

    can you work with the workaround that you have created ?

    Yes, my customer has implemented it in their application to read data out from Flash and can work by now (length < 31k). But I think the workaround can't be used in SBL since SBL may issue transfer that length > 31k.  

    Regards, 

    Will 

  • Hi Will,

    I will increase the priority for the issue stating that it is blocking one of the customers.
    I will share the ticket number with you so you can add more details if needed.

    Hope that helps.

    Best Regards,
    Aakash

  • Hi Will,

    This is recognized as a hardware limitation of QSPI. Read via DMA will not work and Read via CPU may work but is not recommended. This stands true for both address and length. Hence, you have to make sure in your application that address and the length has to be 4B aligned.

    We will definitely fill this information by the next release of TRM.

    Best Regards,
    Aakash

  • Hi Aakash, 

    Thanks for your confirmation. 

    Regards, 

    Will