cancel
Showing results for 
Search instead for 
Did you mean: 

Audio glitches on SAI slave sub-blocks (STM32H7)

sweera
Associate II

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

7 REPLIES 7
LCE
Principal

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?

 

sweera
Associate II

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

> 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.

sweera
Associate II

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

 

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.

sweera
Associate II

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

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;