adafruit / Adafruit_FreeTouch

A QTouch-compatible library
41 stars 15 forks source link

FreeTouch stalls DMA to other peripherals #17

Open mzero opened 2 years ago

mzero commented 2 years ago

FreeTouch as used in libraries

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 commented 2 years ago

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 commented 2 years ago

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 commented 1 year ago

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)