Skip to main content
Associate II
Feb 1, 2024 2:03 AM UTC
Question

Audio glitches on SAI slave sub-blocks (STM32H7)

Hi all,

I am using the SAI peripheral on the STM32H753 and I have it set-up as follows:

SAI2A - Master TX, asynchronous

SAI2B - Slave RX, synchronous with sub-block A

SAI3A - Slave TX, synchronous with SAI2

SAI3B - Slave RX, synchronous with SAI2

(SAI2 SYNCOUT has been set to SAI_SYNCEXT_OUTBLOCKA_ENABLE)

 

Audio from SAI2A is clean.

Audio from all other SAI sub-blocks has glitches as shown below.

Audio 1 - Source signal

Audio 2 - Output from SAI2B

Slave-SAI-Glitch.PNG

I am using the simplified initialization mode using HAL_SAI_InitProtocol() with number of slots set to 2 and data size at 32 bits.

I am starting all the slave sub-blocks before starting the master. I have also tried adding a delay between starting the slaves and the master, but it didn't make a difference to the behaviour.

I am using circular DMA to transfer audio to/from the SAI sub-blocks and I have disabled D-Cache for the SRAM section which contains the DMA buffers.

SAI FIFO thresholds are currently set to SAI_FIFOTHRESHOLD_EMPTY, but I have also tried other FIFO levels (half/full) and it has not made a difference to the behaviour.

 

I appreciate any thoughts on the setup and possible explanations for the behaviour.

Thank you

This topic has been closed for replies.

7 replies

LCE
Principal III
Feb 1, 2024 11:41 AM UTC

Are all DMA buffers in the correct RAM areas? Check buffer addresses and compare to memory area / ref manual.
In case of doubt use linker file / attribute to force buffers where they can be accessed by the DMA.

DMA priorities set high?

What are you doing in the half / complete callbacks? Any lengthy buffer copying?

 

sweeraAuthor
Associate II
Feb 2, 2024 12:43 AM UTC

Thanks for the suggestions.

I have my all my DMA buffers in SRAM2 (using a custom linker file)

DMA priorities are set to HIGH (will test with VERY HIGH as well)

DMA half/complete callbacks are as follows. Only giving a semaphore to unblock buffer copy in non-ISR context.

 
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
    struct sai_stm32_data *sai_data = CONTAINER_OF(hsai, struct sai_stm32_data, tx_hsai);
    struct stream *stream = &sai_data->tx;

    k_sem_give(&stream->sem);
}

void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
    struct sai_stm32_data *sai_data = CONTAINER_OF(hsai, struct sai_stm32_data, tx_hsai);
    struct stream *stream = &sai_data->tx;

    k_sem_give(&stream->sem);
}

void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
    struct sai_stm32_data *sai_data = CONTAINER_OF(hsai, struct sai_stm32_data, rx_hsai);
    struct stream *stream = &sai_data->rx;

    k_sem_give(&stream->sem);
}
 
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
    struct sai_stm32_data *sai_data = CONTAINER_OF(hsai, struct sai_stm32_data, rx_hsai);
    struct stream *stream = &sai_data->rx;

    k_sem_give(&stream->sem);
}
LCE
Principal III
Feb 2, 2024 10:49 AM UTC

> Only giving a semaphore to unblock buffer copy in non-ISR context.

To make buffer copying safer, do you use Double Buffer Moder for the DMA transfers?
If not, try that.

sweeraAuthor
Associate II
Feb 4, 2024 8:35 AM UTC

I'm using circular DMA mode and using the DMA half/full complete interrupts to copy out the half of the buffer that is not in use by DMA.

There is also a key piece of information that I forgot to mention so far.

The glitching on the slave SAI sub-blocks is not consistently happening across reset (power off and on) of the device.

For example, when I power up the device, about 1 in 3 times, the glitching on the slave SAI block is present. The remaining times, the audio will be clean on all the SAI sub-blocks.

This leads me to believe the issue is with the initialization of the SAI

 

sweeraAuthor
Associate II
Feb 5, 2024 4:41 AM UTC

Just realised after reading some other posts that DBM works on top of circular mode. I wasn't aware of that, I thought they were two different modes.

Thanks, I will give that a try.

sweeraAuthor
Associate II
Feb 7, 2024 9:03 AM UTC

I switched over to using double buffer mode, but I was still seeing glitches happening on the boundary of the DMA buffer.

For example, if my DMA buffer size is 96 samples, I would see glitches at multiples of 96 samples.

However, I seem to have found a magic DMA buffer size (64 samples) which does not cause any glitching.
(Each SAI sub-block uses 2 buffers of 64 samples each)

I can't come up with an explanation for this yet, keen to hear any thoughts on this.

I have aligned my DMA buffers to start at 32-byte aligned addresses and all DMA buffers are in SRAM2, with the entire SRAM2 set to be a non-cacheable.

 

LCE
Principal III
Feb 7, 2024 11:20 AM UTC

Have you played with the FIFOs? 

I might be wrong, but I think there might be some race condition with DMA / peripheral FIFO.

Edit:

I am wrong.

I have DMA FIFO enabled, works without glitches at 4 audio channels at 32b / 400 kHz (51.2 Mbit/s), but no copying anywhere (SAI -> DMA -> SRAM -> MDMA -> HyperRAM -> DMA -> ETH)

(on a H735 discovery kit)

 

	/* Peripheral DMA init */
		hDMA_Sai_1_A.Instance 		= DMA1_Stream0;
		hDMA_Sai_1_A.Init.Request 	= DMA_REQUEST_SAI1_A;

		hDMA_Sai_1_A.Init.Direction 			= DMA_PERIPH_TO_MEMORY;
		hDMA_Sai_1_A.Init.PeriphInc 			= DMA_PINC_DISABLE;
		hDMA_Sai_1_A.Init.MemInc 				= DMA_MINC_ENABLE;
		hDMA_Sai_1_A.Init.Mode 					= DMA_CIRCULAR;
		hDMA_Sai_1_A.Init.Priority 				= DMA_PRIORITY_VERY_HIGH;	//	DMA_PRIORITY_MEDIUM; DMA_PRIORITY_HIGH; DMA_PRIORITY_VERY_HIGH;
		hDMA_Sai_1_A.Init.FIFOMode 				= DMA_FIFOMODE_ENABLE;
		hDMA_Sai_1_A.Init.FIFOThreshold 		= DMA_FIFO_THRESHOLD_FULL;
		hDMA_Sai_1_A.Init.PeriphDataAlignment 	= DMA_PDATAALIGN_WORD;
		hDMA_Sai_1_A.Init.MemDataAlignment 		= DMA_MDATAALIGN_WORD;
		hDMA_Sai_1_A.Init.MemBurst 				= SAI2S_DMA_MEMORY_BURST;
		hDMA_Sai_1_A.Init.PeriphBurst 			= SAI2S_DMA_PERIPH_BURST;

		if( HAL_DMA_Init(&hDMA_Sai_1_A) != HAL_OK ) Error_Handler_FL(__FILE__, __LINE__);

		__HAL_LINKDMA(phSai, hdmarx, hDMA_Sai_1_A);
		__HAL_LINKDMA(phSai, hdmatx, hDMA_Sai_1_A);

	/* DBM - double buffer mode activation
		- AFTER LINK to DMA!
		- BEFORE DMA enable!
	 */
		pDmaStream = (DMA_Stream_TypeDef *)hDMA_Sai_1_A.Instance;

		u32RegTemp 		= pDmaStream->CR;
		pDmaStream->CR 	&= ~DMA_SxCR_EN;
		pDmaStream->CR 	|= DMA_SxCR_DBM;
		u32RegTemp 		&= DMA_SxCR_EN;
		pDmaStream->CR 	|= u32RegTemp;
		/* CT - current buffer reset */
		pDmaStream->CR 	&= ~DMA_SxCR_CT;