Post Go back to editing

SPI Bug breaks our code? Sending with interrupt and then sending with DMA. First DMA transmission is corrupt

I'm using an ADSP-SC573. Need help, this breaks our code. Please run the supplied example code.

First it configures to send 4 bytes on the SPI using interrupt. Then it configures to send 50 bytes on SPI using DMA in a loop.

The problem is, if we send the 4 bytes using interrupt first then the first DMA transmission will be corrupt, only 2 bytes comes out (not 50). But after the first corrupt DMA transmission the following DMA transmissions are ok.

And if I don't send the 4 bytes using interrupt, then all DMA transmissions are OK.

It seems like there is some bug in the Analog devices drivers that can't handle that a SPI has been used in interrupt mode first and later in DMA mode.

Here is the code that shows this bug:

#include "adi_initialize.h"
#include <services/pwr/adi_pwr.h>
#include <drivers/spi/adi_spi.h>
#include <services/gpio/adi_gpio.h>
#include "spu.h"

#define DSTALIGN ADI_CACHE_ALIGN_MIN_4
#define SRCALIGN __attribute__((aligned(4)))
#if !defined(ADI_CACHE_LINE_LENGTH)
/* The ADI_CACHE_* macros were introduced in CCES 2.4.0 in <sys/platform.h>.
 * If using an older toolchain, define them here.
 */
#if defined(__ADSPARM__)
#define ADI_CACHE_LINE_LENGTH (32uL)
#define ADI_CACHE_ALIGN_MIN_4 __attribute__((aligned(ADI_CACHE_LINE_LENGTH)))
#elif defined(__ADSP215xx__)
#define ADI_CACHE_LINE_LENGTH (64uL)
#define ADI_CACHE_ALIGN_MIN_4 _Pragma("align 64")
#else
#error Unknown ADI_CACHE_LINE_LENGTH
#endif
#define ADI_CACHE_ROUND_UP_SIZE(size, type) \
  (((((((size) * sizeof(type)) + (ADI_CACHE_LINE_LENGTH - 1uL)) / ADI_CACHE_LINE_LENGTH) * ADI_CACHE_LINE_LENGTH) + (sizeof(type) - 1uL)) / sizeof(type))
#endif

#define SPI_DEVICE_NUMBER      1                    ///< The SPI device to use
#define SLAVE_SELECT_PIN       ADI_SPI_SSEL_ENABLE1 ///< The slave select pin to use
#define SPI_CLOCK_FREQUENCY_HZ 10000000             ///< Defines the SPI clock frequency in Hz

SRCALIGN uint8_t txBuffer2[50];///< SPI TX buffer

/// Memory required for the SPU
uint8_t spuMemory[ADI_SPU_MEMORY_SIZE];
/// SPU handle
ADI_SPU_HANDLE hSpu;

// Initializes the SPU instance.
bool setupSpu()
{
  if (adi_spu_Init(0u, spuMemory, nullptr, nullptr, &hSpu)) return false;

  // SPI1 TX DDE
  if (adi_spu_EnableMasterSecure(hSpu, 64, true) != ADI_SPU_SUCCESS) return false;

  // SPI1 TX DDE
  if (adi_spu_EnableMasterSecure(hSpu, 65, true) != ADI_SPU_SUCCESS) return false;

  // SPI1
  if (adi_spu_EnableMasterSecure(hSpu, 70, true) != ADI_SPU_SUCCESS) return false;

  return true;
}

// Send 4 bytes on SPI1 using interrupt
bool test1()
{
  uint8_t spiMemory[ADI_SPI_INT_MEMORY_SIZE]; ///< SPI memory
  ADI_SPI_HANDLE spiHandle;                   ///< SPI device handle

  if (adi_spi_Open(SPI_DEVICE_NUMBER, &spiMemory, sizeof(spiMemory), &spiHandle) != ADI_SPI_SUCCESS) return false;

  if (adi_spi_SetMaster(spiHandle, true)) return false;

  if (adi_spi_SetTransceiverMode(spiHandle, ADI_SPI_TXRX_MODE)) return false;

  if (adi_spi_EnableDmaMode(spiHandle, false)) return false;

  if (adi_spi_SetHwSlaveSelect(spiHandle, false)) return false;

  if (adi_spi_ManualSlaveSelect(spiHandle, false)) return false;

  if (adi_spi_SetTransmitUnderflow(spiHandle, true)) return false;

  if (adi_spi_SetClockPolarity(spiHandle, true)) return false;

  if (adi_spi_SetClockPhase(spiHandle, false)) return false;

  uint32_t fsysclk;
  uint32_t fsclk0; // Used for SPI0 and SPI1 (See SPI Port - Master Timing in data sheet)
  uint32_t fsclk1; // Used for SPI2

  if (adi_pwr_GetSystemFreq(0, &fsysclk, &fsclk0, &fsclk1)) return false;
  uint16_t spiClockRate = (uint16_t)(((float)fsclk0 / SPI_CLOCK_FREQUENCY_HZ) - 0.5f);

  if (adi_spi_SetClock(spiHandle, spiClockRate)) return false;

  if (adi_spi_SetSlaveSelect(spiHandle, SLAVE_SELECT_PIN)) return false;

  if (adi_spi_SetWordSize(spiHandle, ADI_SPI_TRANSFER_8BIT)) return false;

  if (adi_spi_SetTxWatermark(spiHandle, ADI_SPI_WATERMARK_50, ADI_SPI_WATERMARK_DISABLE, ADI_SPI_WATERMARK_DISABLE)) return false;

  if (adi_spi_SetRxWatermark(spiHandle, ADI_SPI_WATERMARK_50, ADI_SPI_WATERMARK_DISABLE, ADI_SPI_WATERMARK_DISABLE)) return false;

  if (adi_spi_RegisterCallback(spiHandle, 0, 0)) return false;

  uint8_t txBuffer[] = {0xff};
  uint8_t rxBuffer[3];
  ADI_SPI_TRANSCEIVER spiOrder = {txBuffer, sizeof(txBuffer), nullptr, 0, rxBuffer, sizeof(rxBuffer)};
  if (adi_spi_ReadWrite(spiHandle, &spiOrder)) return false;

  adi_spi_Close(spiHandle);

  return true;
}

volatile bool isBusySendingData = false;

void spiCallback(void *pCBParam, uint32_t Event, void *pArg)
{
  (void)pCBParam; // Unused
  (void)pArg;     // Unused
  ADI_SPI_EVENT event = (ADI_SPI_EVENT)Event;
  switch (event)
  {
    case ADI_SPI_TRANSCEIVER_PROCESSED:
      isBusySendingData = false;
      break;
    default:
      break;
  }
}

// Send 50 bytes on SPI1 using DMA
bool test2()
{
  uint8_t gpioMemory[ADI_GPIO_CALLBACK_MEM_SIZE];
  uint32_t gpioMaxCallbacks;
  if (adi_gpio_Init((void *)gpioMemory, sizeof(gpioMemory), &gpioMaxCallbacks)) return false;
  if (adi_gpio_SetDirection(ADI_GPIO_PORT_C, ADI_GPIO_PIN_14, ADI_GPIO_DIRECTION_OUTPUT)) return false;
  if (adi_gpio_Clear(ADI_GPIO_PORT_C, ADI_GPIO_PIN_14)) return false;

  uint8_t spiMemory[ADI_SPI_DMA_MEMORY_SIZE]; ///< SPI memory
  ADI_SPI_HANDLE spiHandle;                   ///< SPI device handle

  if (adi_spi_Open(SPI_DEVICE_NUMBER, &spiMemory, sizeof(spiMemory), &spiHandle) != ADI_SPI_SUCCESS) return false;

  if (adi_spi_SetMaster(spiHandle, true)) return false;

  if (adi_spi_SetTransceiverMode(spiHandle, ADI_SPI_TXRX_MODE)) return false; // Note: ADI_SPI_TX_MODE is not implemented in ADI drivers

  // Use DMA
  if (adi_spi_EnableDmaMode(spiHandle, true)) return false;

  if (adi_spi_SetHwSlaveSelect(spiHandle, false)) return false;

  if (adi_spi_ManualSlaveSelect(spiHandle, false)) return false;

  if (adi_spi_SetTransmitUnderflow(spiHandle, true)) return false;

  if (adi_spi_SetClockPhase(spiHandle, false)) return false;

  if (adi_spi_SetClockPolarity(spiHandle, true)) return false;

  // Setup SPI clock
  uint32_t fsysclk;
  uint32_t fsclk0; // Used for SPI0 and SPI1 (See SPI Port - Master Timing in data sheet)
  uint32_t fsclk1; // Used for SPI2

  if (adi_pwr_GetSystemFreq(0, &fsysclk, &fsclk0, &fsclk1)) return false;

  uint16_t spiClockRate = (uint16_t)(((float)fsclk0 / SPI_CLOCK_FREQUENCY_HZ) - 0.5f);

  if (adi_spi_SetClock(spiHandle, spiClockRate)) return false;

  if (adi_spi_SetSlaveSelect(spiHandle, SLAVE_SELECT_PIN)) return false;

  if (adi_spi_SetWordSize(spiHandle, ADI_SPI_TRANSFER_8BIT)) return false;

  if (adi_spi_SetDmaTransferSize(spiHandle, ADI_SPI_DMA_TRANSFER_8BIT)) return false;

  if (adi_spi_SetTxWatermark(spiHandle, ADI_SPI_WATERMARK_50, ADI_SPI_WATERMARK_DISABLE, ADI_SPI_WATERMARK_DISABLE)) return false;

  if (adi_spi_SetRxWatermark(spiHandle, ADI_SPI_WATERMARK_50, ADI_SPI_WATERMARK_DISABLE, ADI_SPI_WATERMARK_DISABLE)) return false;

  if (adi_spi_RegisterCallback(spiHandle, spiCallback, 0)) return false;

  ADI_SPI_TRANSCEIVER spiOrder = {txBuffer2, 50, nullptr, 0, nullptr, 0};
  isBusySendingData = true;
  adi_gpio_Set(ADI_GPIO_PORT_C, ADI_GPIO_PIN_14);
  if (adi_spi_SubmitBuffer(spiHandle, &spiOrder) != ADI_SPI_SUCCESS) return false;
  while (isBusySendingData);
  adi_gpio_Clear(ADI_GPIO_PORT_C, ADI_GPIO_PIN_14);

  adi_spi_Close(spiHandle);

  return true;

}

#define RUN_TEST1 // Define this to run test 1 (send 4 bytes witn interrupt)

int main(int argc, char *argv[])
{
  adi_initComponents();

  adi_pwr_Init(0, 25000000);

  setupSpu();
#ifdef RUN_TEST1
  test1();
#endif
  while (true)
  {
    test2();

    // A short delay
    for (volatile uint32_t i = 0; i < 1000; i++);
  }

  return 0;
}



Added the graphs again with the correct signal naming. Updated the code with SPU setup.
[edited by: masip at 7:58 AM (GMT -4) on 25 Mar 2022]