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.

SPI PSP driver in Spi_OpMode_DMAINTERRUPT question

Other Parts Discussed in Thread: OMAPL138

Hi all,

I am using the following system:

- OMAPL138

- bios_5_41_03_17 DSPBIOS 

- pspdrivers_01_30_01 driver

- edma3_lld_01_11_00_03 driver

I've got my SPI driver running in polling mode, but to improve system performance I've decided to use it in DMAINTERRUPT mode. My initialization code is following the code provided in  pspdrivers_01_30_01\packages\ti\pspiom\examples\evmOMAPL138\spi\edma\src\ folder and to initialize SPI driver in EDMA mode. So I am calling  edma3init() function from the library provided in edma3_lld_01_11_00_03\packages\ti\sdo\edma3\drv\lib\Debug\edma3_drv_bios_c674.lib.  The rest of the code doing GIO_create and GIO_write is the same as in the code already working fine in polling mode.

I can see that actual SPI transfer is performed (SPI lines are toggling as expected) but then GIO_write is blocked forever. Globally interrupts are enabled and DSPBIOS is working (tasks are switching as expected). I also see that Spi_localCallbackTransmit is executed twice (EDMA SPI TX transfer completion interrupt service routine), but Spi_localCallbackReceive (EDMA SPI RX transfer completion interrupt service routine) is never called. SPI driver code waits for both of these being called in order to finish off SPI transfer servicing and unblock GIO_write() function. Can anyone tell me what may be the reason why Spi_localCallbackReceive is never called?

Thanks,

Adam Turowski

  • Adam,

    Are you using the OMAPL138 EVM board or any custom board?

    I guess, instead of application provided in the PSP, you have written your own SPI EDMA application to make use the SPI driver in the PSP. Am i right? In that case, can you share your initialization sequence?

    BTW, you can also try out the prebuilt binary of SPI EDMA application in the PSP and share the result.

    Regards,

    Sandeep K

  • Hi Sandeep,

    I am using custom board and I've written my own SPI EDMA application to make use SPI driver in PSP.

    The initialization sequence is as follows:

    spi_params = Spi_PARAMS;
    spi_params.edmaHandle = NULL;
    spi_params.enableCache = TRUE;
    spi_params.hwiNumber = 8;
    spi_params.loopbackEnabled = FALSE;
    // spi_params.opMode = Spi_OpMode_INTERRUPT;
    // spi_params.opMode = Spi_OpMode_POLLED;
    spi_params.opMode = Spi_OpMode_DMAINTERRUPT;

    spi_params.outputClkFreq = 10000000;
    spi_params.pllDomain = Spi_pllDomain_0;
    spi_params.polledModeTimeout = (int)SYS_FOREVER;
    spi_params.pscPwrmEnable = FALSE;

    spi_params.spiHWCfgData.clkInternal = TRUE;
    spi_params.spiHWCfgData.csDefault = 0xef;
    spi_params.spiHWCfgData.delay.c2TDelay = 0;
    spi_params.spiHWCfgData.delay.t2CDelay = 0;
    spi_params.spiHWCfgData.delay.t2EDelay = 0;
    spi_params.spiHWCfgData.delay.c2EDelay = 0;
    spi_params.spiHWCfgData.enableHighZ = FALSE;
    spi_params.spiHWCfgData.intrLevel = TRUE;
    spi_params.spiHWCfgData.masterOrSlave = Spi_CommMode_MASTER;
    spi_params.spiHWCfgData.pinOpModes = Spi_PinOpMode_SPISCS_4PIN;
    spi_params.spiHWCfgData.waitDelay = FALSE;

    spi_params.spiHWCfgData.configDatafmt[0].wDelay = 0;
    spi_params.spiHWCfgData.configDatafmt[0].charLength = 16;
    spi_params.spiHWCfgData.configDatafmt[0].lsbFirst = FALSE;
    spi_params.spiHWCfgData.configDatafmt[0].phaseIn = FALSE;
    spi_params.spiHWCfgData.configDatafmt[0].oddParity = FALSE;
    spi_params.spiHWCfgData.configDatafmt[0].parityEnable = FALSE;
    spi_params.spiHWCfgData.configDatafmt[0].clkHigh = TRUE;
    spi_params.spiHWCfgData.configDatafmt[0].waitEnable = FALSE;

    spi_params.spiHWCfgData.configDatafmt[1].wDelay = 0;
    spi_params.spiHWCfgData.configDatafmt[1].charLength = 16;
    spi_params.spiHWCfgData.configDatafmt[1].lsbFirst = FALSE;
    spi_params.spiHWCfgData.configDatafmt[1].phaseIn = TRUE;
    spi_params.spiHWCfgData.configDatafmt[1].oddParity = FALSE;
    spi_params.spiHWCfgData.configDatafmt[1].parityEnable = FALSE;
    spi_params.spiHWCfgData.configDatafmt[1].clkHigh = TRUE;
    spi_params.spiHWCfgData.configDatafmt[1].waitEnable = FALSE;

    spi_params.spiHWCfgData.configDatafmt[2].wDelay = 0;
    spi_params.spiHWCfgData.configDatafmt[2].charLength = 8;
    spi_params.spiHWCfgData.configDatafmt[2].lsbFirst = FALSE;
    spi_params.spiHWCfgData.configDatafmt[2].phaseIn = FALSE;
    spi_params.spiHWCfgData.configDatafmt[2].oddParity = FALSE;
    spi_params.spiHWCfgData.configDatafmt[2].parityEnable = FALSE;
    spi_params.spiHWCfgData.configDatafmt[2].clkHigh = TRUE;
    spi_params.spiHWCfgData.configDatafmt[2].waitEnable = FALSE;

    spi_params.spiHWCfgData.configDatafmt[3].wDelay = 0;
    spi_params.spiHWCfgData.configDatafmt[3].charLength = 8;
    spi_params.spiHWCfgData.configDatafmt[3].lsbFirst = FALSE;
    spi_params.spiHWCfgData.configDatafmt[3].phaseIn = FALSE;
    spi_params.spiHWCfgData.configDatafmt[3].oddParity = FALSE;
    spi_params.spiHWCfgData.configDatafmt[3].parityEnable = FALSE;
    spi_params.spiHWCfgData.configDatafmt[3].clkHigh = TRUE;
    spi_params.spiHWCfgData.configDatafmt[3].waitEnable = FALSE;

    // enable the GPIO in the PSC module
    PWRM_setDependency(PWRM_RSRC_GPIO);

    Spi_init();

    /..... some other stuff dealing with other peripherials removed for clarity.../

    EDMA3_DRV_Result res = edma3init();

    /.....For clarity I didn't include error checking here, but at this point res always contains EDMA3_DRV_SOK.../

    impl_->spi_gio_attrs_ = GIO_ATTRS;
    impl_->spi_chan_params_.hEdma = edma3.handle(); // edma handle
    impl_->spi_chan_params_.hGpio = gpio.handle();

    impl_->gio_spi_handle_ = GIO_create("/Spi0", IOM_INOUT, &status, &impl_->spi_chan_params_, &impl_->spi_gio_attrs_);

    And then the actual SPI transfer is performed as follows:

    int res;
    size_t size;
    Spi_DataParam dataparam = spi_device_data_params[device];
    dataparam.bufLen = length;
    dataparam.inBuffer = receive_buf;
    dataparam.outBuffer = transmit_buf;
    size = dataparam.bufLen;
    res = GIO_write(impl_->gio_spi_handle_, &dataparam, &size); <- this is where code blocks forever due to lack of execution of Spi_localCallbackReceive function

    Hope that helps,

    Adam

  • I've done a little bit more of investigation. Sample application is working on my platform.

    My custom application is not working though. I had found that there is an interrupt Spi_localCallbackTransmit called after doing GIO_write. The call stack looks like that:

    0 Spi_localCallbackTransmit(unsigned int, enum unknown, void *) at Spi_edma.c:389 0xcf923a80
    1 edma3CCErrHandler(struct unknown *) at edma3resmgr.c:6045 0xcf8df808
    2 lisrEdma3CCErrHandler0(unsigned int) at edma3resmgr.c:6318 0xcf8dfd9a
    3 ECM_dispatch(unsigned int) at ecm_dispatch.c:63 0xcf95fd68

    At this point unknown argument in Spi_localCallbackTransmit is set to be 0x00000002 (EDMA3_RM_E_CC_DMA_EVT_MISS). Spi_localCallbackReceive is never called.  It looks like that there is some DMA event missed, but have no idea why. Any clues what to check next?

    Thanks,

    Adam

  • I've also found that Spi_localCallbackTransmit is actually called twice. First call is done with unknown argument set to  0x00000002 (EDMA3_RM_E_CC_DMA_EVT_MISS), the second call has this argument set to EDMA3_RM_XFER_COMPLETE. Is it expected behavior?

    Also Spi_localCallbackReceive is never called. Any clues?

  • Hi Adam,

    Looking at your SPI configurations, I think you are performing 16 bit transfers. If so, please take a look at this post -

    http://e2e.ti.com/support/embedded/bios/f/355/t/170382.aspx

    Best Regards,

    Raghavendra

  • Hi Raghavendra,

    Thanks for that tip. I've actually found what was causing the problems I've seen. It was Linux running on the ARM in OMAPL-138 and there was SPI driver loaded. SPI driver on Linux runs in EDMA3 mode. So both DSP and ARM have been having their own EDMA interrupt service routines setup to service SPI completion interrupts. Also both of these CPUs were receiving SPI EDMA interrupt, so there was a race between DSP and ARM. DSP was quicker to process ISR, so usually it has managed to service transmit interrupt before ARM got around to do that. Unfortunately ARM was able to process receive interrupt while DSP was in the middle of processing transmit interrupt. So ARM was "stealing" receive interrupt from DSP and made DSP to block on waiting on "stolen" SPI receive interrupt.

    Best regards,

    Adam

  • Hi Adam,

    Thats great :)

    Thanks for the update.

    Can you please close this thread?

    Thanks and Regards,

    Sandeep K