From 7816b1f5134b24d5c352af679d398a491a427edd Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Mon, 16 Jun 2025 12:35:51 +0100 Subject: [PATCH 1/3] rp2/machine_timer: Support hard IRQ timer callbacks. 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 --- ports/rp2/machine_timer.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c index 6f8b04f10eab0..ffb4c7024308a 100644 --- a/ports/rp2/machine_timer.c +++ b/ports/rp2/machine_timer.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "py/gc.h" #include "pico/time.h" #define ALARM_ID_INVALID (-1) @@ -40,13 +41,36 @@ typedef struct _machine_timer_obj_t { uint32_t mode; uint64_t delta_us; // for periodic mode mp_obj_t callback; + bool ishard; } machine_timer_obj_t; const mp_obj_type_t machine_timer_type; static int64_t alarm_callback(alarm_id_t id, void *user_data) { machine_timer_obj_t *self = user_data; - mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + + if (self->ishard) { + // When executing code within a handler we must lock the scheduler to + // prevent any scheduled callbacks from running, and lock the GC to + // prevent any memory allocations. + mp_sched_lock(); + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(self->callback, MP_OBJ_FROM_PTR(self)); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->mode = TIMER_MODE_ONE_SHOT; + mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in timer callback\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + gc_unlock(); + mp_sched_unlock(); + } else { + mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + } + if (self->mode == TIMER_MODE_ONE_SHOT) { return 0; } else { @@ -66,13 +90,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr } static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, }; + enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, ARG_hard, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} }, { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; // Parse args @@ -96,6 +121,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar } self->callback = args[ARG_callback].u_obj; + self->ishard = args[ARG_hard].u_bool; self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true); if (self->alarm_id == -1) { mp_raise_OSError(MP_ENOMEM); From 8f85edad8601c87a107b51a1b417600fbb8d8087 Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Mon, 16 Jun 2025 12:35:51 +0100 Subject: [PATCH 2/3] tests/ports/rp2: Add tests for rp2-specific timer options. Add tests for both one-shot and periodic timers using the rp2-specific tick_hz= and hard= parameters. Signed-off-by: Chris Webb --- tests/ports/rp2/rp2_machine_timer.py | 20 ++++++++++++++++++++ tests/ports/rp2/rp2_machine_timer.py.exp | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/ports/rp2/rp2_machine_timer.py create mode 100644 tests/ports/rp2/rp2_machine_timer.py.exp diff --git a/tests/ports/rp2/rp2_machine_timer.py b/tests/ports/rp2/rp2_machine_timer.py new file mode 100644 index 0000000000000..ac4efcf7f3835 --- /dev/null +++ b/tests/ports/rp2/rp2_machine_timer.py @@ -0,0 +1,20 @@ +from machine import Timer +from time import sleep_ms + +# Test the rp2-specific adjustable tick_hz and hard/soft IRQ handlers +# for both one-shot and periodic timers. + +modes = {Timer.ONE_SHOT: "one-shot", Timer.PERIODIC: "periodic"} +kinds = {False: "soft", True: "hard"} + +for mode in modes: + for hard in kinds: + for period in 2, 4: + timer = Timer( + mode=mode, + period=period, + hard=hard, + callback=lambda t: print("callback", modes[mode], kinds[hard], period), + ) + sleep_ms(9) + timer.deinit() diff --git a/tests/ports/rp2/rp2_machine_timer.py.exp b/tests/ports/rp2/rp2_machine_timer.py.exp new file mode 100644 index 0000000000000..b3dd93dfabfa8 --- /dev/null +++ b/tests/ports/rp2/rp2_machine_timer.py.exp @@ -0,0 +1,16 @@ +callback one-shot soft 2 +callback one-shot soft 4 +callback one-shot hard 2 +callback one-shot hard 4 +callback periodic soft 2 +callback periodic soft 2 +callback periodic soft 2 +callback periodic soft 2 +callback periodic soft 4 +callback periodic soft 4 +callback periodic hard 2 +callback periodic hard 2 +callback periodic hard 2 +callback periodic hard 2 +callback periodic hard 4 +callback periodic hard 4 From 816246836e00b6c2af0ba26e8d16c234b43da4a0 Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Mon, 16 Jun 2025 12:35:51 +0100 Subject: [PATCH 3/3] docs/rp2: Document the new rp2 Timer hard= option. Signed-off-by: Chris Webb --- docs/rp2/quickref.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index 23071d77215d5..ec31442990ff4 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -135,6 +135,12 @@ Use the :mod:`machine.Timer` class:: tim = Timer(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1)) tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2)) +By default, timer callbacks run as soft IRQs so they can allocate but +are prone to GC jitter and delays. Pass ``hard=True`` to the ``Timer()`` +constructor or ``init()`` method to run the callback in hard-IRQ context +instead. This reduces delay and jitter, but see :ref:`isr_rules` for the +restrictions that apply to hard-IRQ handlers. + .. _rp2_Pins_and_GPIO: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy