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.

Write to a FRAM/MRAM over SPI with DMA sometimes produces receiver overrung interrupt

Other Parts Discussed in Thread: TMS570LS3137, CCSTUDIO

Hello,

I'm using TMS570LS3137 with CCStudio 6.0.1 and Compiler 5.1.9.

I've configured channel 11 of DMA for SPI Tx and cannel 12 for SPI Rx. Before I start DMA I send the header (WriteEnable, WriteCommand, Address) manually. Also I've enabeld DMA BTC interrupt on channel 12 to know when the transfer is over. In the BTC ISR I set the CS of the FRAM/MRAM to high again. (I'm using SPI 3pin mode and set/reset CS manually)

This works good but sometimes a receiver overrun interrupt occurs. I don't know why.

Does that mean that DMA is too slow to copy the data? (I can't believe that, although more DMA channels for other SPI ports are configured and running perfectly)

I've attached the function in which I configure DMA and SPI. Are there any errors in configuration? Is there a better way to do this?

#define MAX_DATA_CNT 8187

uint8_t FRAMDrv_WriteDMA(uint32_t Address, uint8_t *pDataToWrite, uint32_t Length)
{
   uint32_t RestLength = Length;
   uint8_t SPITxBuffer[4], i, RetVal = 0;
   g_dmaCTRL dmaSPITxCTRLPKT, dmaSPIRxCTRLPKT;
   portBASE_TYPE xRunningPrivileged;

   xSemaphoreTake(xFRAMMutex, portMAX_DELAY);
   xSemaphoreTakeRecursive(xSPI4Mutex, portMAX_DELAY);
   xRunningPrivileged = prvRaisePrivilege();
   memcpy(&FRAMMirror[Address], pDataToWrite, Length);
   while (RestLength > 0)
   {
      if (RestLength >= MAX_DATA_CNT)
      {
         Length = MAX_DATA_CNT;
         RestLength -= MAX_DATA_CNT;
      }
      else
      {
         Length = RestLength;
         RestLength = 0;
      }

      SPITxBuffer[0] = FRAM_CMD_WRITE;
      SPITxBuffer[1] = (uint8_t)((Address >> 16) & 0x000000FF);
      SPITxBuffer[2] = (uint8_t)((Address >> 8) & 0x000000FF);
      SPITxBuffer[3] = (uint8_t)(Address & 0x000000FF);

      SetTransferMode(WRITE);

      // Clear RXINT flag.
      spiREG4->FLG |= 0x100;

      /* Set CS for FRAM to 0 */
      gioSetBit(spiPORT3, 0, 0);
      // Send Write Enable command.
      spiREG4->DAT1 = (0x00000000 | (uint8_t)FRAM_CMD_WREN);
      /* Wait for transmission to complete */
      while ((spiREG4->FLG & 0x100) == 0)
         ;
      /* Set CS for FRAM to 1 */
      gioSetBit(spiPORT3, 0, 1);

      // Clear RXINT flag.
      spiREG4->FLG |= 0x100;

      /* Set CS for FRAM to 0 */
      gioSetBit(spiPORT3, 0, 0);
      /* Send Write Command and Address to FRAM */
      for (i = 0; i < sizeof(SPITxBuffer); i++)
      {
         spiREG4->DAT1 = (0x00000000 | (uint8_t)SPITxBuffer[i]);
         /* Wait for transmission to complete */
         while ((spiREG4->FLG & 0x100) == 0)
            ;
         // Clear RXINT flag.
         spiREG4->FLG |= 0x100;
      }

      /* Configuring SPI Tx DMA channel */
      /* - assigning dma request: channel-11 with request line - 25 */
      dmaReqAssign(DMA_CH11, 25);

      /* - configuring dma control packets   */
      dmaSPITxCTRLPKT.SADD = (uint32_t)(pDataToWrite); /* source address             */
      dmaSPITxCTRLPKT.DADD = (uint32_t)(&(spiREG4->DAT1)) + 3; /* destination  address       */
      dmaSPITxCTRLPKT.CHCTRL = 0; /* channel control            */
      dmaSPITxCTRLPKT.FRCNT = Length; /* frame count                */
      dmaSPITxCTRLPKT.ELCNT = 1; /* element count              */
      dmaSPITxCTRLPKT.ELDOFFSET = 0; /* element destination offset */
      dmaSPITxCTRLPKT.ELSOFFSET = 0; /* element source offset */
      dmaSPITxCTRLPKT.FRDOFFSET = 0; /* frame destination offset   */
      dmaSPITxCTRLPKT.FRSOFFSET = 0; /* frame source offset   */
      dmaSPITxCTRLPKT.PORTASGN = 4; /* port b                     */
      dmaSPITxCTRLPKT.RDSIZE = ACCESS_8_BIT; /* read size                  */
      dmaSPITxCTRLPKT.WRSIZE = ACCESS_8_BIT; /* write size                 */
      dmaSPITxCTRLPKT.TTYPE = FRAME_TRANSFER; /* transfer type              */
      dmaSPITxCTRLPKT.ADDMODERD = ADDR_INC1; /* address mode read          */
      dmaSPITxCTRLPKT.ADDMODEWR = ADDR_FIXED; /* address mode write         */
      dmaSPITxCTRLPKT.AUTOINIT = AUTOINIT_OFF; /* autoinit                   */

      /* - setting dma control packets */
      dmaSetCtrlPacket(DMA_CH11, dmaSPITxCTRLPKT);
      /* - set channel 11 to high priority queue */
      dmaSetPriority(DMA_CH11, HIGHPRIORITY);
      /* - setting the dma channel to trigger on h/w request */
      dmaSetChEnable(DMA_CH11, DMA_HW);

      // The DMA Rx channel is used to determine if the transmit is already over.
      // This is necessary because the Tx DMA throws the BTC interrupt already
      // when the last transfer to the TXDAT1 register was done. But the SPI
      // interface needs some time to shift the data out.
      /* Configuring SPI Rx DMA channel */
      /* - assigning dma request: channel-12 with request line - 24 */
      dmaReqAssign(DMA_CH12, 24);

      /* - configuring dma control packets   */
      dmaSPIRxCTRLPKT.SADD = (uint32_t)(&(spiREG4->BUF)) + 3; /* source address             */
      dmaSPIRxCTRLPKT.DADD = (uint32_t)FRAMRxBuffer; /* destination  address       */
      dmaSPIRxCTRLPKT.CHCTRL = 0; /* channel control            */
      dmaSPIRxCTRLPKT.FRCNT = Length; /* frame count                */
      dmaSPIRxCTRLPKT.ELCNT = 1; /* element count              */
      dmaSPIRxCTRLPKT.ELDOFFSET = 0; /* element destination offset */
      dmaSPIRxCTRLPKT.ELSOFFSET = 0; /* element source offset */
      dmaSPIRxCTRLPKT.FRDOFFSET = 0; /* frame destination offset   */
      dmaSPIRxCTRLPKT.FRSOFFSET = 0; /* frame source offset   */
      dmaSPIRxCTRLPKT.PORTASGN = 4; /* port b                     */
      dmaSPIRxCTRLPKT.RDSIZE = ACCESS_8_BIT; /* read size                  */
      dmaSPIRxCTRLPKT.WRSIZE = ACCESS_8_BIT; /* write size                 */
      dmaSPIRxCTRLPKT.TTYPE = FRAME_TRANSFER; /* transfer type              */
      dmaSPIRxCTRLPKT.ADDMODERD = ADDR_FIXED; /* address mode read          */
      dmaSPIRxCTRLPKT.ADDMODEWR = ADDR_FIXED; /* address mode write         */
      dmaSPIRxCTRLPKT.AUTOINIT = AUTOINIT_OFF; /* autoinit                   */

      /* - setting dma control packets */
      dmaSetCtrlPacket(DMA_CH12, dmaSPIRxCTRLPKT);
      /* - set channel 12 to high priority queue */
      dmaSetPriority(DMA_CH12, HIGHPRIORITY);
      /* - setting the dma channel to trigger on h/w request */
      dmaSetChEnable(DMA_CH12, DMA_HW);

      // enable Interrupt for BTC on channel 12
      dmaEnableInterrupt(DMA_CH12, BTC);

      // configuring MPU for DMA access.
      dmaDefineRegion(DMA_REGION0, (uint32_t)(FRAMRxBuffer), (uint32_t)(FRAMRxBuffer));
      dmaEnableRegion(DMA_REGION0, FULLACCESS, INTERRUPT_ENABLE);

      // Set current device to FRAM
      SetCurrentSPI4Device(FRAM);

      // enable DMA for SPI transfer
      spiREG4->INT0 |= 0x10000;

      // Correct FRAM Address and data to write pointer
      Address += Length;
      pDataToWrite += Length;

      if (xSemaphoreTake(xFRAMWriteSem, 1000 / portTICK_RATE_MS) == pdFALSE)
      {
         // timeout error
         RetVal = 0xFF;
      }
   }
   portRESET_PRIVILEGE(xRunningPrivileged);
   xSemaphoreGiveRecursive(xSPI4Mutex);
   xSemaphoreGive(xFRAMMutex);
   return RetVal;
}

Thanks and best regards

Christian

  • Christian,

    I do not think that there is a DMA access speed issue. Before you enter this function, you need to make sure that the SPI DMA request enable bit is cleared and the BTC flags of those two DMA channels are cleared. You can prime the DMA receive buffer and check the number of the data received when error occurs.

    As a personal preference, I would like to put the the header together with data and use DMA to do all the transfer. This would make software and especially debugging easier. I do not like switching the serving method to a peripheral dynamically.

    Thanks and regards,

    Zhaohong
  • Hello Zhaohong,

    thanks for your hints. I've tried to fill the receive buffer with a fixed value to see how much data is received until the error occurs. It is always 1 byte less than expected. I think this explains the SPI overrun Interrupt.

    The receive buffer is in external SDRAM. Can this be a performance problem for the DMA? I will try to move the buffer to internal RAM, but this is quit full so it can not stay there.

    Any other suggestions?

    Thanks and best regards

    Christian

  • Tried with buffer in internal RAM with no positive effect.
    BR
    Christian
  • Christian,

    Assume that the DMA block transfer size for the receive channel is 20 and you only see 19 data with SPI overrun flag set. It means that some how either the SPI DMA request does not reach DMA or DMA stops responding to the request. I definitely do not think that there is a transfer speed issue. Did you check if the block transfer flags are cleared before the new block transfer? Can you try using DMA for header+data?

    Thanks and regards,

    Zhaohong
  • Zaohong,

    Which flags (which register) do you mean exactly?

    I can not use DMA for header and data, because between WriteEnable command and Write command with address I have to set the CS line to high manually.

    BR

    Christian

  • Zaohong,

    If I reduce the byte count to Transfer by DMA from 8kB to 2kB its working fine (as far as I can say until know). How can this be explained?

    BR

    Christian

  • Christian,

    I need to spend more time on it. Did you say that you have the same issue when the buffer is in the on-chip RAM. If possible, would you please share your project?

    Thanks and regards,

    Zhaohong