Forums

C5509A : timer/DMA/interrupt problem

Started by Andreas Weishaupt April 7, 2011
Hi all,

I'd like to do periodic DMA transmission from DARAM to EMIF at 1kHz
with the C5509A. Therefore I use a timer and I perform a DMA_start
inside of the timer's ISR. When I look at the signals at the EMIF, it
seems that very often the DMA doesn't start and that this happens very
irregularly. Just to complete the picture: if i symbolize successful
transmissions with a T and unsuccessful ones with a X, I get a random
pattern e.g. like T T X T T T T T T X T X T X T T T X T X T T T T X
etc. with the T's and X's "spaced at 1kHz" (i.e. if I have and X,
there's no output).

If I set a breakpoint in the timer ISR, I see a signal every time I
continue. I thought it first was due to scope settings but I'm pretty
sure that the settings are adapted.

Is there something am I missing or I didn't configure correctly? Here
are some snippets from my code :

void main (void) {
volatile Bool first = TRUE;
c5509a_dspclk_init(12,144,0); // PLL
c5509a_intc_init(); // CSL/interrupts
c5509a_emif_init(); // EBSR+EMIF
c5509a_dma_init(); // DMA
c5509a_timer_init(); // Timer

while (1) {
if (first) {
first = FALSE;
TIMER_start(mhTimer0); // DMA element-by-element transfer
is sync to timer0 => 8MHz
TIMER_start(mhTimer1); // DMA_start is sync to timer1 => 1kHz
}
}
}

void c5509a_intc_init() {
/* Initialize CSL library - This is REQUIRED !!! */
CSL_init();
/* Set IVPD/IVPH to start of interrupt vector table */
IRQ_setVecs((Uint32)(&RESET_VEC));
}

void c5509a_dma_init(Uint16 inclk, Uint16 outclk, Uint16 plldiv)
{
// DMA configuration ... => DMA interrupts are turned off
/* Open DMA channels 4 & 5 and set regs to power on defaults */
hDmaRcv = DMA_open(DMA_CHA4,DMA_OPEN_RESET);
hDmaXmt = DMA_open(DMA_CHA5,DMA_OPEN_RESET);
// Get interrupt event associated with DMA receive and transmit
xmtEventId = DMA_getEventId(hDmaXmt);
rcvEventId = DMA_getEventId(hDmaRcv);
// Temporarily disable interrupts and clear any pending interrupts
old_intm = IRQ_globalDisable();
// Clear any pending interrupts for DMA channels
IRQ_clear(xmtEventId);
IRQ_clear(rcvEventId);
// Enable DMA interrupt in IER register
IRQ_enable(xmtEventId);
IRQ_enable(rcvEventId);
// Place DMA interrupt service addresses at associate vector
IRQ_plug(xmtEventId,&dmaXmtIsr);
IRQ_plug(rcvEventId,&dmaRcvIsr);
// Write values from configuration structure to DMA control regs
DMA_config(hDmaRcv,&dmaRcvConfig);
DMA_config(hDmaXmt,&dmaXmtConfig);
// Restore status of global interrupt enable flag
IRQ_globalRestore(old_intm);
}

void c5509a_timer_init() {
/* Open Timer 0, set registers to power on defaults */
mhTimer0 = TIMER_open(TIMER_DEV0, TIMER_OPEN_RESET);
/* Write configuration structure values to Timer control regs */
TIMER_config(mhTimer0, &timer0_cfg);
/* Open Timer 1, set registers to power on defaults */
mhTimer1 = TIMER_open(TIMER_DEV1, TIMER_OPEN_RESET);
/* Write configuration structure values to Timer control regs */
TIMER_config(mhTimer1, &timer1_cfg);
/* Get Event Id associated with Timer 0/1, for use with */
/* CSL interrupt enable functions. */
tim0EventId = TIMER_getEventId(mhTimer0);
tim1EventId = TIMER_getEventId(mhTimer1);
/* Temporarily disable interrupts and clear any pending interrupts */
old_intm = IRQ_globalDisable();
/* Clear any pending Timer interrupts */
IRQ_clear(tim0EventId);
IRQ_clear(tim1EventId);
/* Place interrupt service routine address at */
/* associated vector location */
IRQ_plug(tim0EventId,&timer0Isr);
IRQ_plug(tim1EventId,&timer1Isr);
/* Enable Timer interrupt */
//IRQ_enable(timEventId);
IRQ_enable(tim1EventId);
/* Restore status of global interrupt enable flag */
IRQ_globalRestore(old_intm);
IRQ_globalEnable();
}

interrupt void timer1Isr(void) {
// restart DMA
DMA_start(hDmaXmt);
}

For testing purpose I only transmit 8 bytes per DMA transfer, so this
should be done in far less than 1 ms!
Nothing more than that. I don't see where the problem is.

I appreciate every help on this.

Best regards,

Andreas
>I'd like to do periodic DMA transmission from DARAM to EMIF at 1kHz
>with the C5509A. Therefore I use a timer and I perform a DMA_start
>inside of the timer's ISR. When I look at the signals at the EMIF, it
>seems that very often the DMA doesn't start and that this happens very
>irregularly. Just to complete the picture: if i symbolize successful
>transmissions with a T and unsuccessful ones with a X, I get a random
>pattern e.g. like T T X T T T T T T X T X T X T T T X T X T T T T X
>etc. with the T's and X's "spaced at 1kHz" (i.e. if I have and X,
>there's no output).
>
>If I set a breakpoint in the timer ISR, I see a signal every time I
>continue. I thought it first was due to scope settings but I'm pretty
>sure that the settings are adapted.
>
>Is there something am I missing or I didn't configure correctly? Here
>are some snippets from my code :
>For testing purpose I only transmit 8 bytes per DMA transfer, so this
>should be done in far less than 1 ms!
>Nothing more than that. I don't see where the problem is.

My apologies, but I do not have time to pore through all 86 lines of code, so I'll just offer generic advice.

In general, I make it a habit to never call API from within an ISR. You should generally set some sort of flag and return from the ISR, then catch the flag in your main() loop and execute the global changes from outside the ISR.
However, upon reading your opening paragraph again, I think you have things set up incorrectly.

When using the Timer to control the DMA, you should not need a Timer ISR at all. Instead, program the DMA to take its sync directly from the Timer without requiring any code at all. This leaves lots more DSP cycles for your main() code, and uses the absolute minimum cycles to accomplish your DMA task. You will want a DMA ISR, I presume.

So, to reiterate, you start by setting up your DMA before enabling the Timer. If the DMA is properly set up to trigger from the Timer, then you should actually start your overall process by calling Timer_start() after DMA_start().

Note that you only need to call DMA_start() once, and it should transfer all 8 bytes. At the end of 8 bytes, the DMA channel will stop unless you have it set to auto-reload.

One problem is that I don't know whether you want a burst of 8 bytes, with 1000 transfers per second, or if you want the 8 bytes to be sent at 1 kHz (equivalent to a sample rate of 1 kHz). If the former, then I think you could probably keep your existing code and just remove DMA_start() from the Timer ISR, replacing it with code to flag the main() loop to handle this instead.

Anyway, I am not 100% certain that you cannot call DMA_start() from an ISR, but every thing I have ever tried from within an ISR fails unless all I do is interact with the specific registers for the peripheral that is responsible for generating the ISR. By using semaphores in my code, I have been able to get a complex combination of 2 Timers, 4 DMA channels, 3 McBSP ports, the XF and USB all operating at the same time. This leads me to stand behind my recommendation to avoid calling complex API from within an ISR.

Brian Willoughby
Sound Consulting