Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FreeTouch stalls DMA to other peripherals #17

Open
mzero opened this issue Dec 26, 2021 · 3 comments
Open

FreeTouch stalls DMA to other peripherals #17

mzero opened this issue Dec 26, 2021 · 3 comments

Comments

@mzero
Copy link

mzero commented Dec 26, 2021

FreeTouch as used in libraries

  • Adafruit_Circuit_Playground 1.11.4
  • Adafruit_FreeTouch 1.1.1
  • Arduino board: Circuit Playground Express, or any SAM D21 board
  • Arduino IDE version: 1.8.13

Summary

During a measurement, FreeTouch makes many reads to some Read-Synchronized registers in the PTC, and does so without the synchronizing read-request sequence. Because the PTC is clocked slower than the system clock, these reads cause a significant stall (~185µs each) of the Peripheral Bridge C, stalling all access to the other peripherals on the same bridge.

If DMA is trying access any of those peripherals (like the DAC or ADC) - then it will be stalled. At ~185µs stall, this is enough to lose several samples at audio rates, causing significant distortion.

Discussion

This bridge stall can be induced with any peripheral on the C bridge that has a Read Synchronized register. To induce the stall:
a) the peripheral needs to be clocked with a clock slower than GCLK0
b) make the register read without first performing the READREQ sequence

Note that if the peripheral is clocked via GLCK0 - as most are by default - then a read to a Read Synchronized register made without the READREQ sequence will still stall the bridge, but it will be for a very short time (docs. imply <8 clocks). This won't materially affect DMA audio.

However, the PTC must be clocked at 4MHz, and this is achieved by setting GCLK1 to use the 8MHz clock source and dividing down. FreeTouch sets this up. And then FreeTouch makes reads that stall the bridge.

Demonstration

This sketch demonstrates the issue on a Circuit Playground Express: https://gist.github.com/mzero/89955e14d41d7e37a439ba806746f632

The sketch uses DMA drive audio out to the DAC "in the background", only using CPU during DMA interrupts to compute the next buffer of samples.

In the foreground (in loop()) the sketch can call FreeTouch ever 50ms to demonstrate the issue. It can also demonstrate the issue by directly accessing the PTC, or reading TC3 configured to cause the issue. The distortion is plainly audible.

FreeTouch and PTC

The main issue is in the code:

bool adafruit_ptc_is_conversion_finished(Ptc *module_inst) {
  return module_inst->CONVCTRL.bit.CONVERT == 0;
}

uint16_t adafruit_ptc_get_conversion_result(Ptc *module_inst) {
  sync_config(module_inst);
  return module_inst->RESULT.reg;
}

Both CONVERT and RESULT appear to be Read Synchronized registers. (Note: the sync_config call does not implement the read request sequence and does not sync reads.)

In the the Circuit Playground version, this code is:

uint16_t Adafruit_CPlay_FreeTouch::startPtcAcquire(void) {
  ptcConfigIOpin();

  ptcAcquire();

  while (QTOUCH_PTC->CONVCONTROL.bit.CONVERT) {
    yield();
  }

  sync_config();
  uint16_t result = QTOUCH_PTC->RESULT.reg;

  return result;
}

This makes the issue plainly bad: The CONVERT read is done in a spin loop... causing stalls lasting over 8ms during which only a fraction of DMA operations get in.

It is clear from the code that the READREQ register for the PTC hasn't been sleuthed out yet. There are two other SAM D21 modules with READREQ functionality (RTC & TC timers) and one imagines that PTC's is similarly constructed in layout and operation. Some more reverse engineering of the PTC is in order.

@ladyada
Copy link
Member

ladyada commented Dec 26, 2021

thanks for the detailed writeup! more reverse engineering would be a great project for someone to undertake :) if someone is up for it, we are happy to take pull requests as we have no plans to revisit this code soon!
one could also change the structure of the code to add a longer delay/yield or add a callback. many options :)

@mzero
Copy link
Author

mzero commented Dec 26, 2021

I did a preliminary hunt for where that READREQ register might be... Alas I don't have a set up to observe QTouch's bus access patterns that might give us clue. I'll keep poking...

@combs
Copy link

combs commented Oct 6, 2022

is this the same as the SYNCBUSY flag described in https://github.com/jgilbert20/Libre_PTC#enabling-the-ptc---our-first-register-access ?

(forgive me if this is unrelated, as I am not very familiar with this device)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants