SAMD21 DMAC weird behaviour

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm using multiple DMA channels in an Atmel SAMD21G18A (in an Arduino MKR1000), and I'm seeing some obviously incorrect behaviour.

DMA channels 2 & 3 are servicing a SERCOM SPI channel, using single block transfers with a completion interrupt. DMA channel 0 continuously writes a sequence of values to the DAC, triggered by a TCC channel. It uses a single descriptor for the transfer, of a 64-word block of values, and which chains to itself in order to run continuously at the rate determined by TCC (10us per beat, but changing this doesn't prevent the problem).

The problem I'm seeing is that after running for a variable amount of time, usually anywhere between 5 seconds to a couple of minutes, channel 0 stops - indicating no error, just TCMPL (transfer complete). If I enable the TCMPL interrupt, the interrupt is issued when the channel stops - but not before, while it's happily repeating. Wiggling GPIOs in the DMAC ISR shows that the failure always occurs just after one of the SPI DMA channels has completed a block transfer.

When I examine the descriptor writeback array, I find that the channel data for channel 0 has been written with the exact (completed) channel data for either channel 2 or channel 3. None of the surrounding descriptors are affected - and the same problem occurs if I assign different DMA channels.

I'm aware of item 1.7.2 in the Silicon Errata, but I'm applying the specified workaround by ensuring that the channel number of the new channel enabled (i.e. channel 2 or 3) is greater than the other channel numbers (channel 0).

I assume I'm doing something else wrong - I suspect the self-chaining has something to do with it.  I wonder if anybody has come across a similar issue?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi jeremy,

 

I am probably too late for you, sorry. But maybe for others:
Do you always enable the lower DMAC-Channels before the higher channels?
In the errata sheet there is an DMAC-error stated (1.7.2), when enabling a lower channel after a higher channel.

Workaround:
When enabling a DMA channel while other channels using linked descriptors are already active, the channel number
of the new channel enabled must be greater than the other channel numbers
.

Kaum macht man's richtig - und schon geht's!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for responding.  I've worked around the problem in another way, but I'm still hoping to solve the mystery.

 

Unfortunately I've already addressed that erratum (as per my final paragraph).  Of course it never hurts to double- and triple-check these things, which I will do.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jeremy0 wrote:
I've worked around the problem in another way

Would you care to share the alternative workaround - for the benefit of others who may have the same problem?

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi jeremy0,

jeremy0 wrote:

I've worked around the problem in another way, but I'm still hoping to solve the mystery.

 

 

 

As I am about to try a very similar task, I would like to know your workaround.
I have one DMAC-channel to write 500 kByte/sec. to Master-SPI in a loop (self-linking descriptor),
and I have to read from Slave-SPI with another DMAC-channel.
So I am afraid to stumble into the same problem.

Kaum macht man's richtig - und schon geht's!

Last Edited: Tue. Jul 28, 2020 - 11:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Sure, I've successfully implemented two alternatives, both using a one-shot (non-linked) block descriptor:
a) Use the DMA completion interrupt to re-enable the transfer every time it completes.
b) Use a timer interrupt to periodically re-enable the transfer.

For my specific purposes I'm using the second approach, with a 1ms period.
Obviously depending what else is going on in the system, in both cases the interrupt load can start to become significant.

 

Note that in both cases there's a pause between each block transfer (brief in (a), longer in (b)), whereas my original goal was to have a perfectly continuous transfer stream - but my use case is flexible enough for this not to be a problem for me.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi jeremy0,

 

did you try to raise the Quality of Service for the DMAC?

DMAC->QOSCTRL.bit.DQOS: 3x MEDIUM / HIGH.
Because this determines the DMAC access proirity to RAM / BUS Matrix(?) versa the CPU.

And did you try to have different Channel Arbitration Levels for the different channels?
DMAC->CHCTRLB.bit.LVL= level;
Maybe it helps to have a different level on the different channels.

I will try to get 2 DMAC-channels working, one with 187kBytes/s and the other with 500kBytes/s, both with self-linked descriptors.
I need a seamless transmission definitely. And to cope the DMAC behaviour with an interrupt will be too processor-power consuming for to me too.
I will report on my experience, as soon as I have checked this out.

Kaum macht man's richtig - und schon geht's!