Open mzero opened 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 :)
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...
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)
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:
Both
CONVERT
andRESULT
appear to be Read Synchronized registers. (Note: thesync_config
call does not implement the read request sequence and does not sync reads.)In the the Circuit Playground version, this code is:
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 withREADREQ
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.