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.

TMS320F280049: Issue with SPI synchronization

Part Number: TMS320F280049
Other Parts Discussed in Thread: TMS570LS3137, TMS570LS0332

Context:

We have an application using the following microcontrollers:

TMS320F280049CPZS - DSP

TMS570LS3137 – Main

TMS570LS0332 - Secondary

All processors communicate with each other through the SPI interface. The SPI and DMA modules are configured in the DSP as follows:

Communication between Main and DSP

SPI bus: SPIA

SPI bitrate: 8MHz

SPI msg: 64 words every 1 msec.

Main is Master. DSP is slave.

DMA channels: CH5 (DSP to Main) and CH6 (Main to DSP).

DMA CH6: Continuous Mode ON

DMA CH5: Continuous Mode OFF

DMA interrupt: INT_AT_END

Communication between Secondary and DSP

SPI bus: SPIB

SPI speed: 8MHz

SPI msg: 64 words every 1 msec.

Secondary is Master. DSP is slave.

DMA channels: CH3 (DSP to Secondary) and CH4 (Main to Secondary).

DMA CH4: Continuous Mode ON

DMA CH3: Continuous Mode OFF

DMA interrupt: INT_AT_END

 

Problem

SPI data is not “understood” correctly if one of the microcontrollers is forced to reset while the others continue running. This problem is intermittent (it does not occur for every reset).

Inspecting the DMA memory, it has been observed the data is shifted a given number of words.

Hypothesis

SPI slave loses synchronization with the master when reset is forced. When the reset occurs while data was being transmitted, part of the message is lost. The DMA memory stores an incomplete message stream.

Suggested solution

Reset the SPI and DMA modules to its default state if we ever detect an incorrect data stream. We were using the CS line and a GPIO interrupt to bring the SPI/DMA to a known state. However, we would like to confirm what the appropriate set of steps should be to bring both the SPI and DMA modules to its initial state (working state).

We would need to communication between main and DSP while keeping the communication between the Secondary and DSP active or vice versa.

Would the following code make it for the communication between main and DSP?

    //Disable SPI
    SPI_disableModule(SPIA_BASE);
    //Stop channel and clear trigger Flag
    DMA_stopChannel(DMA_CH6_BASE);
    DMA_clearTriggerFlag(DMA_CH6_BASE);
    //Soft Reset the DMA Channel 6
    DMA_triggerSoftReset(DMA_CH6_BASE);
    // Soft Reset Channel 5 and Flush SPI Tx Buffer
    DMA_triggerSoftReset(DMA_CH5_BASE);
    SPI_resetTxFIFO(SPIA_BASE);
    DMA_enableTrigger(DMA_CH5_BASE);
    //Enable SPI
    SPI_enableModule(SPIA_BASE);
    //Start Channel
    DMA_startChannel(DMA_CH6_BASE);

 

We suspect some code may not be necessary or additional code may be required.

  • Hi Jose,

    In terms of resetting the SPI and DMA, the general process would be to reset the SPI module and disable, reconfigure and re-enable DMA channel(s). The reset can be done during runtime but needs to be done carefully. 

    The PRIORITYSTAT register shows if a transfer is happening and can be used to track what is going on the bus. I would recommend waiting for a channel to not be active before disabling it (or anything else you need it to do). The HALT and RUN bits can be used for this specific application.

    Aishwarya

  • Hello Aishwarya,

    This is how we are currently configuring the DMA (once at startup):

        // Initialize DMA
        DMA_initController();
     
        // Configure DMA Ch5 for TX. W
        DMA_stopChannel(DMA_CH5_BASE);
        DMA_clearTriggerFlag(DMA_CH5_BASE);
        DMA_configAddresses(DMA_CH5_BASE, (uint16_t *)(SPIA_BASE + SPI_O_TXBUF),
                            sData);
        DMA_configBurst(DMA_CH5_BASE, BURST, 1, 0);
        DMA_configTransfer(DMA_CH5_BASE, TRANSFER, 1, 0);
        DMA_configMode(DMA_CH5_BASE, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE |
                       DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT);
     
        // Configure DMA Ch5 interrupts
        DMA_enableTrigger(DMA_CH5_BASE);
     
        // Configure DMA Ch6 for RX. 
    	DMA_stopChannel(DMA_CH6_BASE);
     
        DMA_clearTriggerFlag(DMA_CH6_BASE);
        DMA_configAddresses(DMA_CH6_BASE, rData,
                            (uint16_t *)(SPIA_BASE + SPI_O_RXBUF));
        DMA_configBurst(DMA_CH6_BASE, BURST, 0, 1);
        DMA_configTransfer(DMA_CH6_BASE, TRANSFER, 0, 1);
        DMA_configMode(DMA_CH6_BASE, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE |
                       DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);
     
        // Configure DMA Ch6 interrupts
        DMA_setInterruptMode(DMA_CH6_BASE, DMA_INT_AT_END);
        DMA_enableInterrupt(DMA_CH6_BASE);
        DMA_enableTrigger(DMA_CH6_BASE);
     
        
        DMA_startChannel(DMA_CH6_BASE);
        DMA_startChannel(DMA_CH5_BASE)

    Do you think we need to re-run this configuration when we need to reset the DMA?

    If that's correct we could think of a reset containing the following:

    /***************************/
        //Disable and Enable SPI
        SPI_disableModule(SPIA_BASE);
        SPI_enableModule(SPIA_BASE);
    /***************************/   
        //Disable DMA Channels
        DMA_stopChannel(DMA_CH6_BASE);
        DMA_stopChannel(DMA_CH5_BASE);
    /***************************/ 
        //Wait for DMA to be idle
        //(pseudocode)
        while(PRIORITYSTAT & 0x7 != FALSE); 
    /***************************/
        //Reconfigure
        // Initialize DMA
        DMA_initController();
     
        // Configure DMA Ch5 for TX. W
        DMA_clearTriggerFlag(DMA_CH5_BASE);
        DMA_configAddresses(DMA_CH5_BASE, (uint16_t *)(SPIA_BASE + SPI_O_TXBUF),
                            sData);
        DMA_configBurst(DMA_CH5_BASE, BURST, 1, 0);
        DMA_configTransfer(DMA_CH5_BASE, TRANSFER, 1, 0);
        DMA_configMode(DMA_CH5_BASE, DMA_TRIGGER_SPIATX, DMA_CFG_ONESHOT_DISABLE |
                       DMA_CFG_CONTINUOUS_DISABLE | DMA_CFG_SIZE_16BIT);
     
        // Configure DMA Ch5 interrupts
        DMA_enableTrigger(DMA_CH5_BASE);
     
        // Configure DMA Ch6 for RX. 
        DMA_clearTriggerFlag(DMA_CH6_BASE);
        DMA_configAddresses(DMA_CH6_BASE, rData,
                            (uint16_t *)(SPIA_BASE + SPI_O_RXBUF));
        DMA_configBurst(DMA_CH6_BASE, BURST, 0, 1);
        DMA_configTransfer(DMA_CH6_BASE, TRANSFER, 0, 1);
        DMA_configMode(DMA_CH6_BASE, DMA_TRIGGER_SPIARX, DMA_CFG_ONESHOT_DISABLE |
                       DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT);
     
        // Configure DMA Ch6 interrupts
        DMA_setInterruptMode(DMA_CH6_BASE, DMA_INT_AT_END);
        DMA_enableInterrupt(DMA_CH6_BASE);
        DMA_enableTrigger(DMA_CH6_BASE);
    /***************************/
        // Renable
        DMA_startChannel(DMA_CH6_BASE);
        DMA_startChannel(DMA_CH5_BASE);
    /***************************/

    Can you confirm if this is more or less what we need?

    Thanks!

  • Hi Jose,

    This expert is currently on leave until 4/5/24, so please expect a delay in response until then. I will see if I can loop in another expert to assist in the meantime.

    Thanks & Regards,

    Allison

  • Hi Jose,

    Sorry for the delay. Typically, with a receiving DMA I would recommend stopping the relevant channels during an error condition and re-starting the corresponding channel when the error condition is over so that no errored data is transferred by the DMA at all. Assuming you aren't wanting the DMA to overwrite the previously received data in rData. For your application, is there some indication to the F280049 slave device from the master devices that a reset is occurring? For example, will the CS line go inactive in the middle of a transmission? You could potentially generate an interrupt on this condition and stop the corresponding RX DMA channel in the ISR and then re-start the channel when the condition is no longer present. 

    Let me know if this will work for your use case or if I'm misunderstanding what you are trying to achieve.

    Best Regards,

    Delaney

  • Hello,

    Sorry for the late response, lots of things going on around this parts.  We don't really care about overwriting previously received data in rData, ideally once the transmission is complete that data is copied to another buffer so it can be overwritten safely, if a full message is not received, then it should be discarded too.   We don't have a way to know if the event of a reset on the other processor occurs.  

    Is the code sequence above the correct means to "stop the relevant channel and re-start it"?

    Thanks!

  • Hi Jose,

    By stopping and starting the relevant channel I meant just calling DMA_stopChannel(); during the error condition and then calling DMA_startChannel(); when the condition is over. However, if you aren't worried about overwriting previously received data, I believe your approach will work. 

    Best Regards,

    Delaney