cancel
Showing results for 
Search instead for 
Did you mean: 

Data coherency with DMA and I2S audio on STM32H7

Alefal
Associate II

Moving from a audio effect prototype based on the STM32F407 to the STM32H757. Did a lot of stuff on the F4 with audio but on the H7 I'm getting stuck and can't get a simple audio loopback with DMA.

I'm using Cirrus Logic's CS5343 and CS4344 as external ADC/DAC connected to the I2S interface. I2S data frames are read and written in memory with DMA configured in circular RX/TX mode

.

Without DMA, audio loopback is OK. However, with DMA, the RX buffer isn't filled. The half complete and complete callbacks are triggered tho.

I'm aware that DMA can't access DTCM RAM and that with cache enabled, data coherency should be taken care of thanks to the MPU configuration and modification of the linker script :

MPU creates a region of 128kB device memory type (TEX : 0b000, not cacheable, bufferable, shareable)

RX and TX buffers (8kB each) subsections have been placed at 0x30010000 and 0x30012000 (SRAM1 in D2) in the linker script.

I tried to move the buffers in other RAM domains, make the MPU Region as shareable normal memory type but my Rx Buffer remains empty and/or doesn't update.

What I don't get is why my rx buffer isn't filled and updated by the DMA in the first place. If anyone got any clue or hints about what I'm missing, I'm all ears.

You'll find my whole main.c file attached.

And here's the modification of the linker script I've done :

.buffers(NOLOAD) :

{

. = ALIGN(4);

. = ABSOLUTE (0x30010000);

*(.rxBuffer)

. = ABSOLUTE (0x30012000);

*(.txBuffer)

} >RAM_D2

1 ACCEPTED SOLUTION

Accepted Solutions
LCE
Lead III

Maybe using 24 bit in I2S peripheral and half-word in DMA is not such a good idea?

In your I2S2_Init:

 hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;

DMA init:

hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

I have both I2S and DMA set to 32 bit , that's working here on H723 / H735.

View solution in original post

18 REPLIES 18
LCE
Lead III

I guess the DMA init and start for I2S is done somewhere... ? 😉

I would have guessed it's some "DMA_x cannot access SRAM_y / Domain_z", but you seem to have checked that.

Have you checked the map file if the buffers are really placed where they should be?

And does the H757 also have this feature how the AXI Ram is split / used? Maybe there's something wrong?

And then comes DMA and the cache - which I simply disabled on the H735 I'm working with...

Alefal
Associate II

DMA and I2S is done in main function.

Yes the build analyser, expression debugger tell me buffers are at the adresses I want them to be.

What do you mean by AXI Ram being split ? Are you refering to DTCM Ram ? My buffers are placed in D2 domain in SRAM1.

I always use the DMA_TXRX function so I'm not really sure when to call the Invalidate/Clean Cache functions. And I would prefer a cleaner solution where I define a non cacheable memory region with the MPU.

If you disable data cache altogether, DMA works as expected?

JW

> DMA and I2S is done in main function.

I can't find the init for hdma_spi2_rx / tx. Only the interrupt enables for the streams.

Neither some "start DMA transfer" function.

Peripheral's DMA (and GPIO) init in HAL/Cube is usually done in "Msp_Init()" or so, which is called by the peripheral's HAL_Init().

> What do you mean by AXI Ram being split ?

The H723..H735 has some feature where you can assign some portions of SRAM to AXI or ITCM.

> ... cache

Never used it. Maybe try turning it off.

Alefal
Associate II

> Peripheral's DMA (and GPIO) init in HAL/Cube is usually done in "Msp_Init()" or so, which is called by the peripheral's HAL_Init().

Yes, it's done in MSP_Init();

> The H723..H735 has some feature where you can assign some portions of SRAM to AXI or ITCM.

Fine, I'm putting my buffers into D2 domain anyway

DMA's callbacks are always triggered but buffers are always filled with zeros if when disabling the cache.

I can fill the buffers without DMA though.

LCE
Lead III

Maybe you post your DMA (hdma_spi2_rx, hdma_spi2_tx) init here?

Edit: in case you are using HAL stuff, sometimes there's one or the other peripheral's DMA or interrupt enable missing. Checked that?

Piranha
Chief II

> MPU creates a region of 128kB device memory type (TEX : 0b000, not cacheable, bufferable, shareable)

Normal non-cacheable memory is enough.

https://community.st.com/s/question/0D53W00001Z9K9TSAV/maintaining-cpu-data-cache-coherence-for-dma-buffers

Alefal
Associate II

The hdma_spi2_rx and hdma_spi2_tx inits are done in the HAL_I2S_MspInit(); called in the HAL_I2S_Init();

void HAL_I2S_MspInit(I2S_HandleTypeDef* hi2s)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

 if(hi2s->Instance==SPI2)

 {

 /* USER CODE BEGIN SPI2_MspInit 0 */

 /* USER CODE END SPI2_MspInit 0 */

 /** Initializes the peripherals clock

 */

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SPI2;

  PeriphClkInitStruct.PLL2.PLL2M = 4;

  PeriphClkInitStruct.PLL2.PLL2N = 25;

  PeriphClkInitStruct.PLL2.PLL2P = 1;

  PeriphClkInitStruct.PLL2.PLL2Q = 2;

  PeriphClkInitStruct.PLL2.PLL2R = 2;

  PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_2;

  PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOMEDIUM;

  PeriphClkInitStruct.PLL2.PLL2FRACN = 3072;

  PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2;

  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)

  {

   Error_Handler();

  }

  /* Peripheral clock enable */

  __HAL_RCC_SPI2_CLK_ENABLE();

  __HAL_RCC_GPIOC_CLK_ENABLE();

  __HAL_RCC_GPIOB_CLK_ENABLE();

  /**I2S2 GPIO Configuration

  PC6   ------> I2S2_MCK

  PB12   ------> I2S2_WS

  PB15   ------> I2S2_SDO

  PB13   ------> I2S2_CK

  PB14   ------> I2S2_SDI

  */

  GPIO_InitStruct.Pin = GPIO_PIN_6;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;

  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_13|GPIO_PIN_14;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

  GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;

  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* I2S2 DMA Init */

  /* SPI2_RX Init */

  hdma_spi2_rx.Instance = DMA1_Stream0;

  hdma_spi2_rx.Init.Request = DMA_REQUEST_SPI2_RX;

  hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

  hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;

  hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;

  hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

  hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

  hdma_spi2_rx.Init.Mode = DMA_CIRCULAR;

  hdma_spi2_rx.Init.Priority = DMA_PRIORITY_LOW;

  hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

  if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)

  {

   Error_Handler();

  }

  __HAL_LINKDMA(hi2s,hdmarx,hdma_spi2_rx);

  /* SPI2_TX Init */

  hdma_spi2_tx.Instance = DMA1_Stream1;

  hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;

  hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

  hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;

  hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;

  hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

  hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

  hdma_spi2_tx.Init.Mode = DMA_CIRCULAR;

  hdma_spi2_tx.Init.Priority = DMA_PRIORITY_LOW;

  hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

  if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)

  {

   Error_Handler();

  }

  __HAL_LINKDMA(hi2s,hdmatx,hdma_spi2_tx);

 /* USER CODE BEGIN SPI2_MspInit 1 */

 /* USER CODE END SPI2_MspInit 1 */

 }

}