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]