PDB -> ADC -> DMA + _int_disable = busted!

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

PDB -> ADC -> DMA + _int_disable = busted!

2,909 Views
billnd
Contributor IV

Hi all.

I have a problem, and I'm tearing my hair out over it. Just wondering if anyone has any info that might help me see the wood for the trees.

I'm running with IAR, on a K10 (50MHz) and with MQX 4.0.0.

I've got the PDB firing off every 20uS, triggering an ADC, that in turn fires off a series of DMA's. The ADC is configured so that the conversion takes 17uS.

This all works fine, and is seemingly reliably while nothing else is going on. However, once other software is running, every now and again the PDB stops with a sequence error. I think I've tracked it down to things disabling the interrupts for a period of time that is approaching 3uS. I'm guessing this is relevant as it's 20uS - 17uS.

I've removed as much software as i can, and just have a task that does _int_disable, short loop, _int_enable. As the short loop approaches 3uS in length, the problem starts to happen, as the loop length is increased the problems happens more regularly.

So I'm assuming that the problem is that the ADC completes, but (due to interrupts being disabled) before the DMA's can do their business, the PDB triggers. As the ADC has not been fully serviced by the DMA the COCO flag is still set and causes the sequence error.

Does this sound like a sensible analysis of the problem?

Presumably there is a way to priorities things so that the DMA has precedence even during an ints disabled period?

Any thoughts most welcome, thanks in advance.

Bill

Tags (5)
17 Replies

937 Views
JerryFan
NXP Employee
NXP Employee

_int_disable will disable the interrupt per ARM core perspective, while DMA will continue see the dma request once the ADC interrupt occured.

Per my understanding, ARM core should take care of the DMA interrupt instead of the ADC's. And a ping-pong way is preferred for the DMA transfer, such as, DMA tranfer 64 ADC words to the buffer A, then continue transfer to buffer B, meanwhile, araise a interrupt to ARM core to read the buffer.  

0 Kudos

937 Views
billnd
Contributor IV

CF, thanks.

What you say confirms what I originally thought. That the disabled int's should not stop the ADC requested DMA transfer. I'm just struggling to see which bit of the system is being upset by the int's being disabled.

Your example is roughly how I run the system. The PDB starts the ADC conversion repeatedly. Each time the ADC completes its conversion requests a DMA. The 1st DMA reads the RA register (to a buffer), this then links to a DMA that updates the ADC channel (ADCH). Once the buffer is full a final DMA is linked to that generates an interrupt for the system to deal with the data. Ah, now I've written that down, I wonder if I'm doing those 1st two stages in the wrong order...Should I 1st update the ADCH, and then read the RA?

As far as I understand it, once up and running, the PDB -> ADC -> DMA should not need any processor NVIC intervention, so disabling the interrupts should have no effect. What am I missing? I feel I'm being a bit thick and missing something obvious!

As before, any information would be more than welcome :-)

0 Kudos

937 Views
JerryFan
NXP Employee
NXP Employee

Hi billnd,

you meationed that  "once other software is running, every now and again the PDB stops with a sequence error". What the "PDB stops with a sequence error "?

And given that "I've got the PDB firing off every 20uS, triggering an ADC, that in turn fires off a series of DMA's. The ADC is configured so that the conversion takes 17uS", so there is only 3 us for DMA/ARM_core to reponse . For DMA, 3 us should not be a big problem, while it is too critical for ARM core. Per you post above, you used only one buffer for the DMA, not ping-pong buffer, right? And one the DMA interrupt araised, I guess you should re-config the DMA in the ISR? Please keep in mind that you only have 3 us, and the DMA isr may be preepted by other ISRs or disabled for a while by the kernel.

I am not sure, but you can have a try:

1. Could you make the conversion faster to take less time, such as 10us?

2. Using ping-pong buffer for DMA. When buffer A finished, the DMA targets to buffer B automaticlly and meanwhile araise a interrupt.


Should I 1st update the ADCH, and then read the RA?

[FCB] You can have a try, it is more reasonable. But I do think the ping-pong buffer is the final answer.

As far as I understand it, once up and running, the PDB -> ADC -> DMA should not need any processor NVIC intervention, so disabling the interrupts should have no effect.

[FCB] Yes.


0 Kudos

937 Views
billnd
Contributor IV

So a little more digging, and a little more understanding, but I'm still not clear.

I've pruned down my project so that it is now just the PDB, triggering the ADC, that in turn triggers two DMA's. The 1st DMA reads the RA data, the 2nd DMA loads ADCH with a new channel. This is exactly as detailed in AN4590, I based the idea on the app note in the 1st place.

This all works fine and dandy. I then introduce a period of time where the interrupts are disabled, Tdis. As I then increase Tdis, the ADC system stalls, the PDB reports a sequence error. Tdis only has to be a few uS before this starts to cause problems.

If I make the 2nd DMA update an address in ram, rather than ADCH, the problem goes away. Suggesting to me that the problem is occurring because the 2nd DMA is writing to the ADC later than expected, ie after the PDB has initiated the next conversion.

So by what mechanism is Tdis delaying the 2nd DMA? Anyone have any thoughts??

Cheers.

0 Kudos

937 Views
JerryFan
NXP Employee
NXP Employee

Per the RM, the sequence error occurred if the ADC be re-triggered by PDB when the conversion is in process. Are you sure the conversion is about 17us?  Could you make the conversion more faster, please?  Could you share your code, so I can check it on my side? 

0 Kudos

937 Views
billnd
Contributor IV

Sorry for the long time between posts. This problem is still confusing me, and I'm still looking for inspiration!

I've removed all the code I can, and am down to just a handful of configuration functions, and a simple foreground loop. I've managed to rule out what I originally thought was causing the problem, the _int_disable / _int_enable pairing, as I've managed to remove them and still have the problem.

The file attached is the whole of my system (although not the MQX BSP/PSP etc). In its current configuration, it runs OK, the PDB works, the ADC fires and the DMA shifts data exactly as expected. But after 4 or 5 seconds the PDP reports a sequence error, and stalls.

Interesting point number 1.

PROBLEMATIC_LOOP_VALUE, causes the sequence error to alter its appearance. If the value is made greater, ie 150, the system runs for less time before the sequence error is reported. If the value is lowered the system starts to run for a few more seconds before stalling. For example, when set to 50 the system runs for 10 or so seconds before stalling. If the value is reduced as low as 10 the system seems to run indefinitely. The time the system runs is consistent, as long as the value is maintained, it seems deterministic in that regard.

Interesting point number 2.

If the two DMA channels are unlinked, running Code B instead of Code A, so that only the 1st DMA runs. The problem seems to go away, no mater what value you make PROBLEMATIC_LOOP_VALUE.

Any suggestions of where I look next?

Cheers

Bill

0 Kudos

937 Views
yifangguo-b4310
NXP Employee
NXP Employee

Hi billnd,

  From  you expression,  " ADC system stalls, the PDB reports a sequence error. "  , your thought is "As the short loop approaches 3uS in length, the problem starts to happen, as the loop length is increased the problems happens more regularly."  and matter with the loop length.  So, you can increase PDB timer more longer  to  start  to trigger ADC(e.g. PDB  trigger ADC every 100us(not 20uS) or 1mS ) .

   Read your attached code.  From RM,  "Writing SC1A while SC1A is actively controlling a conversion aborts the current
conversion. In software trigger mode (ADTRG=0), writes to the SC1A register subsequently initiate a new conversion ". Now, you use ADC hardware trigger mode,  and  your DMA ch14  written ADC0_SC1A  may case a conversion aborts.

  In my point,  you can set , DMA ch15 move ADC0_RA to  memory array buffer_A(dma minor loop first) ,  DAM ch14 move ADC0_RB to memory array buffer_B(dma minor loop second ) ,  again DMA ch15 move ADC0_RA to  memory array buffer_A(dma minor loop 3 th), DAM ch14 move ADC0_RB to memory array buffer_B(dma minor loop 4th) . Because  when use PDB hardware to triger ADC,  you should use  DMA to  read ADC0_RA(clear SC1A coco and start ADC0_RB auto by hardware) and   ADC0_RB(clear SC1B coco and start ADC0_RA auto by hardware)   alternately  like ping-pong.

SO,  we should distinguish your problem from that 3us is a less interval  time  which another executing code  affect the PDB timer,   or  your ADC and DAM  work   in an unstable state.

0 Kudos

937 Views
billnd
Contributor IV

YG, thanks for the reply.

I've not had a chance to ping pong the DMA's like you suggest, but have managed to alter the PDB timings with some interesting results...

Bear in mind that the original PDB_MOD_VALUE value is 960 (equating to approx 20uS), and with that setting the system runs for about 4 seconds before generating the sequence error.

PDB_MOD_VALUE     system behaviour

2000                         seems stable

1700                         seems stable

1500                         seems stable

1400                         seems stable

1300                         seems stable

1200                         runs for approx 2seconds

1100                         runs for approx 1second

1000                         runs for approx 1second

965                         runs for less than 1 second

964                         runs for less than 1 second

963                         runs for approx 1second

962                         runs for approx 1second

961                         runs for approx 2seconds

960                         runs for approx 4seconds (original setting)

959                         system seems stable

958                         runs for approx 5seconds

957                         runs for approx 1second

956                         runs for less than 1 second

Not exactly sure what that tells us, but is interesting that as expected a much longer time seems to cure the problem, at least in my initial short testing period. Its perhaps more interesting that the system becomes more unstable as the value is increased from 960, but more stable at a setting of 959.

If my timing calculations had given a figure of 959 for the setting required, this problem may never have been seen!!

I'll try my best to do the ping pong test later today, but in the meantime would be happy get any more ideas.

Cheers

Bill

0 Kudos

937 Views
yifangguo-b4310
NXP Employee
NXP Employee

Hi billnd,

  I think you can confirm  does  your PDB + ADC(no DMA function ) work well  firstly.  Just Read   ADC0_RA register by " temp  = ADC0_RA " in you code but no read ADC0_RB(not  chance to ping pong  for ADC read as you mention).

   If  you  debug  "PDB + ADC" ok ,  just  read  only ADC0_RA by   variable well continuous(not  chance to ping pong  for ADC read ) , then  add DMA function , and debug.   In this situation, your DMA_TCD0_ATTR = DMA_ATTR_SMOD(2) | DMA_ATTR_SSIZE(1) | DMA_ATTR_DSIZE(1).   SMOD(2) = 2*2 ,  not SMOD(3)=2*2*2.

0 Kudos

937 Views
billnd
Contributor IV

YG, Thanks for your input.

I have tried what you have suggested. Firstly running the PDB + ADC (no DMA), reading the ADC0_RA in a tight foreground loop. This runs OK with no problems. In this mode, the PDB_MOD_VALUE can go as low as 770 before problems occur. This is expected, as it is when the PDB interval time is approximately equal to the ADC conversion time. The system will require a PDB_MOD_VALUE of 960, so there will be plenty of slack time to play with.

Secondly, as above, but with the ADC triggering one DMA channel to read ADC0_RA, no ping pong and the foreground read of ADC0_RA was removed. This again runs perfectly. In this mode, PDB_MOD_VALUE can be reduced further to 763. I'm assuming this reduction is possible due to the DMA having a more predictable/precise response time to the ADC complete signal compared to the rather hit and miss read in the foreground loop method.

Please ignore the PDB_MOD_VALUE information if you think it is not relevant. I've really only included it to indicate that the value of 960 should give plenty of response time.

I've tried other things, but am interested to see what you suggest I should try next.

Cheers

Bill

0 Kudos

937 Views
yifangguo-b4310
NXP Employee
NXP Employee

Hi billnd,

  As you say " the foreground read of ADC0_RA was removed."this action is correct . When DMA move data from SADDR(&ADC0_RA) to  DADDR(memory array) automatically,  it is the same effect as " read  ADC0_RA in foreground." It clear coco flag automatically when once ADC0_RA data moved finished.

By the way,  you'd better run your  ADC+DMA code in bareboard(no MQX OS), because OS have much other overhead(e.g. task schedule), and Application is best not to directly access hardware registers but use their drivers(e.g. DMA driver).

0 Kudos

937 Views
billnd
Contributor IV

YG,

I'm not sure I understand your last paragraph. You say run in bareboard, but then say use drivers. Could you rephrase this please.

Cheers.

0 Kudos

937 Views
yifangguo-b4310
NXP Employee
NXP Employee

Hi billnd,

  I have debug PDB+ADC+DMA in Using DMA to Emulate ADC Flexible Scan Mode too.  It is run ok. Attached is the sample codes. Maybe can have a check with yours.

0 Kudos

937 Views
yifangguo-b4310
NXP Employee
NXP Employee

By the way,  you'd better run your  ADC+DMA code in bareboard(no MQX OS), because OS have much other overhead(e.g. task schedule).    If you use MQX os, the MQX Application is not better to access hardware registers directly, but use their drivers(e.g. DMA driver).

0 Kudos

937 Views
billnd
Contributor IV

Just a quick update. I've now created a new project, without using MQX, just based on the default IAR project. The problems still occur, but at least this removes MQX from the equation entirely. Progress indeed.

937 Views
yifangguo-b4310
NXP Employee
NXP Employee

Hi billnd,

  I have worked PDB + ADC + DMA  modules well.   PDB timer trigger ADC1,  ADC1 coco tigger DAM ch0 in my K70 towersystem board. My code work in bareboard (NO MQX OS) ,but the configuration of PDB , ADC1 and DMA may can  help to you understand the work theory and have a reference.

  DMA Source Address is ADC1_RA, First, the frist ADC1 request from  ADC1_RA result completion,  DMA move    ADC1_RA to adc1_result[0], and second,  ADC1 request from  ADC1_RB result completion,  DMA move ADC1_RB(SADD+ SOFF) to  adc1_result[1], and so on,  so,  adc1_result[index] save ADC1_RA when  index even,   adc1_result[index] save ADC1_RB when  index odd. There is only one DMA channel 0 to  response the  request from  coco of ADC1_SC1A and ADC1_SC1B alternately(when CITER even, DMA response request from ADC1_SC1A coco;  CITER odd, DMA response request from ADC1_SC1B coco ).

  Because ADC1  use  hardware trigger modes of operation(PDB trigger),  "The SC1n registers have identical fields, and are used in a "ping-pong" approach to control ADC operation"  in chapter "39.3.1 ADC status and control registers 1 (ADCx_SC1n)"  of RM K70.    So,  have to read ADC1_RA and ADC1_RB alternately.

  Here,  Attached is my  code,  hope can help you.

0 Kudos

937 Views
billnd
Contributor IV

It is an assumption that the system stops working because of a sequence error. All I know is that once the ADC subsystem stops, I pause the CPU with the debugger and the sequence error bit is set. It only ever shows as being set when the ADC is stalled.

I am running with ping pong buffers. Say two buffers of 128 ADC samples each. ADC converts, triggers a DMA to copy data from RA to buffer A, and update ADCH. once buffer A is full, it switches to fill buffer B, and links to another DMA that ends by involving an interrupt that uses the data in buffer A.

So the only point CPU response time should be an issue is the interrupt that analyses buffer A. It has time till buffer B is full, which should be plenty.

This is what really confuses me, the whole PDB, ADC, DMA system should run with no influences from the CPU interrupt status, right up until buffer is ready to be analysed. Perhaps I need to go back to basics, strip it down to individual components, and add things back in till it breaks.

Sorry for brief msg, made on a phone.

0 Kudos