AnsweredAssumed Answered

SAI DMA double buffer problem

Question asked by Anders Esbensen on Oct 26, 2015
Latest reply on Mar 21, 2016 by Donald Bosley

Hi i am quite new to MQX and the Kinetis family.

 

I am trying to build a system using a K24 that uses the SAI/I2S interface together with the DMA to send and receive data across an AES/EBU interface.

 

I have based my code on the example in this post https://community.freescale.com/thread/335561 but have difficulties getting the system to run stable.

 

The SAI is running slave mode.  Bitclock is 12.288 Mhz, Fs is 48 Khz and in RX i receive 8x32 bit words in each frame. In TX direction only 4x8 bit word are received.

 

My problem is that i from time to time get data corruption on the I2S data, both RX and TX are affected, but error rate in RX seems to be highest.

 

Only way i have been able to get the system  running stable for longer period of time is by violating the rules for setting interrupt priorities, and increasing the DMA irq priorities to 2. This way i get stable data, but this lead to other problems with the timer system, and other task are affected ( LedTask).

 

Relevant part of my code is attached below.

 

The DMA is running 5 ms frame (IRQ at half buffer size) and the isr is being serviced each 5 ms.  Therefore I would not expect the DMA interrupt to be that critical since in theory have up to 5ms to either read or write new data into the different half's of the DMA buffers.

 

I am wondering why I need to call EDMA_HAL_HTCDSetHalfCompleteIntCmd from the ISR for each interrupt, since I have not been able to find anything in the reference manual that indicates that this is cleared after each irq.  Also as I see it this call is the only thing that could be really time critical on the ISR's.

 

Any suggestions are welcome .

 

#define MQX_LED_TASK_PRIORITY 15

#define MQX_PCM_TASK_PRIORITY 8  // The MQX RTOS using priority levels 1-7 so please start your task priorities at 8.

 

 

#define DMA_TX_PRIO 4 // according to user guide 4 and above are valid values

#define DMA_RX_PRIO 4 // according to user guide 4 and above are valid values

 

 

#define TX_DMA_BUFFER_LENGTH (4*1*240*2)  // 4 RFP's 1 samples pr tick- 5ms frames -> 240 ticks  Doublebuffer*2

#define RX_DMA_BUFFER_LENGTH (4*2*240*2)  // 4 RFP's 2 samples pr tick- 5ms frames -> 240 ticks  Doublebuffer*2

#define SAI_WATER_MARK_SIZE 4

 

 

static void I2sInit(void)

{

  reg_base = g_saiBase[0]; // Register base for SAI/I2S

 

 

  sai_tx_user_cfg.mclk_source = kSaiMclkSourceSysclk; /*!< Master clock source. @internal gui name="MCLK source" id="CfgMclkSource" */

  sai_tx_user_cfg.channel = 1;                        /*!< Which FIFO is used to transfer. @internal gui name="Channel" id="Channel" */

  sai_tx_user_cfg.sync_mode = kSaiModeAsync;          /*!< Synchronous or asynchronous. @internal gui name="Mode" id="Mode" */

  sai_tx_user_cfg.protocol = kSaiBusI2SType;          /*!< I2S left, I2S right or I2S type. @internal gui name="Protocol" id="BusType" */

  sai_tx_user_cfg.slave_master = kSaiSlave;           /*!< Master or slave. @internal gui name="Master / Slave mode" id="MasterSlave" */

  sai_tx_user_cfg.bclk_source = kSaiBclkSourceBusclk; /*!< Bit clock from master clock or other modules.  */

  sai_tx_user_cfg.watermark = SAI_WATER_MARK_SIZE;    /*!< When to send interrupt or dma request. @internal gui name="Watermark" id="Watermark" */

  sai_tx_user_cfg.dma_source = 1;                     /*!< Dma request source. @internal gui name="DMA request value" id="DmaRequest" */

 

 

  g_format_tx.bits = 8;

  g_format_tx.mclk = 12288000;

  g_format_tx.mono_stereo = kSaiStereo;

  g_format_tx.sample_rate = 48000;

 

 

  sai_rx_user_cfg.mclk_source = kSaiMclkSourceSysclk; /*!< Master clock source. @internal gui name="MCLK source" id="CfgMclkSource" */

  sai_rx_user_cfg.channel = 1;                        /*!< Which FIFO is used to transfer. @internal gui name="Channel" id="Channel" */

  sai_rx_user_cfg.sync_mode = kSaiModeSync;           /*!< Synchronous or asynchronous. @internal gui name="Mode" id="Mode" */

  sai_rx_user_cfg.protocol = kSaiBusI2SType;          /*!< I2S left, I2S right or I2S type. @internal gui name="Protocol" id="BusType" */

  sai_rx_user_cfg.slave_master = kSaiSlave;           /*!< Master or slave. @internal gui name="Master / Slave mode" id="MasterSlave" */

  sai_rx_user_cfg.bclk_source = kSaiBclkSourceBusclk; /*!< Bit clock from master clock or other modules. */

  sai_rx_user_cfg.watermark = SAI_WATER_MARK_SIZE;    /*!< When to send interrupt or dma request. @internal gui name="Watermark" id="Watermark" */

  sai_rx_user_cfg.dma_source = 0;                     /*!< Dma request source. @internal gui name="DMA request value" id="DmaRequest" */

 

 

  g_format_rx.bits = 32;

  g_format_rx.mclk = 12288000;

  g_format_rx.mono_stereo = kSaiStereo;

  g_format_rx.sample_rate = 48000;

 

 

  SAI_DRV_TxInit(0, &sai_tx_user_cfg, &sai_tx_state);

 

 

  SAI_DRV_RxInit(0, &sai_rx_user_cfg, &sai_rx_state);

 

 

  SAI_DRV_TxConfigDataFormat(0, &g_format_tx);

  SAI_DRV_RxConfigDataFormat(0, &g_format_rx);

 

 

  // Set framesize

  I2S_BWR_TCR4_FRSZ(reg_base,31u); // 32 x 8 bit frames in TX

 

 

  I2S_BWR_RCR4_FRSZ(reg_base,7u);  // 8 x 32 bit frames in RX

 

 

  I2S_WR_TMR(reg_base, 0xFFFFEEEE); // Enable 4 *8 bit control slots in TX (each forth slot in the first I2S half)

  I2S_WR_RMR(reg_base, 0xFFFFFF00); // Enable all 8 slots in Rx

}

 

void DmaRxInit(void)

{

  uint16_t i;

  uint32_t rx_fifo_addr;

  rx_fifo_addr = SAI_DRV_RxGetFifoAddr(0, 1);

 

  /* DMA */

  i2s0_rx_tcd = &edma_rx_transfer_descriptor[0];  //(edma_software_tcd_t *) malloc(sizeof(edma_software_tcd_t)+1);

  memset(i2s0_rx_tcd, 0, sizeof(edma_software_tcd_t));

  g_edma_rx_userconfig.chnArbitration = kEDMAChnArbitrationRoundrobin;

  g_edma_rx_userconfig.notHaltOnError = false;

  EDMA_DRV_Init(&g_edma_rx_state, &g_edma_rx_userconfig);

 

  EDMA_DRV_RequestChannel(0, kDmaRequestMux0I2S0Rx, &dmaCh0);

 

  EDMA_DRV_ConfigLoopTransfer(&dmaCh0, i2s0_rx_tcd, kEDMAPeripheralToMemory, (uint32_t) rx_fifo_addr,

                             (uint32_t) rx_ctrl_buffer, 4, 4*SAI_WATER_MARK_SIZE, RX_DMA_BUFFER_LENGTH<<2, 1);

  EDMA_DRV_StartChannel(&dmaCh0);

 

  SAI_DRV_RxSetDmaCmd(0, true);

  EDMA_DRV_InstallCallback(&dmaCh0, RxDmaCallback, (void *) &CbContext);

  EDMA_HAL_HTCDSetHalfCompleteIntCmd(DMA_BASE_PTR, 0, true);

  EDMA_DRV_StartChannel(&dmaCh0);

 

 

  OSA_InstallIntHandler(DMA0_IRQn, MQX_DMA0_IRQHandler);

  NVIC_SetPriority(DMA0_IRQn,DMA_RX_PRIO);

}

 

 

void DmaTxInit(void)

{

  uint32_t tx_fifo_addr;

  tx_fifo_addr = SAI_DRV_TxGetFifoAddr(0, 1);

 

  /* DMA */

  i2s0_tx_tcd = &edma_tx_transfer_descriptor[0];  //(edma_software_tcd_t *) malloc(sizeof(edma_software_tcd_t)+1);

  memset(i2s0_tx_tcd, 0, sizeof(edma_software_tcd_t));

  g_edma_tx_userconfig.chnArbitration = kEDMAChnArbitrationRoundrobin;

  g_edma_tx_userconfig.notHaltOnError = false;

  EDMA_DRV_Init(&g_edma_tx_state, &g_edma_tx_userconfig);

 

  EDMA_DRV_RequestChannel(1, kDmaRequestMux0I2S0Tx, &dmaCh1);

 

  EDMA_DRV_ConfigLoopTransfer(&dmaCh1, i2s0_tx_tcd, kEDMAMemoryToPeripheral, (uint32_t)tx_ctrl_buffer,

                              (uint32_t) tx_fifo_addr, 1, SAI_WATER_MARK_SIZE , TX_DMA_BUFFER_LENGTH, 1);

 

  EDMA_DRV_StartChannel(&dmaCh1);

 

  SAI_DRV_TxSetDmaCmd(0, true);

  EDMA_DRV_InstallCallback(&dmaCh1, TxDmaCallback, (void *) &CbContext);

  EDMA_HAL_HTCDSetHalfCompleteIntCmd(DMA_BASE_PTR, 1, true);

  EDMA_DRV_StartChannel(&dmaCh1);

 

  OSA_InstallIntHandler(DMA1_IRQn, MQX_DMA1_IRQHandler);

  NVIC_SetPriority(DMA1_IRQn,DMA_TX_PRIO);

}

 

void TxDmaCallback(void *param, edma_chn_status_t status)

{

  uint32_t TxFBytes;

#ifdef TX_DMA_DEBUG

  GPIO_DRV_SetPinOutput(kGpioM4_DEBUG_6);

#endif

  TxFBytes = EDMA_HAL_HTCDGetFinishedBytes(DMA_BASE_PTR,1);

 

  if(TxFBytes >= 0x3c0)

  {

    _event_set(event_ptr, 0x04);

    EDMA_HAL_HTCDSetHalfCompleteIntCmd(DMA_BASE_PTR, 1, true);

  }

  else

  {

    _event_set(event_ptr, 0x08);

    EDMA_HAL_HTCDSetHalfCompleteIntCmd(DMA_BASE_PTR, 1, true);

  }

#ifdef TX_DMA_DEBUG

  GPIO_DRV_ClearPinOutput(kGpioM4_DEBUG_6);

#endif

}

 

void RxDmaCallback(void *param, edma_chn_status_t status)

{

  uint32_t  FBytes;

  #ifdef RX_DMA_DEBUG

  GPIO_DRV_SetPinOutput(kGpioM4_DEBUG_7);

#endif

 

  FBytes = EDMA_HAL_HTCDGetFinishedBytes(DMA_BASE_PTR,0);

 

  if(FBytes >= 0x1e00)

  {

    _event_set(event_ptr, 0x01);

    EDMA_HAL_HTCDSetHalfCompleteIntCmd(DMA_BASE_PTR, 0, true);

  }

  else

  {

    _event_set(event_ptr, 0x02);

    EDMA_HAL_HTCDSetHalfCompleteIntCmd(DMA_BASE_PTR, 0, true);

  }

#ifdef RX_DMA_DEBUG

  GPIO_DRV_ClearPinOutput(kGpioM4_DEBUG_7);

#endif

}

 

static void PcmTask(uint32_t param)

{

  uint16_t y;

  uint8_t rfp_id=0;

  uint16_t cnt=0;

 

  I2sInit();

 

  DmaRxInit();

  DmaTxInit();

 

  // Start I2S interface

  SAI_DRV_RxStartModule(0);

  SAI_DRV_TxStartModule(0);

 

  _event_create("global");

  _event_open("global", &event_ptr);

 

  while(1)

  {

    uint16_t i = 0;

    _event_wait_any(event_ptr, 0x0f, 0);

 

    uint32_t ev_val;

    _event_get_value(event_ptr, &ev_val);

 

    i = 0;

    /*******************************************

    *   Rx processing

    *******************************************/

 

 

    if(ev_val & 1) // First half processed

    {

      _event_clear(event_ptr,1);

      GPIO_DRV_SetPinOutput(kGpioM4_DEBUG_5);

      for (i = 0; i < (RX_DMA_BUFFER_LENGTH>>1);i+=8 )

      {

        PutRxData((rx_ctrl_buffer[i+0]  & 0xFF000000) >> 24, 0);

        PutRxData((rx_ctrl_buffer[i+1]  & 0xFF000000) >> 24, 1);

        PutRxData((rx_ctrl_buffer[i+2]  & 0xFF000000) >> 24, 2);

        PutRxData((rx_ctrl_buffer[i+3]  & 0xFF000000) >> 24, 3);

      }

      GPIO_DRV_ClearPinOutput(kGpioM4_DEBUG_5);

    }

 

 

    if(ev_val & 2) // second half process

    {

      _event_clear(event_ptr,2);

      GPIO_DRV_SetPinOutput(kGpioM4_DEBUG_5);

      for (i = 0; i < (RX_DMA_BUFFER_LENGTH>>1); i+=8)

      {

          PutRxData((rx_ctrl_buffer[(RX_DMA_BUFFER_LENGTH>>1)+i+0]  & 0xFF000000) >> 24, 0);

          PutRxData((rx_ctrl_buffer[(RX_DMA_BUFFER_LENGTH>>1)+i+1]  & 0xFF000000) >> 24, 1);

          PutRxData((rx_ctrl_buffer[(RX_DMA_BUFFER_LENGTH>>1)+i+2]  & 0xFF000000) >> 24, 2);

          PutRxData((rx_ctrl_buffer[(RX_DMA_BUFFER_LENGTH>>1)+i+3]  & 0xFF000000) >> 24, 3);

      }

      GPIO_DRV_ClearPinOutput(kGpioM4_DEBUG_5);

    }

 

 

    /*******************************************

    *   Tx processing

    *******************************************/

    if(ev_val & 4)

    {

      //_event_clear(event_ptr,4);

      GPIO_DRV_SetPinOutput(kGpioM4_DEBUG_4);

      for (i = 0; i < (TX_DMA_BUFFER_LENGTH>>1);i+=4)

      {

        GetTxData(&tx_ctrl_buffer[i+0],0);

        GetTxData(&tx_ctrl_buffer[i+1],1);

        GetTxData(&tx_ctrl_buffer[i+2],2);

        GetTxData(&tx_ctrl_buffer[i+3],3);

      }

      _event_clear(event_ptr,4);

      GPIO_DRV_ClearPinOutput(kGpioM4_DEBUG_4);

    }

    if(ev_val & 8)

    {

      //_event_clear(event_ptr,8);

      GPIO_DRV_SetPinOutput(kGpioM4_DEBUG_4);

      for (i = 0; i < (TX_DMA_BUFFER_LENGTH>>1);i+=4)

      {

        GetTxData(&tx_ctrl_buffer[(TX_DMA_BUFFER_LENGTH>>1)+i+0],0);

        GetTxData(&tx_ctrl_buffer[(TX_DMA_BUFFER_LENGTH>>1)+i+1],1);

        GetTxData(&tx_ctrl_buffer[(TX_DMA_BUFFER_LENGTH>>1)+i+2],2);

        GetTxData(&tx_ctrl_buffer[(TX_DMA_BUFFER_LENGTH>>1)+i+3],3);

      }

      _event_clear(event_ptr,8);

      GPIO_DRV_ClearPinOutput(kGpioM4_DEBUG_4);

    }

  }// while(1)

}

 

 

Only other task running is :

 

static void LedTask(uint32_t param)

{

  while (1)

  {

    LED1_TOGGLE;

    delay_ms(50);

    LED4_TOGGLE;

    delay_ms(50);

    LED2_TOGGLE;

    delay_ms(50);

    LED3_TOGGLE;

    delay_ms(500);

  }

}

Outcomes