This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DMA McBSP transmit sync events MIA



I've finally decided to throw this question to the big guns here on the e2e because I'm stumped.

My scenario: McBSP configured in transmit mode with CLKX/FSX coming from external source.  The McBSP data should be fed by the DMA controller, synchronized with XEVT events from the McBSP.  I am having difficulties getting the McBSP XEVT and thus difficulty getting the DMA controller to feed the McBSP data.

I am fairly confident of my McBSP and DMA configurations, but I will post them here for reference (in the 2 following posts to improve readability).

When I request XEVT as the sync event, I get one DMA interrupt as soon as I start the McBSP then never get a single additional one.  My method for starting the DMA and McBSP follow (note the initial read from DMACSR, as noted in this e2e post from a user having a very similar problem, however with the receive McBSP: http://e2e.ti.com/support/dsp/c5000/f/109/t/47776.aspx):

  • My DMA Configuration:

    /* Open the DMA channel and save off the handle. */
    dac->hDmaTx = DMA_open( TX, DMA_OPEN_RESET );
    
    /* Check for valid. */
    if( (NULL == dac->hDmaTx) || (INV == dac->hDmaTx) )
    {
        dac->hDmaTx = 0;
        return -ENO_DMA;
    }
    
    /*
     * Setup the CSDP register
     *      This is the source and destination register, and controls
     *      which memory banks are accessed by the DMA engine, as well
     *      as the data widths involved and bursting.
     */
    dac->dmaCfg.dmacsdp = \
        DMA_DMACSDP_RMK(
        	/* No bursting to destination */
        	DMA_DMACSDP_DSTBEN_NOBURST,
        	/* No packing at destination. */
    		DMA_DMACSDP_DSTPACK_OFF,
    		/* Destination is peripheral (McBSP). */
    		DMA_DMACSDP_DST_PERIPH,
    		/* No bursting from source */
    		DMA_DMACSDP_SRCBEN_NOBURST,
    		/* Use packing at source since our data is 16-bit and the EMIF (source/SDRAM) has a 32-bit data bus */
    		DMA_DMACSDP_SRCPACK_ON,
    		/* Source is EMIF (SDRAM) */
    		DMA_DMACSDP_SRC_EMIF,
    		/* Data is 16-bit */
    		DMA_DMACSDP_DATATYPE_16BIT );
    
    /*
     * Set up the CCR register
     *      The CCR register controls how the addressing occurs on both
     *      the source and destination addresses.  It also controls the
     *      autoinitialization behavior, or auto-repeat.
     */
    dac->dmaCfg.dmaccr = \
        DMA_DMACCR_RMK(
        	/* Destination address is constant (McBSP peripheral) */
        	DMA_DMACCR_DSTAMODE_CONST,
    		/* Source address should be incremented post-transfer */
    		DMA_DMACCR_SRCAMODE_POSTINC,
    		/* Changing configuration (block length) after each block transfer */
    		DMA_DMACCR_ENDPROG_ON,
    		/* Repeat only if ENDPROG == 1 */
    		DMA_DMACCR_REPEAT_ENDPROG1,
    		/* Autoninit on. */
            DMA_DMACCR_AUTOINIT_ON,
            /* DMA channel enable */
            DMA_DMACCR_EN_START,
            /* Transmitting our samples is the highest priority */
            DMA_DMACCR_PRIO_HI,
            /* Sync is an element transfer */
            DMA_DMACCR_FS_ELEMENT,
            /* Sync on McBSP1 Transmit Events (p.28 of 5510 datasheet) */
            6 );
    
    /*
     * Set up the CICR register
     *      Configure control of what interrupts are generated.  We're
     *      interested in frame interrupts.  We also want to know if there
     *      is ever a dropped sample, so we've turned on drop IE as well.
     */
    dac->dmaCfg.dmacicr = \
        DMA_DMACICR_RMK( 
        	/* DMA block interrupts */
        	DMA_DMACICR_BLOCKIE_ON,
        	/* No last frame interrupts */
    		DMA_DMACICR_LASTIE_OFF,
    		/* Frame interrupts on */
    		DMA_DMACICR_FRAMEIE_ON,
    		/* No half-frame interrupts */
    		DMA_DMACICR_FIRSTHALFIE_OFF,
    		/* Interrupts for dropped events */
    		DMA_DMACICR_DROPIE_ON,
    		/* Timeout interrupts on. */
    		DMA_DMACICR_TIMEOUTIE_ON );
    
    dac->dmaCfg.dmacen = DMA_DMACEN_RMK(625);
    dac->dmaCfg.dmacfn = DMA_DMACFN_RMK(50);
    
    /* Multiply by 2 since start_read_addr is a word address, and dmacssal should be a BYTE address */
    dac->dmaCfg.dmacssal = (DMA_AdrPtr) (((UINT32)(dac->start_read_addr) << 1) & 0xFFFF); 
    /* Top 16 bits multiplied by 2 to turn it into a BYTE address */
    dac->dmaCfg.dmacssau = (((UINT32)(dac->start_read_addr) >> 15) & 0xFFFF);
    
    /* Destination is the McBSP peripheral */
    dac->dmaCfg.dmacdsal = (DMA_AdrPtr)((Uint16)(MCBSP_ADDRH(dac->serial.hMcbsp, DXR1) << 1));
    dac->dmaCfg.dmacdsau = (Uint16) 0x0000;
    
    /*
     * Now that all of the registers are setup, we'll configure the DMA
     * and actually write them all in.  At this point, we'll have a fully
     * ready to run DMA engine that should be ready to accept a "start"
     * call and just go.
     */
    DMA_config( dac->hDmaTx, &dac->dmaCfg );
    
    DMA_RSET( DMAGCR, DMA_DMAGCR_RMK(   
    	/* Free run of DMA on breakpoint */
    	DMA_DMAGCR_FREE_OFF,
    	/* HPI doesn't get exclusive use of internal RAM */
        DMA_DMAGCR_EHPIEXCL_OFF,
        /* HPI is lower priority */
        DMA_DMAGCR_EHPIPRIO_LOW ) );
    
    /* Turn off enhanced mode indexing */
    DMA_RSET( DMAGSCR, DMA_DMAGSCR_RMK( DMA_DMAGSCR_COMPMODE_ON ) );	/* Compatibility mode */
    
    /* Disable timeouts on the DARAM and SARAM ports */
    DMA_RSET( DMAGTCR, DMA_DMAGTCR_RMK( DMA_DMAGTCR_DTCE_LOW,
                                        DMA_DMAGTCR_STCE_LOW ) );
    
    /* Save off the event id. */
    dac->uiDmaEvtId = DMA_getEventId( dac->hDmaTx );
    
    /* Map the event for DSP BIOS */
    IRQ_map( dac->uiDmaEvtId );
    
    /* Mask ourselves only */
    attrs.arg = (Arg)dac;
    attrs.ier0mask = 0x0;
    attrs.ier1mask = 0x0; /* Self */
    
    /* Send it to the HWI dispatcher */
    HWI_dispatchPlug( dac->uiDmaEvtId, (Fxn)DAC_DMA_isr, &attrs );

  • My McBSP Configuration:

    dac->serial.sConfig.spcr1 = \
        MCBSP_SPCR1_RMK(
    		/* Digital Loop Back off */
    		MCBSP_SPCR1_DLB_OFF,
    		/* Right Justify and fill with zeros */
    		MCBSP_SPCR1_RJUST_RZF,
    		/* No clockstop */
    		MCBSP_SPCR1_CLKSTP_DISABLE,
    		/* DX delay off */
    		MCBSP_SPCR1_DXENA_OFF,
    		/* Set to 0 */
    		MCBSP_SPCR1_ABIS_DISABLE,
    		/* Doesn't matter - not receiving */
    		MCBSP_SPCR1_RINTM_RRDY,
    		/* No Frame Sync Errors */
    		MCBSP_SPCR1_RSYNCERR_NO,
    		/* Doesn't matter - not receiving */
    		MCBSP_SPCR1_RRST_DISABLE 
    	);
    
    dac->serial.sConfig.spcr2 = \
        MCBSP_SPCR2_RMK(    
    		/* Free run on breakpoint */
    		MCBSP_SPCR2_FREE_YES,   
    		/* Doesn't matter */
            MCBSP_SPCR2_SOFT_NO,
            /* Don't enable it yet - will get set by call to McBSP_start (SRGR_FRAMESYNC) */
            MCBSP_SPCR2_FRST_RESET,
            /* Don't enable it yet - will get set by call to McBSP_start (SRGR_START) */
            MCBSP_SPCR2_GRST_RESET,
            /* send transmit interrupt request when XRDY changes from 0 to 1 */
            MCBSP_SPCR2_XINTM_XRDY,
            /* No frame sync error on transmit */
            MCBSP_SPCR2_XSYNCERR_NO,
            /* Don't enable it yet - will get set by call to McBSP_start (XMIT_START) */
            MCBSP_SPCR2_XRST_DISABLE 
    	);
    
    dac->serial.sConfig.xcr1 = \
    	MCBSP_XCR1_RMK( 
    		/* Single phase with 1 word / phase */
    		MCBSP_XCR1_XFRLEN1_OF(0),
    		/* 16-bit word length */
    		MCBSP_XCR1_XWDLEN1_16BIT
    	);
    
    dac->serial.sConfig.xcr2 = \
    	MCBSP_XCR2_RMK( 
    		/* 1-bit data delay to facilitate continuous transmission (refer to 2-10 of the McBSP Reference Guide (spru592e)) */
    		MCBSP_XCR2_XDATDLY_1BIT,
    		/* Ignore unexpected frame-sync pulse */
    		MCBSP_XCR2_XFIG_NO,
    		/* No companding */
    		MCBSP_XCR2_XCOMPAND_MSB,
    		/* Doesn't matter since we're doing 1 phase transfers */
    		MCBSP_XCR2_XWDLEN2_16BIT,
    		/* Doesn't matter */
    		MCBSP_XCR2_XFRLEN2_OF(0),
    		/* Do single phase transfers */
    		MCBSP_XCR2_XPHASE_SINGLE
    	);
    
    /* Doesn't matter...we're doing transmit */						
    dac->serial.sConfig.rcr1 = dac->serial.sConfig.xcr1;
    /* Doesn't amtter...we're doing transmit */
    dac->serial.sConfig.rcr2 = dac->serial.sConfig.xcr2;
    
    dac->serial.sConfig.srgr1 = \
    	MCBSP_SRGR1_RMK( 
    		/* Doesn't matter since frame-sync pulse comes from external source */
    		MCBSP_SRGR1_FWID_OF(0),
    		/* GLKGDV of 0 for CLKR inputs.  No divide (doesn't matter since our clock source is CLKX) */
            MCBSP_SRGR1_CLKGDV_OF(0) 
    	);
    
    dac->serial.sConfig.srgr2 = \
        MCBSP_SRGR2_RMK( 
    		/* Doesn't matter. "GSYNC is used only when the input clock source for the SRG is external on the CLKS or CLKR pin." */
    		MCBSP_SRGR2_GSYNC_SYNC,
    		/* Doesn't matter since CLKS is not the input clock source */
    		MCBSP_SRGR2_CLKSP_RISING,
    		/* DOES MATTER.  We receive the clocks on CLKX */
    		MCBSP_SRGR2_CLKSM_INTERNAL,
    		/* Doesn't matter since FSXM = 0 */
    		MCBSP_SRGR2_FSGM_DXR2XSR,
    		/* 17 clocks/frame sync (16 data bit + 1 don't care) - 1 = 16 */
    		MCBSP_SRGR2_FPER_OF(DAC_FSYNC_LEN) 
    	);
    
    dac->serial.sConfig.pcr = \
    	MCBSP_PCR_RMK(  
    		/* McBSP remains active when PERIPH domain is idled */
    		MCBSP_PCR_IDLEEN_RESET,
    		/* TX Pins are serial port pins (not GPIO) */
            MCBSP_PCR_XIOEN_SP,
            /* RX Pins are serial port pins (not GPIO) */
            MCBSP_PCR_RIOEN_SP,
            /* External Frame Sync Generation */
            MCBSP_PCR_FSXM_EXTERNAL,
            /* Doesn't matter */
            MCBSP_PCR_FSRM_INTERNAL,
            /* Bit Clock is an input */
            MCBSP_PCR_CLKXM_INPUT,
            /* Doesn't matter */
            MCBSP_PCR_CLKRM_OUTPUT,
            /* External CLKX is the source of the clock */
            MCBSP_PCR_SCLKME_BCLK,
            /* Doesn't matter.  Only used for GPIO. */
            MCBSP_PCR_DXSTAT_1,
            /* TX active low frame sync */
            MCBSP_PCR_FSXP_ACTIVELOW,
            /* Doesn't matter */
            MCBSP_PCR_FSRP_ACTIVEHIGH,
            /* TX on falling edge */
            MCBSP_PCR_CLKXP_FALLING,
            /* Doesn't matter */
            MCBSP_PCR_CLKRP_FALLING 
    	);
    
    /* Open our McBSP port. */
    dac->serial.hMcbsp = MCBSP_open( AERO_TX, MCBSP_OPEN_RESET );
    
    /* Make sure we got a valid port */
    if( (INV == dac->serial.hMcbsp) || (NULL == dac->serial.hMcbsp) ){
        dac->serial.hMcbsp = 0;
        return -ENO_MCBSP;
    }
    
    /* Save off the McBSP event ID number */
    dac->serial.intnum = MCBSP_getRcvEventId( dac->serial.hMcbsp );
    
    /* Configure our port */
    MCBSP_config( dac->serial.hMcbsp, &dac->serial.sConfig );
    
    /* Map the event for DSP BIOS */
    IRQ_map( dac->serial.intnum );
    
    /* Set the HWI attributes to only mask ourself in the interrupt */
    attrs.arg = (Arg)dac->serial.hMcbsp;
    attrs.ier0mask = 0x0;
    attrs.ier1mask = 0x0; /* Self */
    
    /* Post it to the HWI dispatcher */
    HWI_dispatchPlug( dac->serial.intnum, (Fxn)MCBSP_isr, &attrs );

  • Note in the DMA configuration, the channel control register (DMACCR) has '6' for the sync event (the McBSP XEVT as specified in the 5510 datasheet).  If I specify '0' (i.e. allow the DMA to rearm itself), I do get my DMA interrupts as expected.

  • This is how I start the McBSP and DMA:

            /* Clear DMA Control and Status to get things rolling */
            i = DMA_RGETH( dac->hDmaTx, DMACSR );
    
            /* Globally disable interrupts while we setup new ones (briefly) */
            hwi_state = HWI_disable();
    
            /* Clear any pending interrupts */
            IRQ_clear( dac->uiDmaEvtId );
    
            /* Enable the interrupt associated with DMA1/DAC1 */
            IRQ_enable( dac->uiDmaEvtId );
    
            /* Clear any pending McBSP interrupts */
            IRQ_clear( dac->serial.intnum );
    
            /* Enable the McBSP interrupt */
            IRQ_enable( dac->serial.intnum );
    
            /* Enable all of them. */
            HWI_restore( hwi_state );
    
            /* Start the DMA engine */
            DMA_start( dac->hDmaTx );
    
    	MCBSP_start(dac->serial.hMcbsp,
                        MCBSP_XMIT_START |
                        MCBSP_SRGR_START |
                        MCBSP_SRGR_FRAMESYNC,
                        625);
    

  • Note that in the function which starts the DMA and McBSP, there's a read of the DMACSR, as suggested in this forum post from a user with a very similar problem: http://e2e.ti.com/support/dsp/c5000/f/109/t/47776.aspx

  • That user also suggests a dummy read from the McBSP after configuration and prior to starting the peripherals.  I've tried a dummy write to the McBSP in a task which runs after configuration prior to peripheral start without success:

        MCBSP_write16( dac->serial.hMcbsp, 0xAAAA );
    

  • Hi,

    We are working on and we will provide you an update.

    Thanks & regards,

    Sivaraj K

  • Thanks, Sivaraj.  I've made some progress, but I'm still having difficulties with something.  Note that in my McBSP initialization, specifically in setting up the McBSP interrupt (not the DMA interrupt), I have the line

    dac->serial.intnum = MCBSP_getRcvEventId(dac->serial.hMcbsp);

    which should actually be using: MCBSP_getXmtEventId(~).  (Copy and paste bug from my receive section...)

    This allowed me to get a McBSP interrupt to ensure that I was indeed seeing frame sync signals inside the chip as well as ensure that XRDY was toggling; both are true.  That change, however, did not fix the problem of no XEVT events to the DMA controller (I didn't expect it to - it did help narrow things down a bit).  (No XSYNCERR errors which might prevent the XEVT events.)

    In a background task, I have a semaphore which waits for initialization/configuration of the various peripherals on my board to be complete; after which I do a dummy write to the McBSP (as detailed above).  I must have been doing the write too early or too late before, because now I am starting to see regular XEVT events.  (Though why I have to do a dummy write is still a mystery to me.)

    However...

    My DMA requires two separate configurations which switch back and forth on block boundaries.  My first DMA block consists of 625 words and 50 frames.  My second DMA block consists of 625 words and 52 frames.  These ping pong back and forth ad infinitum.  Upon receiving the first frame interrupt within each block, I reconfigure the DMA controller with the appropriate (next) block configuration.  I've ensured that the reconfiguration is taking place (i.e. waiting on ENDPROG before reconfiguring, as described in the DMA Reference Guide).

    During the very first block (of 50 frames), I receive all the XEVT events I expect.  Once the new block (of 52 frames) starts, I stop receiving XEVT events.  Though I note that I'm still receiving McBSP interrupts (i.e. XRDY still toggling and FSX still going).

    I don't know how to ensure that my DMA controller properly auto-reconfigured itself (i.e. that the new configuration registers made it to the working registers at the end of the first block), as I'm only able to access the configuration registers and not the current working registers in the DMA controller.  The code which does the reconfiguration is almost identical to the initial configuration (note that the dma->dmaCfg object still holds all the configuration registers from initialization):

    int update_dmacfg(dac_dma_t *dma)
    {
    	uint32_t dmacfn, dmaccr;
    	uint16_t new_frame_cnt;
    	INT16 *dma_next_block_start_addr;
    
    	dmaccr = (uint32_t)DMA_RGETH( dma->hDmaTx, DMACCR); /* current channel control register */
    	if((dmaccr >> 11) & 0x1)
    	{
    		/*
    		 * ENDPROG == 1, so the DMA controller has not yet finished transferring config registers to working registers....
    		 * Exit without clearing b_update_dmacfg, and try again later (i.e. next DMA 'frame' interrupt).
    		 */
    		return EDAC_DMA_STILL_CONFIGURING;
    	}
    
    	dmacfn = (uint32_t)DMA_RGETH( dma->hDmaTx, DMACFN ); /* current DMA 'frame'/'block' count...we'll toggle it between 50 and 52 */
    
    	new_frame_cnt = 50 + (1 - dma->which_half) * 2;
        dma->dmaCfg.dmacfn = DMA_DMACFN_RMK(new_frame_cnt); /* 50 / 52 / 50 / 52 ... */
    
        dma_next_block_start_addr = dma->dma_block_start_addr + (dmacfn * DAC_FRAME_SIZE_SAMPLES );
        if((uint32_t)(dma_next_block_start_addr) > dma->burst_map_end)
        {
        	dma_next_block_start_addr = dma->start_read_addr;
        }
        dma->dma_next_block_start_addr = dma_next_block_start_addr;
    
        /* Multiply by 2 since start_read_addr is a word address, and dmacssal should be a BYTE address */
        dma->dmaCfg.dmacssal = (DMA_AdrPtr) (((UINT32)(dma_next_block_start_addr) << 1) & 0xFFFF);
    
        /* Top 16 bits multiplied by 2 to turn it into a BYTE address */
        dma->dmaCfg.dmacssau = (((UINT32)(dma_next_block_start_addr) >> 15) & 0xFFFF);
    
        /* Set the ENDPROG bit back to 1 to indicate done programming */
        dma->dmaCfg.dmaccr = \
            DMA_DMACCR_RMK(
            		/* Destination address is constant (McBSP peripheral) */
            		DMA_DMACCR_DSTAMODE_CONST,
            		/* Source address should be incremented post-transfer */
    				DMA_DMACCR_SRCAMODE_POSTINC,
    				/* Changing context after each block transfer */
    				DMA_DMACCR_ENDPROG_ON,
    				/* Repeat only if ENDPROG == 1 */
    				DMA_DMACCR_REPEAT_ENDPROG1,
    				/* Autoninit on. */
    				DMA_DMACCR_AUTOINIT_ON,
    				/* DMA channel enable */
    				DMA_DMACCR_EN_START,
    				/* Transmitting our samples is the highest priority */
    				DMA_DMACCR_PRIO_HI,
    				/* Sync is an element transfer */
    				DMA_DMACCR_FS_ELEMENT,
    				/* Sync on McBSP1 Transmit Events (p.28 of 5510 datasheet) */
    				6 );
    
        DMA_config( dma->hDmaTx, &dma->dmaCfg );
    
    	dma->b_update_dmacfg = 0;
    
    	return EDAC_DMA_NO_ERROR;
    }
    

    Again, thanks in advance for your help!

  • Okay, a little more information...

    If I forget about reconfiguring the DMA controller, and I set DMA_DMACCR_ENDPROG_OFF and DMA_DMACCR_REPEAT_ALWAYS (so basically, just repeat the initial configuration over and over), I get all XEVT events, but *only* after I do a restart of the DSP through JTAG.

    Steps to reproduce:

    1.) Start DSP (through JTAG) with a breakpoint in the DMA block interrupt portion (indicated below with /* BREAKPOINT */) - application doesn't break.

    2.) Pause JTAG, Restart (using the restart button in CCS Debug perspective), with same breakpoint - application breaks over and over.

    So it seems I get my XEVT events only after JTAG Restarting.  My very simple interrupt handler is below:

    void DAC_DMA_isr( void *param )
    {
        volatile int dmacsr_read;    			/* Used to force a read of DMACSR */
        dac_dma_t *dma = (dac_dma_t *) param;	/* Our only arg is an DAC pointer.*/
    
        dmacsr_read = DMA_RGETH( dma->hDmaTx, DMACSR );
    
       	/* Timeout Interrupt */
        if(dmacsr_read & 0x1)
        {
        	TRACE(&trace, "A DMA timeout interrupt has occurred.");
        }
        
       	/* Dropped event interrupt */
        if((dmacsr_read >> 1) & 0x1)
        {
        	TRACE(&trace, "A DMA dropped element interrupt has occurred.");
        }
    
    	/* Frame Interrupt */
        if((dmacsr_read >> 3) & 0x1)
        {
        	dma->last_read_loc += LOCATIONS_PER_DAC_DMA_FRAME;
        }
    
    	/* Block Interrupt */
        if((dmacsr_read >> 5) & 0x1)
        {
        	/* BREAKPOINT */
    	    dma->which_half = 1 - dma->which_half;
        }
    
        /*
         * Kick off SWI here to perform additional tasking
         */
        SWI_inc(&SWI_task);
    
        return;
    }

  • It seems that the above method for getting all XEVT events (Run, Pause, JTAG Restart, Run) works even with the DMA reconfiguration.

    So why does behavior vary after a JTAG Restart?

  • Hi,

      The same is being addressed in the below forum post.

       http://e2e.ti.com/support/dsp/c5000/f/109/p/361235/1269686.aspx#1269686

    Regards

     Vasanth