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.

how to avoid the SPI slave mode writes to Tx FiFO can lose data

Part Number: CC2642R

Tool/software: TI-RTOS

Hi TI experts,

I am trying to implement SPI slave under TI-RTOS driver environment with the below code.

Without semaphore methods using SW CSN pin triggering, instead, with HW CSN pin handling(that is, #define CC26X2R1_SATELLITE_SPI0_CSN              IOID_12), I was able to see the data frame on MISO line with updated values from 'SPI_TRANSFER_COMPLETED'.

By the way, after adding the semaphore and SW CSN pin triggering((that is, #define CC26X2R1_SATELLITE_SPI0_CSN              PIN_UNASSIGNED, instead,with  #define Board_CSN_0             IOID_12), only the data frame on MISO line is always 0x00. And it shows always 'SPI_TRANSFER_FAILED' or no 'SPI_TRANSFER_COMPLETED'.

I didn't understand on how to avoid the 'Writes to Transmit FiFO can Lose data' on your errata sheet.

should I pad two bytes with 0x00 as dummy message ?

Thanks in advance,

Ji-won Lee

/*********************************************************************
 * CONSTANTS
 */
 
#define TASKSTACKSIZE   1024

#define SPI_PACKET_COUNT 4

/*********************************************************************
 * TYPEDEFS
 */


/*********************************************************************
 * GLOBAL VARIABLES
 */

// Task data
Task_Struct SpiSlaveTask;
Char SpiSlaveTaskStack[TASKSTACKSIZE];

static Semaphore_Struct semSpiSlaveTaskAlert;

/*********************************************************************
 * LOCAL VARIABLES
 */


SPI_Handle spiHandle;
SPI_Params spiParams;
SPI_Transaction spiTransaction;
uint16_t txBuf[SPI_PACKET_COUNT];
uint16_t rxBuf[SPI_PACKET_COUNT];                              // Receive buffer
const uint8_t transferSize = SPI_PACKET_COUNT;

PIN_Handle pinHandle;
PIN_Config pinConfig[] = {
    PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE | Board_CSN_0,
    PIN_TERMINATE  // Terminate list
};

static circularQueue_t   RxQueueForSpiSlave;   

void SPI_send(void * _txData, void * _rxData, size_t _size)
{
  bool transferOK;

  spiTransaction.count = _size;
  spiTransaction.txBuf = _txData;
  spiTransaction.rxBuf = _rxData;

  transferOK = SPI_transfer(spiHandle, &spiTransaction);
  if(!transferOK)
  {
    Display_print0(dispHandle, 0, 0, "[SPI Slave]generating failed!!!!!!\r\n");   
    //SPI_transferCancel(spiHandle);
  }    
}

// Chip select callback
static void chipSelectCallback(PIN_Handle handle, PIN_Id pinId)
{
  bool transferOK = FALSE;      
  #if 0
  // Release the chip select pin
  PIN_remove(handle, pinId);
 
  // Open SPI driver
  spiHandle = SPI_open(Board_SPI0, &spiParams);
  #endif    
    
  // Wake up the OS task
  Semaphore_post(Semaphore_handle(&semSpiSlaveTaskAlert));      
}

// SPI transfer callback
static void SpiTransferCbFxn(SPI_Handle handle, SPI_Transaction *transaction)
{
  #if 0
  // Close the SPI driver
  SPI_close(handle);
 
  // Add chip select back to the PIN driver
  PIN_add(pinHandle, pinConfig[0]);
 
  // Register chip select callback
  PIN_registerIntCb(pinHandle, chipSelectCallback);
  #endif   
 
  // Wake up the OS task
  //Semaphore_post(Semaphore_handle(&semSpiSlaveTaskAlert));   
 
  if(transaction->status == SPI_TRANSFER_COMPLETED)
  {
    {
      #if 1
      static uint8 k = 0, j;      
      k++;
      for (j = 0; j < SPI_PACKET_COUNT; j++ )
        txBuf[j] = k;

      SPI_send(txBuf, NULL, sizeof(txBuf));
      #endif   
    }     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_COMPLETED] Count : %d\r\n", transaction->count);    
  }
  else if(transaction->status == SPI_TRANSFER_STARTED)
  {     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_STARTED] Count : %d\r\n", transaction->count);      
  }    
  else if(transaction->status == SPI_TRANSFER_CANCELED)
  {     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_CANCELED] Count : %d\r\n", transaction->count);
  }
  else if(transaction->status == SPI_TRANSFER_FAILED)
  {     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_FAILED] Count : %d\r\n", transaction->count);
    /*
    * # Timeout #
    * Timeout can occur in ::SPI_MODE_BLOCKING, there's no timeout in ::SPI_MODE_CALLBACK.
    * When in ::SPI_MODE_CALLBACK, the transfer must be cancelled by calling SPI_transferCancel().\n
    * If a timeout happens in either ::SPI_SLAVE or ::SPI_MASTER mode,
    * the receive buffer will contain the bytes received up until the timeout occurred.
    * The SPI transaction status will be set to ::SPI_TRANSFER_FAILED.     
    */
    SPI_transferCancel(spiHandle);
  }    
  else if(transaction->status == SPI_TRANSFER_CSN_DEASSERT)
  {     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_CSN_DEASSERT] Count : %d\r\n", transaction->count);
    /*      
    * if PARTIAL_RETURN is enabled, the transfer will end when chip select is deasserted.
    * the SPI_Transaction.status and the SPI_transcation.count will be updated to indicate
    * whether the transfer ended due to chip select deassertion and how many bytes were transferred.
    */      
  }    
  else if(transaction->status == SPI_TRANSFER_PEND_CSN_ASSERT)
  {     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_PEND_CSN_ASSERT] Count : %d\r\n", transaction->count);
  }     
  else if(transaction->status == SPI_TRANSFER_QUEUED)
  {     
    Display_printf(dispHandle, 0, 0, "[SPI_TRANSFER_QUEUED] Count : %d\r\n", transaction->count);
  }    
  else
  {
    Display_printf(dispHandle, 0, 0, "[TRANSFERRING] Count : %d\r\n", transaction->count);
  }     
}


uint16 spiSlaveRxDataCallback(void   *p_rx_buffer,
                                 uint16  length,
                                 uint16 *p_additional_req_data_length)
{
    uint8 rx_data;
    //Display_printf(dispHandle, 6, 0, "\r\nlength(2): %x\r\n", length);
    for (int8 i = 0; i < length; i++ )
    {
        getDataFromQueue(p_rx_buffer, &rx_data);
        processRxSpiData(&rx_data, length);
    }

    *p_additional_req_data_length -= length;

    /* Return the number of bytes that have been processed */
    return length;        
}

static void BLECentral_processSpiSlaveforMainNode(uint8_t length)
{
  static uint16 length_to_be_handled = 0;
  uint8 i = 0;

  //Display_printf(dispHandle, 6, 0, "\r\nSPI data length: %d", length);

  //while (length--)
  for ( i = 0; i < length; i++ )
  {   
      uint16 return_value = rxBuf[i];
      uint8 rx_data = return_value & 0x00ff;
      putDataToQueue(&RxQueueForSpiSlave, rx_data);
      //Display_print1(dispHandle, 6, 0, "\r\nSPI data: %x", rx_data);
      length_to_be_handled++;
      //Display_printf(dispHandle, 6, 0, "\r\nSPI data(1): 0x%02x, length_to_be_handled: %x", rx_data, length_to_be_handled);
  }

  spiSlaveRxDataCallback((void*)&RxQueueForSpiSlave, length_to_be_handled, &length_to_be_handled);      
}

/*********************************************************************
 * @fn      SpiSlaveFxn
 *
 * @brief   Application task entry point for Huf SPI Slave
 *
 * @param   a0, a1 - not used.
 *
 * @return  None.
 */
Void SpiSlaveFxn(UArg arg0, UArg arg1)
{  
  bool transferOK = FALSE;  
  static int8 rxFifoCount = 0;
  uint8_t i;
  PIN_State   pinState;

  SPI_init();  
  SPI_Params_init(&spiParams);
  spiParams.dataSize            = 16;                 // 16-bit data size(4-bit ~ 16-bit)
  spiParams.bitRate             = 1000000;            // 1MHz
  spiParams.frameFormat         = SPI_POL0_PHA0;
  spiParams.mode                = SPI_SLAVE;
  spiParams.transferMode        = SPI_MODE_CALLBACK;
  spiParams.transferCallbackFxn = SpiTransferCbFxn;
 
  // Configure the transaction
  spiTransaction.arg = NULL;  
  spiTransaction.count = SPI_PACKET_COUNT;            // maximum length is 1024 if stack size is enough.
  spiTransaction.txBuf = txBuf;
  spiTransaction.rxBuf = rxBuf;
 
  // Open the SPI and initiate the first transfer
  spiHandle = SPI_open(Board_SPI0, &spiParams);
  if (spiHandle == NULL)
  {
    while(1);           // SPI_open() failed
  }
 
  // Enable RETURN_PARTIAL
  //SPI_control(spiHandle, SPICC26XXDMA_RETURN_PARTIAL_ENABLE, NULL);

  // First echo message
  for (i = 0; i < transferSize; i++) {
      txBuf[i] = i + 1;
      rxBuf[i] = i + 1;      
  }
 
  #if 1
  // Open PIN driver and configure chip select pin callback
  pinHandle = PIN_open(&pinState, pinConfig);
  PIN_registerIntCb(pinHandle, chipSelectCallback);  
  #endif
 
#if 1
  SPI_send(txBuf, rxBuf, sizeof(txBuf));
#endif

  // Wait forever
  while (1) {       
   
    // Wait for an ALERT callback
    // Allow application to sleep until receiving the desired number of characters.
    Semaphore_pend(Semaphore_handle(&semSpiSlaveTaskAlert), BIOS_WAIT_FOREVER);        
    
    // all characters currently in the RX FIFO
    rxFifoCount = SPI_PACKET_COUNT;   
    BLECentral_processSpiSlaveforMainNode(rxFifoCount);
  }
}

void SpiSlaveCreateTask(void)
{
  Task_Params taskParams;

  // Configure the OS task
  Task_Params_init(&taskParams);
  taskParams.stack = SpiSlaveTaskStack;
  taskParams.stackSize = sizeof(SpiSlaveTaskStack);
  taskParams.priority = 1;
  Task_construct(&SpiSlaveTask, SpiSlaveFxn, &taskParams, NULL);

  // Create the semaphore used to wait for Sensor Controller ALERT events
  Semaphore_Params semParams;
  Semaphore_Params_init(&semParams);
  semParams.mode = Semaphore_Mode_BINARY;
  /* Construct APIs are given a data structure with which to store hte instance's variables.
   * As the memory has been pre-allocated for the instance, error checking may not be required after constructing
   */
  Semaphore_construct(&semSpiSlaveTaskAlert, 0, &semParams);
}

  • Hi Ji-Won,

    To workaround the errata ('Writes to Transmit FiFO can Lose data' ) you would need to append each slave transaction with a dummy frame (in 16-bit mode, this would be two bytes). This means that the master needs to always filter away the first frame received from the slave.

    Regarding the SW vs HW CS, have you read the SSI chapter of the Technical reference manual (TRM) regarding frame formats? For Motorola SPI Format SPO0 SPH0, the CS (or FSS as it is called in the TRM) need to be toggled between every frame. This could cause issues when not using the HW CS as a slave device.
  • Thanks M-W,

    when adding the dummy frame for each slave transaction, does the 'count' have to have the number of the existing frame + 1?
    as using 16-bit data size for each frame, but count has to have 1 plus. right?
  • Hi Ji-Won,

    Yes, this would have to be included into the count as we are actually sending an extra frame over the wire. The count is the number of frames, if you are using a 16-bit frame, this is still only counts as one.