2024-01-31 06:03 PM - edited 2024-02-07 01:42 AM
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
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
2024-02-01 03:41 AM
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?
2024-02-01 04:43 PM
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.
2024-02-02 02:49 AM
> 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.
2024-02-04 12:35 AM
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
2024-02-04 08:41 PM
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.
2024-02-07 01:03 AM
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.
2024-02-07 03:20 AM - edited 2024-02-07 03:21 AM
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;