cancel
Showing results for 
Search instead for 
Did you mean: 

STM32N6570-DK SPI DMA inconsistent transfers

TerZer
Associate II

Hi all,

I'm having trouble getting reliable SPI communication using the STM32N6570-DK board. I'm using SPI5 with DMA, but the data I receive is only correct about 50% of the time.
I'm expecting to receive the value 1025, but often get corrupted values like 1024, 512, or 256. However, on the oscilloscope, the SPI signal is always correct—it doesn't vary between good and bad reads.
I've already taken steps to avoid cache issues by placing the DMA buffers in a non-cacheable memory region:

__attribute__((section(".noncacheable"))) static uint8_t spi_tx_buf[IO_CHUNK_SIZE]; 
__attribute__((section(".noncacheable"))) static uint8_t spi_rx_buf[IO_CHUNK_SIZE];

My MPU configuration for this region is as follows:

/**
  * @brief  Configure the MPU attributes for the non-cacheable RAM section.
  * @note   This function should be called before enabling the caches.
  */
static void MPU_Config_NOCACHE(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct = {0};

  /* Make sure the address is aligned to the region size */
  uint32_t region_addr = (uint32_t)&__snoncacheable;
  uint32_t region_end = (uint32_t)&__enoncacheable;

  /* The MPU region size must be a power of 2 and aligned
   * Choose the smallest power of 2 that covers the section.
   * IO_CHUNK_SIZE is 512, so 2 * 512 + flags is ~1KB. A 2KB region is safe.
  */

  HAL_MPU_Disable(); // Disable MPU before configuration

  /* Configure the MPU attributes for the Non-Cacheable RAM region */
  MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  MPU_InitStruct.Number           = MPU_REGION_NUMBER5; // Use an available region number
  MPU_InitStruct.BaseAddress      = region_addr;
  MPU_InitStruct.LimitAddress     = region_end;
  MPU_InitStruct.AttributesIndex  = MPU_ATTRIBUTES_NUMBER2;
  MPU_InitStruct.AccessPermission = MPU_REGION_ALL_RW;
  MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.DisablePrivExec  = MPU_PRIV_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable      = MPU_ACCESS_OUTER_SHAREABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); // Re-enable MPU
}

Attributes:

MPU_AttributesInit.Number = MPU_ATTRIBUTES_NUMBER2; 
MPU_AttributesInit.Attributes = MPU_DEVICE_NGNRNE;

Memory:

  .noncacheable :
  {
    . = ALIGN(8);
    __snoncacheable = .;/* create symbol for start of section */
    KEEP(*(.noncacheable))
    . = ALIGN(8);
    __enoncacheable = .;  /* create symbol for end of section */
  } > RAM

SPI5 is initialized as master with an 8-bit data size and 60 MHz SPI clock (prescaler 2). Here's my MX_SPI5_Init configuration:

void MX_SPI5_Init(void)
{
  /* SPI5 parameter configuration*/
  hspi5.Instance = SPI5;
  hspi5.Init.Mode = SPI_MODE_MASTER;
  hspi5.Init.Direction = SPI_DIRECTION_2LINES;
  hspi5.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi5.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi5.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi5.Init.NSS = SPI_NSS_SOFT;
  hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi5.Init.CRCPolynomial = 0x7;
  hspi5.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  hspi5.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi5.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi5.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi5.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi5.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi5.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi5.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  hspi5.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
  hspi5.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
  if (HAL_SPI_Init(&hspi5) != HAL_OK)
  {
    Error_Handler();
  }
}

I'm using DMA to transmit 2 bytes and receive 2 bytes. Length is set to 4 bytes and check rx buffer bytes rx[3] and rx[4]:

HAL_StatusTypeDef CUSTOM_SPI_TransmitReceive(const uint8_t* tx, uint8_t* rx, uint16_t len)
{
    if (len > IO_CHUNK_SIZE) return HAL_ERROR;
    memset(spi_rx_buf, 0x00, IO_CHUNK_SIZE);
    memset(spi_tx_buf, 0x00, IO_CHUNK_SIZE);
    SCB_CleanDCache_by_Addr(tx, align32(len));
    memcpy(spi_tx_buf, tx, len);
    dma_txrx_done = 0;
    if (HAL_SPI_TransmitReceive_DMA(&hspi5, spi_tx_buf, spi_rx_buf, len) != HAL_OK) return HAL_ERROR;
    if (wait_for_flag(&dma_txrx_done) != HAL_OK) return HAL_TIMEOUT;
    memcpy(rx, spi_rx_buf, len);
    SCB_CleanDCache_by_Addr(rx, align32(len));
    return HAL_OK;
}

The DMA completion flag is set in the callback:

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if (hspi->Instance == SPI5) {
		dma_txrx_done = 1;
	}
}

DMA and SPI interrupts are enabled and set to priority 5.

Despite all this, about half the reads return corrupted values. Oscilloscope traces show perfect SPI transactions every time.

Could this be a subtle memory or synchronization issue? Or is there something wrong with the way I’m handling DMA, cache, or buffer usage?

Any suggestions would be greatly appreciated.

Thanks!

0 REPLIES 0