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.

PROCESSOR-SDK-TDAX: TDA2x: Information regarding EVE EDMA time calculation

Part Number: PROCESSOR-SDK-TDAX

Hi,

I am working on 640 * 480 image size and written kernel to transpose image using EVE auto increment usecase.

Please note that we are using non-BAM framework.

To transpose the image, as per my calculations, it should take ~0.3m sec (196000 cycles) while on the board, it is taking ~1m sec (650000 cycles), which is not justifiable.

As per our analysis, DMA is taking more time as compare to VCOP processing. To ensure this, we have disable kernel processing and found DMA operations are taking ~1m sec.

So, I need information regarding any provision/method to find DMA approximate time.

Please suggest some way to optimize DMA time.

Please help me with this.

Regards,

Prashant Kothari

  • Hi Prashant,
    In general you can assume 0.25 VCOP cycles per byte of transfer using DMA.

    Regards,
    Anshu
  • Hi Anshu,

    Thanks for the reply.

    I am using _tsc function to calculate cycle. However, number of cycles are not same on board and simulator.

    Ideally, DMA should take less time and Kernel time should be the execution time. But In my image transpose kernel, EDMA is running in foreground (Disabled kernel processing to know DMA time). That is the reason, I am getting more time for it's execution.

    Can you please suggest mechanism to reduce DMA time in Auto increment usecase?

    Regards,
    Prashant Kothari
  • Prashant,
    If you are using _tsc function to calculate the number of cycles then you will have to multiply it by 2 to get the number of VCOP cycles. So for your case as input is 640x480 and I am assuming output is of size 480x640 then the total number of VCOP cycles required for DMA should be 2 *
    (640*480*2)/4. If your kernel cycles is less than these cycles then DMA will come in foreground.
    Now this is a guideline and actual DMA cycles will actually depend on the overall system usage of DDR. If there are other algorithms running which are also accessing DDR then number of cycles can be higher. In general if you are running your algorithm in standalone mode then cycles taken by DMA will be close to what we estimated.

    Regards,
    Anshu
  • Hi Anshu,

    Thanks for the quick response.

    In the above cycle calculations, we have considered multiplication by 2 (VCOP cycles).

    As per your suggestion, we have disabled all the processing and Transpose kernel is running standalone.

    In auto increment usecase,
    Init time = ~275 usec
    Init + DMA = ~1 msec
    Init + DMA + Kernel = ~1.13 msec

    Ideally with our calculation DMA should take ~236 usec. So, Toal DMA time should be (init + DMA) ~500 usec. However, in our case it is taking ~800 usec.

    It will be great if you could suggest some way to optimize DMA time.

    Regards,
    Prashant Kothari
  • Prashant,

       Can you share your DMA configuration for both input and output channel?

    Regards,

    Anshu

  • Hi Anshu,

    Below given is the DMA configuration and kernel processing function.
    Please note that for DMA output channel, we are manually updating ext memory pointer.

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>
    #include <vcop/vcop.h>
    #include <arp32.h>

    /*-----------------------------------------------------------------------*/
    /* These are the include files that are needed for edma/qdma. */
    /*-----------------------------------------------------------------------*/
    #include "edma_utils_autoincrement.h"
    #include "edma_utils_context_size.h"
    #include "edma_utils.h"
    #include "edma_utils_state.h"

    #include "eve_algo_dma_auto_incr_Transpose.h"

    #pragma DATA_SECTION (autoIncrementContext, ".edmaUtilsMem");
    static unsigned char autoIncrementContext[EDMA_UTILS_AUTOINCREMENT_CONTEXT_SIZE];

    int EVELIB_algoDMAAutoIncrInitSrcTranspose (
    unsigned char *src,
    unsigned int srcImageWidth,
    unsigned int srcImageHeight,
    int srcImagePitch,

    unsigned char *srcBlk,
    unsigned int srcBlkWidth,
    unsigned int srcBlkHeight,
    int srcBlkPitch,

    int N,

    unsigned char *dst,
    unsigned int dstImageWidth,
    unsigned int dstImageHeight,
    int dstImagePitch,

    unsigned char *dstBlk,
    unsigned int dstBlkWidth,
    unsigned int dstBlkHeight,
    int dstBlkPitch)
    {
    EDMA_UTILS_autoIncrement_initParam initParam;
    int32_t status = 0;

    /* Reset the global State Structure */
    //EDMA_UTILS_globalResetExtY();
    EDMA_UTILS_globalReset();

    VCOP_SET_MEM_VIEW (ALIAS_128K_VIEW);
    VCOP_BUF_SWITCH_SET (WBUF_SYST, IBUFHB_SYST, IBUFLB_SYST, IBUFHA_SYST, IBUFLA_SYST);

    /*vcop_setview(VCOP_MEMALIASED);*/

    /* ------------------------------------------------------------------ */
    /* Configure channel for EDMA transfer. We will use two channels, one */
    /* for the input transfer and one for the output transfer. */
    /* ------------------------------------------------------------------ */

    initParam.numInTransfers = 1;
    initParam.numOutTransfers = 1;
    initParam.transferType = EDMA_UTILS_TRANSFER_INOUT;

    initParam.transferProp[0].roiWidth = srcImageWidth * N;
    initParam.transferProp[0].roiHeight = srcImageHeight;
    initParam.transferProp[0].roiOffset = 0;
    initParam.transferProp[0].blkWidth = srcBlkWidth * N;
    initParam.transferProp[0].blkHeight = srcBlkHeight;
    initParam.transferProp[0].extBlkIncrementX = srcBlkWidth * N;
    initParam.transferProp[0].extBlkIncrementY = srcBlkHeight;
    initParam.transferProp[0].intBlkIncrementX = 0;
    initParam.transferProp[0].intBlkIncrementY = 0;
    initParam.transferProp[0].extMemPtrStride = srcImagePitch * N;
    initParam.transferProp[0].interMemPtrStride = srcBlkPitch * N;
    initParam.transferProp[0].extMemPtr = src;
    initParam.transferProp[0].interMemPtr = srcBlk;
    initParam.transferProp[0].dmaQueNo = 0;


    initParam.transferProp[1].roiWidth = dstImageWidth * N ;
    initParam.transferProp[1].roiHeight = dstImageHeight;
    initParam.transferProp[1].roiOffset = 0;
    initParam.transferProp[1].blkWidth = dstBlkWidth * N;
    initParam.transferProp[1].blkHeight = dstBlkHeight;
    initParam.transferProp[1].extBlkIncrementX = 0;//dstBlkWidth * N;
    initParam.transferProp[1].extBlkIncrementY = 0; //dstBlkHeight;
    initParam.transferProp[1].intBlkIncrementX = 0;
    initParam.transferProp[1].intBlkIncrementY = 0;
    initParam.transferProp[1].extMemPtrStride = dstImagePitch * N;
    initParam.transferProp[1].interMemPtrStride = dstBlkPitch * N;
    initParam.transferProp[1].extMemPtr = dst;
    initParam.transferProp[1].interMemPtr = dstBlk;
    initParam.transferProp[1].dmaQueNo = 0;

    status = EDMA_UTILS_autoIncrement_init(autoIncrementContext,&initParam);

    if ( status == 0 )
    {
    status = EDMA_UTILS_autoIncrement_configure(autoIncrementContext , EDMA_UTILS_TRANSFER_INOUT);
    }

    return status;
    }


    void EVELIB_algoDMAAutoIncrProcessSrcTranspose
    (
    EVELIB_KernelFuncTypeTranspose execFunc[],
    EVELIB_KernelContextTypeTranspose context[],
    unsigned int numKernels,

    unsigned int VBlock,
    unsigned int HBlock,

    unsigned int BlockWidth,
    unsigned int BlockHeight,

    int N,

    unsigned char *dst,
    unsigned int DstPitch
    )
    {
    unsigned int flag;
    int sinkStatus = 0;
    int k, RowCount, ColumnCount;

    unsigned int FirstBlock = 0;
    unsigned int PrevAddr = 0;


    EDMA_UTILS_autoIncrement_updateParams updateParams;

    VCOP_BUF_SWITCH_SET (WBUF_SYST, IBUFHB_SYST, IBUFLB_SYST, IBUFHA_SYST, IBUFLA_SYST);

    /* ----------------------------------------------------------------- */
    /* Processing loop begins here */
    /* ----------------------------------------------------------------- */
    flag = BUF_PING;
    VCOP_BUF_SWITCH_SET (WBUF_VCOP, IBUFHB_VCOP, IBUFLB_VCOP, IBUFHA_SYST, IBUFLA_SYST);


    /* ------------------------------------------------------------- */
    /* Prologue: */
    /* Get First Block In */
    /* ------------------------------------------------------------- */
    EDMA_UTILS_autoIncrement_triggerInChannel(autoIncrementContext);
    EDMA_UTILS_autoIncrement_waitInChannel(autoIncrementContext);

    updateParams.transferType = EDMA_UTILS_TRANSFER_OUT;
    updateParams.updateMask = EDMA_UTILS_AUTOINCREMENT_UPDATE_MASK_EXTMEMPTR;

    for (RowCount = 0; RowCount < HBlock; RowCount++)
    {
    for (ColumnCount = 0; ColumnCount < VBlock; ColumnCount++)
    {

    flag = VCOP_BUF_SWITCH_TOGGLE (flag);

    if (FirstBlock){
    //updateParams.updateParams[0].extMemPtr = (uint8_t*) (dst + (ColumnCount * BlockWidth * DstPitch) + (RowCount * BlockHeight));
    updateParams.updateParams[0].extMemPtr = (uint8_t*) (dst + PrevAddr);

    //EDMA_UTILS_autoIncrement_update(autoIncrementContext,&updateParams);

    /* Rachit Fix: Update external memory address in each iteration at the time of Output channel trigger
    *
    * blkHorzIdxOut = 0, Update External memory pointer
    *
    * blkVertIdxOut = 0, Update extPtrOffset = 0
    *
    * */
    autoIncrementContext[24] = 0;
    autoIncrementContext[25] = 0;

    sinkStatus = EDMA_UTILS_autoIncrement_triggerOutChannel(autoIncrementContext);

    PrevAddr = (ColumnCount * BlockWidth * DstPitch * N) + (RowCount * N * BlockHeight);
    }

    FirstBlock = 1;

    EDMA_UTILS_autoIncrement_triggerInChannel(autoIncrementContext);

    /* ------------------------------------- */
    /* Submit the remaining kernels to VCOP */
    /* ------------------------------------- */
    for(k=0; k<numKernels; k++)
    {
    (execFunc[k])(context[k]);
    }

    /* ------------------------------------------------------------- */
    /* Check for completion of first horizontal block before */
    /* before entering inner loop. */
    /* ------------------------------------------------------------- */
    EDMA_UTILS_autoIncrement_waitOutChannel(autoIncrementContext);
    EDMA_UTILS_autoIncrement_waitInChannel(autoIncrementContext);

    /* ------------------------ */
    /* Wait for VCOP to be done */
    /* ------------------------ */
    _vcop_vloop_done();
    }
    }

    /*-------------------------------------------------------------------*/
    /* Epilogue: */
    /* Transfer the last processed block in the row */
    /* ----------------------------------------------------------------- */
    flag = VCOP_BUF_SWITCH_TOGGLE (flag);

    updateParams.updateParams[0].extMemPtr = (uint8_t*) (dst + PrevAddr);

    EDMA_UTILS_autoIncrement_update(autoIncrementContext,&updateParams);

    autoIncrementContext[24] = 0;
    autoIncrementContext[25] = 0;

    sinkStatus = EDMA_UTILS_autoIncrement_triggerOutChannel(autoIncrementContext);
    EDMA_UTILS_autoIncrement_waitOutChannel(autoIncrementContext);

    /*---------------------------------------------------------------*/
    /* Return all buffers to system before returning. */
    /*---------------------------------------------------------------*/
    VCOP_BUF_SWITCH_SET(WBUF_SYST, IBUFHB_SYST, IBUFLB_SYST, IBUFHA_SYST, IBUFLA_SYST);
    }

    void EVELIB_algoDMAAutoIncrDeInitTranspose()
    {
    EDMA_UTILS_globalReset();
    }
  • Prashant,
    I was looking for the configuration of DMA registers. Can you tell me what block dimensions you are using?

    Regards,
    Anshu
  • Block Width = 64
    Block Height = 48
  • Prashant,
    Why are you updating memory pointers after each loop? The update API is meant to be used only once per frame as it has multiple condition checks. Can you explain me your data flow and why this update is required?

    Regards,
    Anshu
  • Anshu,

    Because we have to store horizontal transposed block vertically.

    Request you to refer my previous post wherein I have mentioned analysis of auto increment usecase time.

    Init time (Kernel and DMA data transfer disable) is ~275 usec which has update API time as well. So, Update API is not increasing execution time.

    Regards,
    Prashant Kothari
  • Prashant,
    I dont think it is the right way to use DMA. I see that you are setting some of the values of EDMA context to zero which is also not the right way to use the API as you are modifying the internal context which is not expected to be modified by the user. Its difficult to comment on why DMA is taking extra time as I am not very sure what all is exactly included in your profiling code. One thing which you can look is to see if any of the DMA context or stack is in DDR instead of DMEM. This can result in performance degradation.
    I do see that the EDMA API's are not used properly so lets first understand what you want then we can suggest how to achieve this efficiently.
    I am listing down the data flow which I have understood so far. Let me know if there is any gap in the understanding :
    1. Input Image 640 x 480
    2. Input Block size = 64 x 48
    3. Output Block Size = 48 x64 ( assuming the operation which you are doing is transpose)
    4. Output Image size = 480 x 640 ( assuming the operation which you are doing is transpose)
    As I understand you want to first get 64x48 block horizontally transpose it and write the output block vertically. For this data flow using auto-inrement API's is not recommended but you can still use if it your vertical jump is within 16 bit signed number ( which is true for your current configuration of 480 x 640 as jump would be 480 * 64). So if the image size is fixed then you can use the above API by setting extBlkIncrementX and extBlkIncrementY correctly. In your case for output channel it should be extBlkIncrementX = 480 * 64 and extBlkIncrementY = 48. With this user update is required during processing. But this condition may not always be true if your image dimensions are bigger. In those cases you should use scatter gather EDMA utility in which you can update the pointers at each block level and it would be much more efficient.

    Regards,
    Anshu
  • Anshu,
    Our auto context is in DMEM only.

    You are correct about the data flow which we want to transfer via DMA.
    We want to make our API configurable so image size could be anything.
    Block size also could vary depending upon image size. Our image size can vary upto 1280 X 960 to 160 X 120.
    In init time we have commented kernel calling function + DMA_SUBMIT() & DMA_WAIT() calling.
    So it is base time of EVE without any processing on VCOP & data transfer on DMA which comes around 275 us for 100 blocks.

    Using the same auto context for input & output transfers, we should have same number of Horizontal blocks & vertical blocks in both the cases.
    But that is not always possible to transpose the image. Changing the auto context in process is work around to use the same auto context for Input & output.

    In auto context we are basically setting up autoIncrementHandle->blkHorzIdxOut = 0 which will help us to update pointer every time.

    The main problem is here about the eve version. Initially we are using Vision SDK 2.7 for our development in which we were getting more time, but with Vision SDK 2.12 we are getting time close to estimated.

    Secondly, We have tried scatter gather EDMA utility and we are getting close to expected time (~1 msec).

    Thanks a lot for support.

    Regards,
    Prashant Kothari
  • Prashant,
    Good to hear that your timing issues are resolved. One point I want to make is that you can use two different context for input and output channel with one configure input and other configured only for output ( remember to make numInTransfer = 0 in this case). This way you will not have restriction of having same number of horizontal and vertical blocks for input and output channel.
    But as you want it to work with configurable dimensions then its better to use scatter gather dma utils in this case you will anyway have different context for input and output channels.

    Regards,
    Anshu
  • Anshu,

    We had tried different context for input and output for this however we were not getting significant timing differences.

    As mentioned in above post, we have integrated auto increment usecase in vision_sdk 12.0 and getting close to estimated time.

    Secondly, we have integrated scatter gather changes also in vision_sdk 12.0, execution time is same as vision_sdk 2.7.

    So, is there any DMA library optimization for auto increment usecase is vision_sdk 12.0?

    Regards,
    Prashant Kothari
  • Prashant,
    The two context solution was not to improve the performance it was mainly to cleanly use the DMA utils (avoid touching the internal contexts of the Utility).
    There might have been improvements which would have improved the performance but I cannot pin point what exactly has changed as these releases are very old.

    Regards,
    Anshu
  • Anshu,

    Thanks for you support.