Or you can try to use external DMA triggers (wired to the timer output) (I never tried) they are only level sensitive.
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.
Hi There,
Question:
Why does my DMA and Timer lose synchronization when the ADC ISR is enabled?
Information:
I did some work to achieve the behavior I wanted but have recently experienced a problem which has challenged my understanding. Let me explain. I am using the MSP430F5438A Rev F.
I have a parallel external flash IC and want to minimize the CPU and Flash power consumption by using DMA to transfer the data from the MSP430 to the external flash IC.
As the MSP430 does not provide an external transfer strobe (a line to show when the output has been updated by the DMA), I use a timer, sourced from MCLK, 8MHz, with two compare channels. One compare channel triggers the DMA to take a byte from RAM and place it on the parallel port (PxOUT). It is in repeated single transfer mode. After a few more clock edges the second compare channel is set. This timer output is routed out of the msp430 to the flash write pin using the PxSEL register. The delay between is to allow for the DMA to update the port. My settings are:
TA0CCR0 = 20; 21 edges in all TA0CCR3 = 19;//WE -> msp430 output TA0CCR2 = 1;//DMA Trigger
P8SEL |= 1<< WE; //output TA0CC3 on pin 8.3
This works well in isolation i.e. with the CPU not doing anything else.
The Issue
I also need to capture ADC data to fill the RAM buffer, which is then written to the flash when full. One method I have tried is to use the ADC ISR triggered by a different Timer and sourced from ACLK (which is Timer B). What I find is that occasionally the data gets corrupted, which I suspect is a loss in synchronization between the DMA and the Timer. The question is why is this happening?
What I have tried/thought:
I thought a repeated-single transfer takes 5 MCLK cycles + latency (i.e. 2MCLK cycles + 2 to synchronize + wait). I have 18 clocks between DMA trigger and the strobe. Also I thought that the DMA can interrupt the ISR - so the ISR shouldn't be hogging the clock. So what is causing this loss in synchronization? - what is causing the DMA to take longer than expected? I need to use repeated-single transfer to allow the ADC ISR to complete to maintain sampling.
I have tried to use the ADC with DMA and I believe this works. But I am worried as I don't understand the above scenario that I might have a hidden issue.
As I said, this has really knocked my confidence and I am concerned. Any suggestions/thoughts/discussion would really appreciated.
Hi Leo,
Thanks for your time looking at my problem.
Just to confirm, you are suggesting that if the CPU is in the ADC ISR and the timer compare channel for the DMA is set i.e. the edge to trigger the DMA this can be missed?
To clarify, I am not using a DMA ISR for every byte transfer - just when its completed the stream.
If this is true, then it might explain the reason that the DMA ADC based stuff works... and currently as my ADC ISR is large it has a good chance of occurring vs. the generally shorter ISR if all prefer/when I use DMA ADC.
How could I avoid this? Purely being making the ISR as short as possible?
Thanks again!
James Hillman said:How could I avoid this?
Don’t use DMA. It’s bringing not much sometimes even less.
James Hillman said:Purely being making the ISR as short as possible?
Still danger.
Hi Leo,
I have heard people say this about DMA but I don't understand the comment. For me, I can reduce the transaction (the flash write time) from 200ms to 10ms which saves power on the msp430 and the external flash module. The savings have been significant.
In reality, how fast code could hand optimized code implement a ram read and pin toggle vs. the few MCLK cycles of DMA?
Thanks for your input,
James
James Hillman said:which saves power on the msp430
Is the only reason to use DMA.
You must make it so that the trigger never comes during servicing an ISR, but that’s difficult. There is no way to recover the missing trigger other than a complete reset.
Or you can try to use external DMA triggers (wired to the timer output) (I never tried) they are only level sensitive.
Leo Bosch said:DMA is triggered by an edge-sensitive, rising level. When the DMA misses the trigger it will not start anymore. This can happen when the trigger comes during servicing another ISR, for example ADC ISR.
Honestly - I did read multiple times but did not get it.
Please explain how it can be that DMA looks for trigger, in this case CCIFG, at specific time (?) and if CPU is busy with ISR processing then DMA completely miss CCIFG and never looks again? If it could be so then it's not DMA actually. Am I missing something here?
If you copy ADC results from ADC to RAM, you use the ADC as DMA trigger. This is independently of the timer triggering the ADC.James Hillman said:I suspect is a loss in synchronization between the DMA and the Timer.
plus any startup time for the MCLK source to come up. Which is 6µs for DCO, if the SVS is in performance mode, and much, much longer if not.James Hillman said:I thought a repeated-single transfer takes 5 MCLK cycles + latency (i.e. 2MCLK cycles + 2 to synchronize + wait).
Yes. ISR execution is simple CPU operation, and DMA halts CPU operation (if RMWDIS is not set, even in the middle of an read/write instruction) to access the memory bus and do the transfer.James Hillman said:Also I thought that the DMA can interrupt the ISR
Any LPM deeper than LPM0.James Hillman said:what is causing the DMA to take longer than expected?
The DMA trigger is checked by a hardware wire from the module IFG bit (or its internal substitute in case of a more complex IFG structure) to the DMA controller. CPU activity has no influence.Leo Bosch said:DMA is triggered by an edge-sensitive, rising level. When the DMA misses the trigger it will not start anymore. This can happen when the trigger comes during servicing another ISR, for example ADC ISR.
I used DMA with SPI (512 byte burst transfer from/to SD-card) as well as with DAC (to generate a waveform from a memory table). Found no issues yet. But there's a known erratum with DMA and eUSCI peripheral.
Is the only reason to use DMA.James Hillman said:which saves power on the msp430
Also, DMA is the only way, to have a DAC output with 2 MHz update frequency (sufficient fine shaped waveform for 100kHz output frequency) when you run on 4MHz MCLK (1x family).
But yes, power saving is one big reason for DMA. Especially if you switch MCLK to XT1 (32kHz, already left running due to slow crystal startup or alarm job) and the CPU can continue sleeping while the DMA does the transfer sufficiently fast.
Depending on the peripheral, clearing and setting the IFG flag will instantly (re)start the DMA transfer.Leo Bosch said:There is no way to recover the missing trigger other than a complete reset.
External triggers can be both, edge and level sensitive. Only the internal triggers are said to not working with level sensitive mode. Maybe they still do, but show glitches. While maybe solving the glitch that seems to happen here.Leo Bosch said:Or you can try to use external DMA triggers (wired to the timer output) (I never tried) they are only level sensitive.
Hi Everyone for your interesting replies!
Jens-Michael Gross said:ADC results from ADC to RAM, you use the ADC as DMA trigger. This is independently of the timer triggering the ADC.
I am not sure I understand. In the setup discussed above I have two timers, A and B.
Timer B (Sampling Rate) -> ADC ISR (writes to RAM, when full, sets flag for main loop to write to flash - which uses a DMA2 and Timer B)
Timer A (with two compare channels) -> Triggers DMA 2 -> some time later toggles CCRx output -> resets
Therefore I am not using the ADC as the DMA trigger.
My setup relies on ISRs during the DMA transfer - if this is risky as is seems, then I need to change my architecture. One option I think would work is to use two buffers feed by DMA which stops ISRs firing during the DMA flash write i.e.
Timer B (sampling rate) -> ADC
ADC -> DMA0 (trigger = ADC done) -> RAM Ping
DMA0 ISR (buffer full) -> Triggers DMA1 to transfer RAM Ping to RAM Pong
DMA1 ISR (transfer done) -> Set main flag to setup flash write (which uses DMA2)
now I have a nice longer window of no ISRs, as DMA2 is faster than DMA0 (and turn off all other ISRs except DMA)
This is the setup I have used in the passed and believed to have worked well (though all of this has made me doubt).
However, I recently started to experiment with a larger flash device which required a buffer of 8192 bytes. As I use the MSP430F5438A which has 16kB I can't use two buffers. Therefore, I used only one buffer which preemptively triggered the main flag to write to flag whilst DMA0 was reconfigured to complete the transfer into RAM. This worked, except for the issue above,where I get a DMA 0 ISR once is reaches the end of the RAM buffer whilst DMA2 is transferring to flash. The above scenario, is made worse further, as I was using the ADC ISR also. But I have found, that even using ADC DMA I still get - what we are suggesting - is missed triggers. Does this sound correct?
Please correct me if I a wrong but I think the following options:
1. Look at one of the MSP430 5 series which has more RAM.
2. Experiment with the external DMA trigger - as suggested by Leo. However, the manual says "
When DMALEVEL = 1, transfer modes selected when DMADT = {0, 1, 2, 3} are recommended because
the DMAEN bit is automatically reset after the configured transfer." Will this be a problem for me if I use the mode 4 (repeated single transfer) as I do currently. I just need the DMA ISR to fire when DMAxSZ and then I will stop the DMA2 as soon as I can - I don't mind if it overruns.
Or you can try to use external DMA triggers (wired to the timer output) (I never tried) they are only level sensitive.
3. Use a SPI-based flash. This is not preferred as the capacities are lower (and in my testing they consumed more power, if I have enough RAM!)
I would like to discuss the use of DMAE0 in more depth - how can I use it?
So far I have,
1. Place first byte on the port
2. Start Timer
3. Make a CCR set the WE pin high on the first clock edge (flash write strobe)
4. Make a second CCR set the DMAE0 high to trigger DMA to transfer next byte from RAM to the Port. As the DMAE0 is an enable pin - the DMA gets to work.
5. CCR0 resets the time once enough clock edges have been to allow the DMA to complete its single-repeated transfer.
How long should I give the DMA to complete a transfer? I will wait for the DMA ISR in lpm0 so 5 clocks seem right to you guys?
My question - what would the settings?
TAx.CCR0 = 2 + 5 (DMA Clks?) -1 (because I get a free edge due to timer reset)
Tax.CCRx (DMAE0) = 2 (probably could be a one)
Tax.CCRy (write) = 1
Thanks for your help.
James Hillman said:DMA0 ISR (buffer full) -> Triggers DMA1 to transfer RAM Ping to RAM Pong
DMA1 ISR (transfer done) -> Set main flag to setup flash write (which uses DMA2)
DMA0 can trigger DMA1 without ISR.
Also for DMA1 -> DMA2 but probably you need some setup ISR and then it becomes tricky, if now ADC is ready the DMA trigger will be lost and end of game.
James Hillman said:2. Experiment with the external DMA trigger - as suggested by Leo. However, the manual says "
When DMALEVEL = 1, transfer modes selected when DMADT = {0, 1, 2, 3} are recommended because
the DMAEN bit is automatically reset after the configured transfer." Will this be a problem for me if I use the mode 4 (repeated single transfer) as I do currently. I just need the DMA ISR to fire when DMAxSZ and then I will stop the DMA2 as soon as I can - I don't mind if it overruns.
This was just an idea I don’t have experience with it, you have to check it.
Hi everyone,
I have now convinced myself that the ISR is the cause of my DMA issue. However, the discuss so far has leaned towards the idea of the DMA missing a trigger during an ISR firing. The impact of this is that the data on the parallel bus does not change and it would be repeated into the flash. However, what I actually see is that I lose a byte - you will see below that I should have received two 0x00 bytes. To me, this is either caused by the DMA starting at the wrong address, the dma triggering correctly but the flash didn't see the WE or that the dma falsely triggered. Thoughts?
e.g. expected 0x00, 0x02, 0x04 (sent 00, 00, 02, 00, 04, 00) and I get ( 00, 02, 00, 04, 00 06) which is 0x0200, 0x0400, 0x0600.
This happens once every few writes.
James Hillman said:Therefore I am not using the ADC as the DMA trigger.
Sorry, I misunderstood your setup.
Well, as I wrote, CPU execution, whether in main or inside ISR, is interrupted by DMA the same way. The CPU has no "I'm in ISR" mode where it behaves differently from any other code where GIE is disabled.
The only moment where it doesn't follow a normal CPU operation is the initial ISR latency, when it reads the vector and prepares for running the ISR. I don't know whether a DMA at this moment might cause problems. But it would be surprising.
You may try setting the RMWDIS flag.
In a different thread, someone noticed that accessing the UCA0 registers during an UCB0 DMA transfer causes the DMA to deliver corrupt data. This issue hasn't been solved yet.
Well, DMA seems to be a bit more sensitive than it should.
For your situation, what do you think of this setup:
Timer triggers ADC. ADC results are moved by DMA0 to RAM in repeated single transfer mode, triggered by ADC.
DMA0 triggers DMA1 when the buffer has been filled. DMA1 moves a config byte to enable the transfer trigger mechanism for DMA2 (e.g. start the timer).
DMA2 ISR resets the transfer trigger and re-arms DMA2 while DMA0 and DMA1 in repeated modes are already armed.
Only one ISR that has nothing to do than to disable the trigger for DMA2 and rearm DMA2. And it is not time critical.
However, this setup only works if you're using just one ADC channel, as DMA0 cannot loop the source address by 16 and the destination by 8k.
James Hillman said:"When DMALEVEL = 1, transfer modes selected when DMADT = {0, 1, 2, 3} are recommended because the DMAEN bit is automatically reset after the configured transfer " Will this be a problem for me if I use the mode 4 (repeated single transfer) as I do currently
The problem is that with DMALEVEL=1, Transfers will be done with maximum speed as long as the trigger is set. That means there is only a small time window in which the external hardware can detect the write and clear the trigger, or you'll be sending with MCLK/2 Hz.
With internal peripherals, this might work. However, DMALEVEL=1 is only recommended for external trigger DMAE0, not for peripherals. Maybe exactly for this reason:
"For proper operation, level-sensitive triggers can only be used when external trigger DMAE0 is selected as the trigger. DMA transfers are triggered as long as the trigger signal is high and the DMAEN bit remains set."
Likely, the DMA might have started the next transfer already, when the peripheral notices the transfer and resets its IFG bit. Especially if the trigger comes from the destination (e.g. TXBUF).
The MSP430F5659 has 64k ram. And 2k USB. Or the MSP430F5359 with 66k (no USB controller)James Hillman said:1. Look at one of the MSP430 5 series which has more RAM.
Interesting detail: 16 of the 64k are mapped to low memory as well as high memory. So you could use up to 48k for buffering, using 20 bit DMA addressing, while your program only uses small data model with 18k low ram.
Jens-Michael Gross said:For your situation, what do you think of this setup:
Timer triggers ADC. ADC results are moved by DMA0 to RAM in repeated single transfer mode, triggered by ADC.
DMA0 triggers DMA1 when the buffer has been filled. DMA1 moves a config byte to enable the transfer trigger mechanism for DMA2 (e.g. start the timer).
DMA2 ISR resets the transfer trigger and re-arms DMA2 while DMA0 and DMA1 in repeated modes are already armed.
This is almost the current scenario!
Jens-Michael Gross said:Only one ISR that has nothing to do than to disable the trigger for DMA2 and rearm DMA2. And it is not time critical.
What you don’t want to understand is; Triggering the DMA directly from another peripheral and also using, any, ISR will lock the DMA! James has, as I believe, slowly on recognized this issue now.
The DMA has an edge-sensitive trigger, which means (in this case) a low to high transition triggers the DMA, when this happens during servicing an ISR the transition will not been seen by the DMA. If any difference, setting the RMWDIS bit will make it only more worse.
Hi Leo and Jens,
Thanks for spending your energy on my problems!
Leo Bosch said:What you don’t want to understand is; Triggering the DMA directly from another peripheral and also using, any, ISR will lock the DMA! James has, as I believe, slowly on recognized this issue now.
Leo - I have come to believe this is the case. But why? Is this TI official? How did you come to know of this? I feel it is rather big flaw - do you agree?
I personally feel I must be doing something wrong... my most recent idea has been to move my time critical code out of main and split it across the appropriate ISRs - as its only once every few page writes which suggests some variation in timings. Would you happen to agree with this statement?
Something like this:
DMA0 ISR = ADC has reached the pre-filled point in the RAM BUFFER. Reconfigure DMA0ISR for remaining bytes. Also, start the write send within this ISR which ultimately enables DMA2. *This way I remove the latency of the main loop. However, I might miss a sample!
DMA0 ISR fires again, the buffer has been filled. Reconfigure. *okay, so this might 'lock up' the DMA but now some of the jitter has been removed, it might, just might, be always working.
DMA 2 ISR fires - DMA write complete, wrap up flash write.
James Hillman said:Is this TI official? How did you come to know of this?
Same as you; Spending a lot of time on it. I have a product with a stressed MCU with a few time critical ISR’s, and in-between the ADC data needs to be saved and tries to do that with DMA, no way! Searching TI site and try’s to understand how DMA works learns me not to use DMA in such a cases. And have to deal with some missing ADC data.
In your case I would try:
Timer starts ADC.
ADC ISR stores data to the right place, like your DMA0 supposed to do. Check if DMA1 = ready and trigger DMA2 if needed. Count the stored ADC data and if complete trigger DMA1, exit ISR.
Hi Leo,
1.Could you please re-explain your suggestion - I am generally confused...
Timer B -> ADC -> ADC ISR -> RAM BUFFER.
ADC ISR -> RAM BUFFER FULL -> ????
2. Your experience
Do you think this issue could cause an extra trigger too? I am not seeing a missed trigger but the DMA seemingly doing a transfer before the first trigger?
Many thanks,
Let’s first confirm what exactly has to be done (maybe I’m lost):
You want to read, let’s say 5 ADC-channels, store them to RAM buffer. This 100 times so you have 500 readings and copy these 500 to a flash device and start over again. Is this correct?
Leo Bosch said:This is almost the current scenario!
Almost. The key difference is that there is only one ISR and it is triggered when the buffer has been sent. With lots of time. It could as well be polled by main without timing problems.
However, thinking again about the whole topic, there is another possible explanation. It has nothing to do with ISR or not. It could be that accessing a peripheral during a DMA transfer causes problems. Any peripheral, perhaps. In case of peripherals with waitstates (e.g. hardware multiplier), this is a know erratum. Not really coincidentally, chance that a peripheral register is accessed during DMA is much higher in an ISR because usually ISRs are called to handle some peripheral event.
Hi Jens, Leo and others!
I have been busy and found the root cause. I had not configured my sampling timer (Timer B, which triggers the ADC) correctly: as I had intended. It was configured to trigger the DMA and to call the ISR. The code went from this:
TB0CCR0 = 1; //roll over TB0CCR1 = 1; //trigger adc TB0CCTL1 = OUTMOD_3+ CCIE; // CCR1 set/reset mode TB0CTL = TBSSEL_1+TBCLR + MC_1+ TBIE; // ACLK, Up-Mode
to this:
TB0CCR0 = 1; //roll over TB0CCR1 = 1; //trigger adc TB0CCTL1 = OUTMOD_3;// CCR1 set/reset mode TB0CTL = TBSSEL_1+TBCLR + MC_1;//ACLK, Up-Mode
This ISR was implemented - but I found even if I removed code from it, it still corrupted my parallel port writes which are DMA based and synchronized to a timer output (for the write strobe).
However, why did this ISR firing (on every sample) corrupt my DMA?
The suggestion thus far is interrupts - however I have produced various test harnesses and found that the DMA did not corrupt the data when the msp430 was loaded with the same ISR demands. Only, when I configured the timer to trigger the ADC AND call ISR. I believe this setup to be invalid - is that right? What are your thoughts on the impact of doing such a thing?
Thanks,
TB0CCR0=1? Quite tight (only two timer ticks per cycle).
However...
In another recent thread, it was suspected that accessing USCI registers (USCIA0) during an ongoing DMA transfer (USCB0!) was corrupting the DMA transfer. Dropping the access to the USCIA0 registers made the DMA working again.
But I don't know why this could happen. I could imagine that on modules with read waitstates (such as the MPY) a DMA transfer while the CPU is in a waitstate could cause problems (and in fact, I think I remember an erratum about this). But for normal periperals... this would render DMA quite useless.
Especially if the DMA source/target and the module accessed inside the ISR (or main code - I don't see a difference here between the two) are not related.
The only thing I could imagine is that a DMA being triggered during an ISR entry sequence could cuse problems. This again would make DMA rather useless.
Since you're the second one now with a similar problem, this issue should be investigated further, perhaps form 'official' side.
It shouldn't be a problem to use the timer as trigger for ADC and a timer ISR simultaneously. However, the ADC runs on faster clock than the CPU by default. It may be that the ADC is ready with the conversion before the ISR init sequence (triggered at the same time) is done. And the DMA happens while the CPU is still in the proccess of entering the ISR. Possibly the CPU is not stopped by DMA in this case, and a bus collision happens.
What happens when you pick a longer ADC12SHT time or a slower ADC12CLK?
**Attention** This is a public forum