Post Go back to editing

SC589 delayed first SPORT DMA Data Interrupt

Hi,

Abstract:

The very first call to our SPORT DMA data interrupt handler is delayed.
A couple of DMA buffers obviously already have been completed after startup (SPORT and DMA enable) before the interrupt handler will be called the first time. 

Setup:

we are using both Half SPORTs of a Full SPORT on SC589 with 1D DMA in Descriptor List Mode.
The Full SPORT used is SPORT4 on DAI1, one Half SPORT is for transmitting and one for receiving.
Both channels Primary and Secondary are used on both Half SPORTs.
Both Half SPORTs are externally clocked with the same clocks.

We operate with the very same 3 buffers for TX and RX:
E.g. while TX DMA operates on Buffer 1of3, RX DMA operates on Buffer 3of3 and so on... 
Thus with no core interaction received data from RX will be looped back to TX - which works perfectly.
(No DMA Errors, No SPORT Errors, verified by comparing input to output data externally)

We later installed interrupt handler and enabled XCNT Interrupt on the DMA channel belonging to the RX Half SPORT.
This works as well - the handler will be called.

Problem in detail:

The very first call to our interrupt handler (after enabling DMAs and SPORTs) arrives unpredictably delayed.
While we would expect the very first buffer (first descriptor) to be completed while our handler is called the first time this is not true - it might be any one of the three available buffers.
We verify this by checking the corresponding DMA ADDRSTART register from within our interrupt handler.

All calls to our handler later on are in sync (regarding buffer order) and in time.

We can hear this effect as well:
Within the interrupt handler we swap received data for channels within the buffers (left <-> right).
Right after starting we hear TX data is 'as is' RX data for a short period of time.
Then the channel flip gets active: TX left is RX right.
It means: there is a delay before our interrupt handler will be used.
At the beginning a couple of buffers will be passed from RX to TX without the interrupt handler being called.

Do you have any idea how to fix this ?

Parents
  • Hi,

    1) Are you missing any data during receive operation?
    2) Can you share the code snippet for DMA configuration along with buffer's used?
    3) How are you conforming that DMA interrupt is delayed? Can you share the screen shot for the interrupts?
    4) Are you using PCG?  Are you using stereo modes? What is the frame sync rate and bit clock rate?

    Regards,
    Anand Selvaraj.

  • Hi Anand, thanks for your reply.

    1)

    No, I do not miss data - DMA is working.
    Maybe the word 'delayed' is bit misleading here.
    What I experience is: even though data transfer via DMA works perfectly from the very beginning, my DMA RX interrupt handler will not be called for the first n RX DMA buffer completions after start, while n is not constant.
    So the very first time my interrupt handler gets called after start it can make no assumption which of the 3 buffers is safely accessable for processing.

    2)

    - snippet of my DMA configuration

    	// -----------------------------------------
        // --- TX DMA ------------------------------
    	
    	int DMA_TX_Config=(		(0						<< BITP_DMA_CFG_WNR  ) |
    	   						(0 						<< BITP_DMA_CFG_TWOD ) |
    	   						(0 						<< BITP_DMA_CFG_INT  ) |
    							ENUM_DMA_CFG_SYNC 	 |
    							ENUM_DMA_CFG_MSIZE04  |
    							ENUM_DMA_CFG_PSIZE04  |
    	   						(0	 					<< BITP_DMA_CFG_SYNC ) |
    	   						(0						<< BITP_DMA_CFG_TWAIT) |
    	   						(0						<< BITP_DMA_CFG_TRIG ) |
    							ENUM_DMA_CFG_DSCLIST |		// flow mode: descriptor list
    							ENUM_DMA_CFG_FETCH02 ); 	// fetch 2 elements: 'next desc' and 'start address'
    	// create descriptor chains, loop chain start with chain end
    	// buffers are placed in normal Word memory , so multiply by 4 and OR MP_OFFSET to get system address
    	for (idesc = 0; idesc < TRANSFER_NUM_BUFFERS; idesc++)
    	{
    		TX_DESCS[idesc].Next_Desc	= (uint32_t)&TX_DESCS[(idesc + 1) % TRANSFER_NUM_BUFFERS] | MP_OFFSET;
    		TX_DESCS[idesc].Start_ADDR	= (uint32_t)p_tx_transfer_buffers[idesc] * 4 | MP_OFFSET;
    	}
    
    	// set descriptor next pointer to descriptor list start
    	// descriptor list is placed in Byte word memory, so OR MP_OFFSET with buffer address to get system address
    	*pREG_DMA10_DSCPTR_NXT = (void*)((uint32_t)&TX_DESCS[0] | MP_OFFSET);
    	// number of samples per DMA buffer
    	*pREG_DMA10_XCNT = buffer_size_samples;
    	// memory address increment after each sample (is same as size of sample here)
    	*pREG_DMA10_XMOD = sample_size_bytes;
    	// set config
    	*pREG_DMA10_CFG = DMA_TX_Config;
    	
    	// -----------------------------------------
        // --- RX DMA ------------------------------
    
    	int DMA_RX_Config=(		(1						<< BITP_DMA_CFG_WNR  ) |
    							(0 						<< BITP_DMA_CFG_TWOD ) |
    							ENUM_DMA_CFG_SYNC 		|
    							ENUM_DMA_CFG_XCNT_INT	|
    							ENUM_DMA_CFG_MSIZE04 	|
    							ENUM_DMA_CFG_PSIZE04 	|
    							(0	 					<< BITP_DMA_CFG_SYNC ) |
    							(0						<< BITP_DMA_CFG_TWAIT) |
    							(0						<< BITP_DMA_CFG_TRIG )
    							| ENUM_DMA_CFG_DSCLIST		// flow mode: descriptor list
    							| ENUM_DMA_CFG_FETCH02 ); 	// fetch 2 elements: 'next desc' and 'start address'
    	// create descriptor chains, loop chain start with chain end
    	// buffers are placed in normal Word memory , so multiply by 4 and OR MP_OFFSET to get system address
    	for (int idesc = 0; idesc < TRANSFER_NUM_BUFFERS; idesc++)
    	{
    		RX_DESCS[idesc].Next_Desc	= (uint32_t)&RX_DESCS[(idesc + 1) % TRANSFER_NUM_BUFFERS] | MP_OFFSET;
    		RX_DESCS[idesc].Start_ADDR	= (uint32_t)p_rx_transfer_buffers[idesc] * 4 | MP_OFFSET;
    	}
    
    	// set descriptor next pointer to descriptor list start
    	// descriptor list is placed in Byte word memory, so OR MP_OFFSET with buffer address to get system address
    	*pREG_DMA11_DSCPTR_NXT = (void*)((uint32_t)&RX_DESCS[0] | MP_OFFSET);
    	// number of samples per DMA buffer
    	*pREG_DMA11_XCNT = buffer_size_samples;
    	// memory address increment after each sample (is same as size of sample here)
    	*pREG_DMA11_XMOD = sample_size_bytes;
    	
    	// set config
    	*pREG_DMA11_CFG = DMA_RX_Config;
    	
    	if (ADI_INT_SUCCESS != adi_int_InstallHandler(INTR_SPORT4_B_DMA_ERR, board_irqhandler_dma_rx_error, NULL, true))
    	{
    		return false;
    	}
    	if (ADI_INT_SUCCESS != adi_int_InstallHandler(INTR_SPORT4_B_DMA, board_irqhandler_dma_rx_data, NULL, true))
    	{
    		return false;
    	}

    - TX_DESC and RX_DESC are globals

    #define TRANSFER_NUM_BUFFERS 3
    
    SPORT_DMA_DESC TX_DESCS[TRANSFER_NUM_BUFFERS];
    
    SPORT_DMA_DESC RX_DESCS[TRANSFER_NUM_BUFFERS];


    - memory for buffers will be given from the outside, there are 3 buffers in total which will be given to DMA descs in different orders fro TX and RX

    section("seg_dmda_nw")
    static int32_t en0_xfer_0[32 * 4];
    
    section("seg_dmda_nw")
    static int32_t en0_xfer_1[32 * 4];
    
    section("seg_dmda_nw")
    static int32_t en0_xfer_2[32 * 4];
    
    .tx_buffers			=
    {
    	en0_xfer_0,
    	en0_xfer_1,
    	en0_xfer_2
    },
    
    .rx_buffers			=
    {
    	en0_xfer_2,
    	en0_xfer_0,
    	en0_xfer_1
    },

    3) 

    1st by listening:
    For as long as my RX DMA buffer completion interrupt handler will not be called the received audio data will be transmitted 'AS IS' (this is because TX DMA and RX DMA operate on the same memory buffers). If my RX DMA buffer completion interrupt handler will be called it alters audio data.
    If I start ,I hear the first couple of buffers played back are not altered - and after that I hear the audio is altered.

    2nd by debugging:
    With the very first time my RX buffer completion interrupt handler gets called I do read pREG_DMA11_ADDRSTART and realize RX DMA operates on another buffer as expected.
    If repeating this (reset, start again, catch first call to handler) I realize it's changing - I can make no assumption about it.
    At the moment I have implemented a work around for this problem: the very first time my interrupt handler gets called I do read the register to synchronize my audio processing with DMA engines:

    RX DMA             --->   BUFFER_2
    AUDIO PROCESSING   <-->   BUFFER_1
    TX DMA             <---   BUFFER_0
    
    // RX DMA writes fresh data
    // AUDIO PROCESSING reads the RX samples and writes processed ones (exchange)
    // TX DMA reads the processed samples

    4) 

    No I do not use PCG, all clocks are externally generated by another hardware.
    No I do not use stereo mode, but multichannel. I use both primary and secondary channels on both of the used Half SPORTs.
    Frame sync rate is 48kHz, bit clock rate is 3.072MHz.
    I experienced this on the ADZS-SC589-EZLITE and the ADZS-SC589-MINI.

    Regards,

    David

  • Hi David,

    1) How are you conforming that the interrupt is delayed or not getting for the first time and interrupt occurring from second time? Can you share the scope images?

    2) Can you try by increasing the buffer size?

    Regards,
    Anand Selvaraj.

  • Hi Anand,

    The interrupt itself most probably is not delayed - as already said DMA is working perfectly from the very beginning !


    It is the call of my interrupt handler which happens not with the very first RX buffer completions after start but after -n- number of DMA buffers initially being completed (while -n- may vary).
    Called once, the handler will be called for every subsequent buffer completed afterwards.

    I conform this by reading pREG_DMA11_ADDRSTART from within my handler and by listening:
    If my handler will be called it will alter the audio data - if not the audio data will be played back as is.

    What I hear is - right after start (SPORT & DMA) for short period I here audio played back as is.
    And after this short period played back audio is altered.
    This leads me to the following conclusions:

    1. DMA RX & DMA TX are working fine because DMA TX is playing back audio received via DMA RX.

    2. For the short period right after start my interrupt handler will not be called because it would alter the audio which will be played back but the played back audio is the same as it has been received.

    3. After this short period the played back audio is constantly altered. Which means now may interrupt handler will be called for all subsequent buffer completions.

    I do have no scope pictures of the DMA buffer completions.

    Increasing buffer size did not change this. 

    Regards,
    David

Reply
  • Hi Anand,

    The interrupt itself most probably is not delayed - as already said DMA is working perfectly from the very beginning !


    It is the call of my interrupt handler which happens not with the very first RX buffer completions after start but after -n- number of DMA buffers initially being completed (while -n- may vary).
    Called once, the handler will be called for every subsequent buffer completed afterwards.

    I conform this by reading pREG_DMA11_ADDRSTART from within my handler and by listening:
    If my handler will be called it will alter the audio data - if not the audio data will be played back as is.

    What I hear is - right after start (SPORT & DMA) for short period I here audio played back as is.
    And after this short period played back audio is altered.
    This leads me to the following conclusions:

    1. DMA RX & DMA TX are working fine because DMA TX is playing back audio received via DMA RX.

    2. For the short period right after start my interrupt handler will not be called because it would alter the audio which will be played back but the played back audio is the same as it has been received.

    3. After this short period the played back audio is constantly altered. Which means now may interrupt handler will be called for all subsequent buffer completions.

    I do have no scope pictures of the DMA buffer completions.

    Increasing buffer size did not change this. 

    Regards,
    David

Children