-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
rp2/machine_timer: Support hard IRQ timer callbacks. #17363
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
Conversation
Code size report:
|
An enthusiastic +1 here. |
Looks like a good addition for a limited increase in size. |
Thanks @peterhinch and @Josverl. I confess I don't know much about MicroPython's automatic testing infrastructure, but it looks like generic 'soft timers' (without a hardware ID) are tested in https://github.com/micropython/micropython/blob/master/tests/extmod/machine_soft_timer.py - so there is at least coverage that the rp2 hard= parameter doesn't break the existing behaviour. Apart from the generic timer skip for esp32 and esp8266 at the top of this, it looks like there's no port-specific timer testing at the moment, e.g. for the What we do with generic vs port-specific testing might also depend on whether you want the hard= option for any other ports that are currently soft-irq only. |
In order to ensure coverage, and make sure that the feature remains operational in the future, an additional test would be needed. I would start with a port specific test, but possibly that could be lifted to a port-agnostic test if/when other ports opt-in to this new keyword. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #17363 +/- ##
=======================================
Coverage 98.55% 98.55%
=======================================
Files 169 169
Lines 21946 21946
=======================================
Hits 21628 21628
Misses 318 318 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
9bcbd1c
to
f8fe705
Compare
I've added tests for both this hard= option and the existing rp2-specific tick_hz= option. This means all the existing rp2-specific timer functionality is now tested as well. I've also documented the ability to choose between soft- vs hard-IRQ context and rebased against the current HEAD of the master branch. |
Rebased against the current HEAD of the master branch again. |
Thanks for the contribution, this is a very nice enhancement! The Timer class is a bit inconsistent across ports, as you have noticed. We can't fix that here, but at least we can try not to make things any worse. One question is whether Overall, I think this PR has got it right in terms of API decision, ie simply add the |
Quite a few ports use Softimer. At a quick glance it seems to be alif, mimxrt, renesas, samd and stm32. |
We can add |
I've pushed a corrected version with all the fixes you spotted - thanks! If you're happy with the API choice, I'm very happy to have a go at a follow-up PR generalising this to the other ports, preserving the current default for hard vs soft in each case? |
Unlike some boards like stm32, timer callbacks on the rp2 port are unconditionally dispatched via mp_sched_schedule(), behaving like soft IRQs with consequent GC jitter and delays. Add a 'hard' keyword argument to the rp2 Timer constructor and init. This defaults to False but if it is set True, the timer callback will be dispatched in hard IRQ context rather than queued. Signed-off-by: Chris Webb <[email protected]>
Add tests for both one-shot and periodic timers using the rp2-specific tick_hz= and hard= parameters. Signed-off-by: Chris Webb <[email protected]>
Signed-off-by: Chris Webb <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating, this looks good now.
And thanks for making clean commits with good git commit messages! Makes it easier to just merge.
Yes, feel free to do that! |
Summary
When using
Timer
on the pyboard (and stm32 more generally), the callback is run in hard IRQ context and is reliably punctual.On a Pi Pico 2 board, I was surprised to find that the rp2 port instead queues timer callbacks via
mp_sched_schedule()
which means they are subject to GC delays and potentially unbounded jitter.This probably makes sense as an 'easy mode' default, allowing allocation and less disciplined code in a timer callback, but having it as the only option makes tight timing more awkward on rp2 than it is on stm32. Searching discussions, some have worked around the unpunctual timers by repurposing a PWM output with a hard trigger IRQ. A PIO state machine in an
irq()
loop with a hard IRQ handler would also do the job, but in each case we're wasting limited resources and adding complexity.Rather than hacking around the problem once again, it feels sensible to add a (non-default) option to make the rp2 timers dispatch callbacks in hard IRQ context in the same way stm32 timers do.
Add a
hard=
parameter to theTimer()
constructor and init, matching thehard=
parameter onPin.irq()
andrp2.DMA.irq()
andrp2.PIO.irq()
. When set true, this dispatches the callback in hard IRQ context in the same way as the other hard IRQ handlers for rp2 and the timer callbacks for stm32, taking the same precautions to lock the scheduler and GC, catch unhandled exceptions, and disable the callback on unhandled exceptions.Does this make sense? I've tested this reasonably heavily in my own application without uncovering any problems (see below for a demo) but would be grateful for any thoughts. For example, should this patch make the behaviour configurable in the other ports at the same time? Obviously, I'll update the patch to update the documentation to match wherever it ends up as well.
Testing
To demonstrate the GC jitter with the default soft callbacks on a Pi Pico or Pico 2, try:
with a scope on pin GP0. The fp loop allocates and gcs heavily, so you'll see the 500Hz square wave often lengthen out from 1 ms to 2-4 ms when the callback is delayed.
With this patch and the new hard callbacks, this effect disappears and the jitter is down in the single digit microseconds:
I've tested with multiple concurrent timers of disjoint frequencies up to 20kHz (pushing towards 100% utilisation in interrupt handling) on a variety of different RP2350 boards.