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.

TI-RTOS-PROC: TMDSEVM6657LS - Writing code for SPI with EDMA3

Hello everyone,

I have established SPI communication using the pins of SPI NOR flash, as I explained in this post: https://e2e.ti.com/support/processors/f/791/p/902619/3342190#3342190.
 

I attempted to upgrade the communication, so that it uses DMA. Therefore, I have noticed that all of the functions of SPI driver have a corresponding calls to functions in case DMA is enabled. I have found the definitions of SPI configuration and transfer using DMA in the C:\ti\pdk_c665x_2_0_15\packages\ti\drv\spi\soc\dma\v0 directory inside the file SPI_dma.c, and also used main_mcspi_test.c, found in C:\ti\pdk_c665x_2_0_15\packages\ti\drv\spi\test\src as reference. Additionally I have imported several files from C:\ti\edma3_lld_2_12_05_30E\packages\ti\sdo\edma3\drv\sample  and  C:\ti\edma3_lld_2_12_05_30E\packages\ti\sdo\edma3\rm\sample, as they contained several function definitions needed for the building of code. Finally, I was able to build the code without the error, but when I try executing it, the EDMA Iinitialization returns PASS, but the transfer is failed due to TIMEOUT error. While debugging, I noticed that functions for dma interrupts are not being called at all, therefore I thought that maybe I have a mistake in SPI configuration. 

Am I initializing SPI and DMA correctly? 

Parameters of SPI configuration structure:

Here is what I change from the parameters of SPI configuration structure:


 

And here is the function for EDMA init:


Here are all of the files imported in the project:


I will appreciate any help or comment. If needed, I can attach any of the files that I use.

Here is the main file: 

/**
 *  \file   main_spi_flash_read_example.c
 *
 *  \brief  Example application main file. This application will read
 *          the data from flash through spi interface.
 *
 */

#ifndef BARE_METAL
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/System.h>
#include <stdio.h>
#include <xdc/runtime/Error.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#endif

/* SPI Header files */
#include <ti/drv/spi/SPI.h>
#if defined(SOC_K2H) || defined(SOC_K2K) || defined(SOC_K2E) || defined(SOC_K2L) || defined(SOC_K2G) || defined(SOC_C6678) || defined(SOC_C6657) || defined(SOC_OMAPL137) || defined(SOC_OMAPL138)
#include <ti/drv/spi/src/v0/SPI_v0.h>
#endif
#include <ti/drv/spi/soc/SPI_soc.h>
#include <ti/drv/spi/test/src/SPI_log.h>
#include <ti/drv/spi/test/src/SPI_test.h>

/* GPIO Header files */
#include <ti/drv/gpio/GPIO.h>
#include <ti/drv/gpio/soc/GPIO_v1.h>

/* Board Header files */
#include <ti/board/board.h>
#include <ti/board/src/flash/include/board_flash.h>

#ifdef SPI_DMA_ENABLE
#include <ti/osal/CacheP.h>

/* EDMA3 Header files */
#include <ti/sdo/edma3/drv/edma3_drv.h>
#include <ti/sdo/edma3/rm/edma3_rm.h>
#include <ti/sdo/edma3/rm/sample/bios6_edma3_rm_sample.h>
#endif

/**********************************************************************
 ************************** Macros ************************************
 **********************************************************************/

/**********************************************************************
 ************************** Internal functions ************************
 **********************************************************************/

/**********************************************************************
 ************************** Global Variables **************************
 **********************************************************************/

#ifdef SPI_DMA_ENABLE
static EDMA3_RM_Handle MCSPIApp_edmaInit(void);
#endif

//Char myTaskStack[1024];
//Task_Struct myTaskStruct;
bool loop = true;
bool dmaMode = true; // PROMIJENITI U SLUCAJU NE KORISCENJA DMA


//SPI_Handle spiHandle;
//SPI_Params spiParams;
//SPI_Transaction spiTransaction;
/*
 *  ======== Board_initSPI ========
 */
void Board_initSPI(void)
{
    Board_initCfg boardCfg;
    SPI_v0_HWAttrs spi_cfg;
    Board_SoCInfo socInfo;
    bool boardStatus;


    /* Get the default SPI init configurations */
    SPI_socGetInitCfg(0, &spi_cfg);

    /* Update the SPI functional clock based on CPU clock*/
    Board_getSoCInfo(&socInfo);
    if(socInfo.sysClock != BOARD_SYS_CLK_DEFAULT)
    {
        spi_cfg.inputClkFreq = socInfo.sysClock/SPI_MODULE_CLOCK_DIVIDER;
    }


#ifdef SPI_DMA_ENABLE
    if (dmaMode == true)
    {
        /* Set the DMA related init config */
        spi_cfg.edmaHandle = MCSPIApp_edmaInit();
        spi_cfg.dmaMode    = TRUE;
        spi_cfg.enableIntr = FALSE;
    }
    else
#endif
    {
        spi_cfg.edmaHandle = NULL;
        spi_cfg.dmaMode    = FALSE;
    }

    /* Set the default SPI init configurations */
    SPI_socSetInitCfg(0, &spi_cfg);


    boardCfg = BOARD_INIT_PINMUX_CONFIG |
        BOARD_INIT_MODULE_CLOCK |
        BOARD_INIT_UART_STDIO;

    boardStatus = Board_init(boardCfg);

    if (boardStatus != BOARD_SOK)
    {

        printf("\nBoard not OK!\n");
    }
    else{
        printf("\nBoard OK!\n");
    }
}

/*
 *  ======== test function ========
 */
#ifdef BARE_METAL
void main()
#else
void spi_test(UArg arg0, UArg arg1)
#endif
{
    SPI_Handle spiHandle;
    SPI_Params spiParams;
    SPI_Transaction spiTransaction1;
    uint32_t        xferEnable;
    uint32_t        terminateXfer = 0;
    SPI_v0_HWAttrs const    *hwAttrs = NULL;
    SPI_v0_Object    *object = NULL;
    bool            retVal;

#ifdef BARE_METAL
    /* Call board init functions */
    Board_initSPI();
#endif

    SPI_init();

    SPI_Params_init(&spiParams);
    spiParams.frameFormat  = SPI_POL1_PHA1;// for DAC
    //spiParams.frameFormat  = SPI_POL0_PHA0; // for ADC
    spiParams.transferMode = SPI_MODE_BLOCKING;
    //spiParams.transferTimeout = SPI_WAIT_FOREVER;
    spiParams.transferTimeout = 1000;
    //spiParams.transferCallbackFxn = NULL;
    spiParams.mode = SPI_MASTER;
    spiParams.bitRate = 1000000000/18;
    spiParams.dataSize = 8;
    spiHandle = SPI_open(0, &spiParams);

    if (!spiHandle)
    {
        printf("\n SPI_open failed. \n");
    }

    hwAttrs = (const SPI_v0_HWAttrs *)spiHandle->hwAttrs;
    object = (SPI_v0_Object*)spiHandle->object;

    // Definition of signal to be sent




    /* Enable transfer */
    //terminateXfer = 0;
    xferEnable = 1;
    SPI_control(spiHandle, SPI_V0_CMD_XFER_ACTIVATE, (void *)&xferEnable);

    uint8_t transmitBuffer1[1];

    uint8_t receiveBuffer[1];

    transmitBuffer1[0] = (uint8_t)0x09;
    transmitBuffer1[1] = (uint8_t)0xff;
    transmitBuffer1[2] = (uint8_t)0xff;
    transmitBuffer1[3] = (uint8_t)0x09;
    transmitBuffer1[4] = (uint8_t)0x8b;
    transmitBuffer1[5] = (uint8_t)0xb7;

    spiTransaction1.txBuf = transmitBuffer1;
    spiTransaction1.rxBuf = NULL;
    spiTransaction1.count = 1;
    terminateXfer = 1;
    spiTransaction1.arg = (void *)&terminateXfer;

    #ifdef SPI_DMA_ENABLE
        if (dmaMode)
        {
           // CacheP_wb((void *)transmitBuffer1, (int32_t)1);
           // CacheP_wb((void *)receiveBuffer, (int32_t)1);
        }
    #endif

    #ifdef SPI_DMA_ENABLE
        if (dmaMode == true)
        {
            //CacheP_Inv((void *)receiveBuffer, (int32_t)1);
        }
    #endif

    while (loop)
    {


      retVal = SPI_transfer(spiHandle, &spiTransaction1);

      //retVal = SPI_transfer(spiHandle, &spiTransaction2);

      //retVal = SPI_transfer(spiHandle, &spiTransaction3);

      //retVal = SPI_transfer(spiHandle, &spiTransaction4);

      //transmitBuffer[0] = (uint8_t)0x0009;
      //transmitBuffer[1] = (uint8_t)0xFFFF;
      //transmitBuffer[2] = (uint8_t)0xFF;


      //spiTransaction.txBuf = transmitBuffer;
      //spiTransaction.rxBuf = NULL;

      //retVal = SPI_transfer(spiHandle, &spiTransaction);

      if (retVal == false){
       printf("\nSPI transfer failed!\n");}
          //printf("%x",receiveBuffer[0]);

      if (retVal == true){
       printf("\nSPI transfer completed!\n");}
          //printf("%x",receiveBuffer[0]);

      //printf("%d",receiveBuffer[0]);

    }
    /* Disable transfer */
    xferEnable = 0;
    SPI_control(spiHandle, SPI_V0_CMD_XFER_ACTIVATE, (void *)&xferEnable);

    SPI_close(spiHandle);

}

/*
 *  ======== main ========
 */
#ifndef BARE_METAL
int main(void){
    System_printf("Start Setup\n");

    /* Call board init functions */
    Board_initSPI();

    /* Configure task. */
    //Task_Params taskParams;
    //Task_Params_init(&taskParams);
    //taskParams.stack = myTaskStack;
    //taskParams.stackSize = sizeof(myTaskStack);
    //Task_construct(&myTaskStruct, spi_test, &taskParams, NULL);

    System_printf("End Setup\n");


    /* Start BIOS */
    BIOS_start();
    return (0);
}
#endif


#ifdef SPI_DMA_ENABLE
EDMA3_RM_Handle gEdmaHandle = NULL;

/**
 * \brief      Function to initialize the edma driver and get the handle to the
 *             edma driver;
 */
static EDMA3_RM_Handle MCSPIApp_edmaInit(void)
{
    EDMA3_DRV_Result edmaResult = EDMA3_DRV_E_INVALID_PARAM;
    uint32_t         edma3Id;

    if (gEdmaHandle != NULL)
    {
        return (gEdmaHandle);
    }

    edma3Id = 0;
    gEdmaHandle = (EDMA3_RM_Handle)edma3init(edma3Id, &edmaResult);
    if (edmaResult != EDMA3_DRV_SOK)
    {
        /* Report EDMA Error */
        System_printf("\nEDMA driver initialization FAIL\n");
    }
    else
    {
        System_printf("\nEDMA driver initialization PASS.\n");
    }
    return(gEdmaHandle);
}
#endif

Kind regards,

Dejana

  • Hi,

    C6657 never tested DMA mode for SPI transfer, so looks you are developing DMA code. There are examples for C674 you can refer to: SPI_LoopbackExample_evmOMAPL137_c674xDMAExampleProject and SPI_LoopbackExample_lcdkOMAPL138_c674xDMAExampleProject, try to see how they are different from the regular project.

    Regards, Eric

  • Hi Eric,

    Thanks for responding.

    And in general, is it expected that the speed of SPI transfer is higher when DMA is used? (I am referring to byte to byte delay)

    Kind regards,

    Dejana

  • Hi,

    Yes, it is true.

    Regards, Eric

  • Hi Eric,

    I am still on this topic. I have looked through the examples you mentioned, as well as the UART_BasicExample_C6657_c66xDMATestProject, as I figured that it also has the function for edma initialization. I implemented it, and during doing so, I noticed the following:

    The train of function calls for obtaining the edma handle for spi configuration:

    spi_cfg.edmaHandle = SPIApp_edmaInit(); ------>SPI_getEdmaInfo(0, &edma3Id, edmaEvent);  and    gEdmaHandle = (EDMA3_RM_Handle)edma3init(edma3Id, &edmaResult);

    In the following part of the post is the SPIApp_edmaInit function. We can see that she uses the edma3Id information from SPI_getEdmaInfo function which is defined in SPI_soc.c. However, this function returns edma3Id to be 2, which makes the SPIApp_edmaInit fail, as this edma3Id is compared with number of edma instances inside edma3init function, and since number of edma instances is 1, the initialization fails. Therefore, I got the feeling that SPI_getEdmaInfo has wrong values for edma3Id. When I changed it in the SPI_soc.c to be 0, I got successful initialization.

    EDMA3_RM_Handle gEdmaHandle = NULL;

    /**
    * \brief Function to initialize the edma driver and get the handle to the
    * edma driver;
    */
    static EDMA3_RM_Handle SPIApp_edmaInit(void)
    {
    EDMA3_DRV_Result edmaResult = EDMA3_DRV_E_INVALID_PARAM;
    uint32_t edma3Id;

    uint32_t edmaEvent[2], i, chnMapping, chnMappingIdx;

    SPI_getEdmaInfo(0, &edma3Id, edmaEvent);


    /* Set the RX/TX ownDmaChannels and dmaChannelHwEvtMap */
    for (i = 0; i < 2; i++)
    {
    chnMapping = edmaEvent[i];
    if (chnMapping < 32)
    chnMappingIdx = 0;
    else
    {
    chnMapping -= 32;
    chnMappingIdx = 1;
    }
    sampleInstInitConfig[edma3Id][0].ownDmaChannels[chnMappingIdx] |= (1 << chnMapping);
    sampleInstInitConfig[edma3Id][0].ownTccs[chnMappingIdx] |= (1 << chnMapping);
    sampleInstInitConfig[edma3Id][0].ownPaRAMSets[chnMappingIdx] |= (1 << chnMapping);
    sampleEdma3GblCfgParams[edma3Id].dmaChannelHwEvtMap[chnMappingIdx] |= (1 << chnMapping);
    }

    if (gEdmaHandle != NULL)
    {
    return (gEdmaHandle);
    }

    //edma3Id = 0;
    gEdmaHandle = (EDMA3_RM_Handle)edma3init(edma3Id, &edmaResult);
    #ifdef USE_BIOS
    if (edmaResult != EDMA3_DRV_SOK)
    {
    /* Report EDMA Error */
    System_printf("\nEDMA driver initialization FAIL\n");
    }
    else
    {
    System_printf("\nEDMA driver initialization PASS.\n");
    }
    #endif
    return(gEdmaHandle);
    }

    Now SPI transfer function returns retVal = 1, but when I look at the status values of transaction variable it says SPI_TRANSFER_FAILED. When I put breakpoints inside interrupt handler functions for Rx and Tx, I see that for Tx once the status is that transfer was missed and another time that it is completed. For Rx it is always transfer completed, but there are no values in the rxBuf after the transaction. 

    On the scope I can see the SPI SCL,K as well as CS, and a slight glich on MOSI, but nothing on MISO.

    Breakpoint in the TX interrupt handler function (in the next breakpoint the status is not missed, but completed):

    Breakpoint in the RX interrupt handler function (always complete):

    When I return from SPI_transfer function, the variables look like this:

    And here is how SPI CLK and CS look like:

    I am attaching my project, I would very much appreciate it if you could take a look and let me know what you think how can I solve it.

    8015.SPI_example.zip

    Additionally, how difficult would it be to adapt the these drivers to ping pong buffering with DMA?

    Kind regards,

    Dejana

  • Hi,

    For the EDMA instance Id 0 or 2, how the UART example can work with Id = 2? 

    For the EDMA transfer of UART, it is event driven (by the UART FIFO level), it is not triggered manually. For the SPI, I thought the concept is the same, please check the C6657 EDMA event number for SPI TX, RX in Table 6-25. EDMA3_CC Events for C665x for C6657 datasheet.

    Also see SPI user guide: 

    2.10 DMA Events Support
    If handling the SPI message traffic on a character-by-character basis requires too much
    CPU overhead, then the CPU can configure the system DMA to handle the SPI data
    transfer.
    The SPI module has two DMA synchronization event outputs for receive (REVT) and
    transmit (XEVT), allowing DMA transfers to be triggered by SPI read receive and write
    transmit events. The SPI module enables DMA requests by enabling the DMA request
    enable (DMAREQEN) bit in the SPI interrupt register (SPIINT0).
    When a character is to be transmitted the SPI module signals the DMA via the XEVT
    signal. The DMA controller then transfers the data from the source buffer into the SPI
    transmit data register (SPIDAT1). When a character is received, the SPI module signals
    the DMA via the REVT signal. The DMA controller then reads the data from the SPI
    receive buffer register (SPIBUF) and transfers it to a destination buffer for ready access.
    In most cases, if the DMA is being used to service received data from the SPI, the receive
    interrupt enable (RXINTEN) bit in SPIINT0 should be cleared to 0. This prevents the
    CPU from responding to the received data in addition to the DMA. For specific SPI
    synchronization event number

    For EDMA pingpong buffer example: \edma3_lld_2_12_05_30E\examples\edma3_driver\src\dma_ping_pong_test

    Regards, Eric

  • Hi Eric,

    The edma3Id for UART as defined in UART_soc.c is 0, that is why it works.

    I will check the things you suggested, I guess I am missing something.

    Also, is there a possibility to control CS inside the transfer when using SPI with DMA? 

    Kind regards,

    Dejana