From c617ed159a632c59838000e21d2dde9ce1feaaec Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Mon, 25 Aug 2025 14:20:34 +0100 Subject: [PATCH] zephyr/machine_timer: Support hard IRQ timer callbacks. machine.Timer() has inconsistent behaviour between ports: some run callbacks in hard IRQ context whereas others schedule them like soft IRQs. As on the rp2 port, add support to the zephyr port for a hard= argument to explicitly choose between these, setting the default to False to match the existing behaviour. Adjust the stack-limit check to use the ISR stack while the callback is dispatched so that hard IRQ callbacks work, as with machine_pin.c and machine_i2c_target.c IRQ callbacks. Signed-off-by: Chris Webb --- ports/zephyr/machine_timer.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/ports/zephyr/machine_timer.c b/ports/zephyr/machine_timer.c index 1bb743c2eb..410e86762b 100644 --- a/ports/zephyr/machine_timer.c +++ b/ports/zephyr/machine_timer.c @@ -27,9 +27,11 @@ #include #include +#include "py/gc.h" #include "py/mperrno.h" #include "py/obj.h" #include "py/runtime.h" +#include "shared/runtime/mpirq.h" #include "modmachine.h" #include #include @@ -51,6 +53,7 @@ typedef struct _machine_timer_obj_t { uint32_t period_ms; mp_obj_t callback; + bool ishard; struct _machine_timer_obj_t *next; } machine_timer_obj_t; @@ -62,12 +65,31 @@ static mp_obj_t machine_timer_deinit(mp_obj_t self_in); static void machine_timer_callback(struct k_timer *timer) { machine_timer_obj_t *self = (machine_timer_obj_t *)k_timer_user_data_get(timer); + + #if MICROPY_STACK_CHECK + // This callback executes in an ISR context so the stack-limit check must + // be changed to use the ISR stack for the duration of this function (so + // that hard IRQ callbacks work). + char *orig_stack_top = MP_STATE_THREAD(stack_top); + size_t orig_stack_limit = MP_STATE_THREAD(stack_limit); + MP_STATE_THREAD(stack_top) = (void *)&self; + MP_STATE_THREAD(stack_limit) = CONFIG_ISR_STACK_SIZE - 512; + #endif + + if (mp_irq_dispatch(self->callback, MP_OBJ_FROM_PTR(self), self->ishard) < 0) { + // Uncaught exception; disable the callback so it doesn't run again. + self->mode = TIMER_MODE_ONE_SHOT; + } + if (self->mode == TIMER_MODE_ONE_SHOT) { machine_timer_deinit(self); } - if (self->callback != mp_const_none) { - mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); - } + + #if MICROPY_STACK_CHECK + // Restore original stack-limit checking values. + MP_STATE_THREAD(stack_top) = orig_stack_top; + MP_STATE_THREAD(stack_limit) = orig_stack_limit; + #endif } static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -109,6 +131,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n ARG_callback, ARG_period, 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} }, @@ -119,6 +142,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n #else { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, #endif + { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -139,6 +163,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n self->mode = args[ARG_mode].u_int; self->callback = args[ARG_callback].u_obj; + self->ishard = args[ARG_hard].u_bool; k_timer_init(&self->my_timer, machine_timer_callback, NULL); k_timer_user_data_set(&self->my_timer, self);