From 647fa63f9c457b616b1b20fdd98403b681302680 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 29 Apr 2021 15:21:22 +1000 Subject: [PATCH] stm32/softtimer: Support static soft timer instances. This adds support for making static (ie not on the Python GC heap) soft timers. This can be useful for a board to define a custom background handler, or eventually for BLE/network processing to use instead of systick slots; it will be more efficient using soft timer for this. The main issue with using the existing code for static soft timers is that it would combine heap allocated and statically allocated soft_timer_entry_t instances in the same pairing-heap data structure. This would prevent the GC from tracing some of the heap allocated entries (because the GC won't follow pointers outside the heap). This commit makes it so that soft timer entries are explicitly marked, instead of relying on implicit marking by having the root of the pairing heap in the root pointer section. Also, on soft reset only the heap- allocated soft timers are deleted from the pairing heap, leaving the statically allocated ones. Signed-off-by: Damien George --- ports/stm32/gccollect.c | 4 +++ ports/stm32/machine_timer.c | 5 ++- ports/stm32/mpconfigport.h | 2 -- ports/stm32/softtimer.c | 65 +++++++++++++++++++++++++++++++------ ports/stm32/softtimer.h | 6 +++- 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/ports/stm32/gccollect.c b/ports/stm32/gccollect.c index ad5645738e..8b47b121ec 100644 --- a/ports/stm32/gccollect.c +++ b/ports/stm32/gccollect.c @@ -32,6 +32,7 @@ #include "py/mpthread.h" #include "lib/utils/gchelper.h" #include "gccollect.h" +#include "softtimer.h" #include "systick.h" void gc_collect(void) { @@ -51,6 +52,9 @@ void gc_collect(void) { mp_thread_gc_others(); #endif + // trace soft timer nodes + soft_timer_gc_mark_all(); + // end the GC gc_collect_end(); diff --git a/ports/stm32/machine_timer.c b/ports/stm32/machine_timer.c index 3f83d5ba61..c945322187 100644 --- a/ports/stm32/machine_timer.c +++ b/ports/stm32/machine_timer.c @@ -73,14 +73,13 @@ STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar mp_raise_ValueError(MP_ERROR_TEXT("period too large")); } self->delta_ms = (uint32_t)delta_ms; - self->expiry_ms = mp_hal_ticks_ms() + self->delta_ms; if (args[ARG_callback].u_obj != MP_OBJ_NULL) { self->py_callback = args[ARG_callback].u_obj; } if (self->py_callback != mp_const_none) { - soft_timer_insert(self); + soft_timer_insert(self, self->delta_ms); } return mp_const_none; @@ -89,7 +88,7 @@ STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { machine_timer_obj_t *self = m_new_obj(machine_timer_obj_t); self->pairheap.base.type = &machine_timer_type; - self->flags = SOFT_TIMER_FLAG_PY_CALLBACK; + self->flags = SOFT_TIMER_FLAG_PY_CALLBACK | SOFT_TIMER_FLAG_GC_ALLOCATED; self->delta_ms = 1000; self->py_callback = mp_const_none; diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 2888cac88c..28a2d6a76d 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -371,8 +371,6 @@ struct _mp_bluetooth_btstack_root_pointers_t; \ mp_obj_t pyb_extint_callback[PYB_EXTI_NUM_VECTORS]; \ \ - struct _soft_timer_entry_t *soft_timer_heap; \ - \ /* pointers to all Timer objects (if they have been created) */ \ struct _pyb_timer_obj_t *pyb_timer_obj_all[MICROPY_HW_MAX_TIMER]; \ \ diff --git a/ports/stm32/softtimer.c b/ports/stm32/softtimer.c index 7e439d4e75..7f19eccd2d 100644 --- a/ports/stm32/softtimer.c +++ b/ports/stm32/softtimer.c @@ -25,6 +25,8 @@ */ #include +#include "py/gc.h" +#include "py/mphal.h" #include "py/runtime.h" #include "irq.h" #include "softtimer.h" @@ -36,9 +38,10 @@ extern __IO uint32_t uwTick; volatile uint32_t soft_timer_next; -void soft_timer_deinit(void) { - MP_STATE_PORT(soft_timer_heap) = NULL; -} +// Pointer to the pairheap of soft timer objects. +// This may contain bss/data pointers as well as GC-heap pointers, +// and is explicitly GC traced by soft_timer_gc_mark_all(). +STATIC soft_timer_entry_t *soft_timer_heap; STATIC int soft_timer_lt(mp_pairheap_t *n1, mp_pairheap_t *n2) { soft_timer_entry_t *e1 = (soft_timer_entry_t *)n1; @@ -57,10 +60,26 @@ STATIC void soft_timer_schedule_systick(uint32_t ticks_ms) { enable_irq(irq_state); } +void soft_timer_deinit(void) { + // Pop off all the nodes which are allocated on the GC-heap. + uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + soft_timer_entry_t *heap_from = soft_timer_heap; + soft_timer_entry_t *heap_to = (soft_timer_entry_t *)mp_pairheap_new(soft_timer_lt); + while (heap_from != NULL) { + soft_timer_entry_t *entry = (soft_timer_entry_t *)mp_pairheap_peek(soft_timer_lt, &heap_from->pairheap); + heap_from = (soft_timer_entry_t *)mp_pairheap_pop(soft_timer_lt, &heap_from->pairheap); + if (!(entry->flags & SOFT_TIMER_FLAG_GC_ALLOCATED)) { + heap_to = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &heap_to->pairheap, &entry->pairheap); + } + } + soft_timer_heap = heap_to; + restore_irq_pri(irq_state); +} + // Must be executed at IRQ_PRI_PENDSV void soft_timer_handler(void) { uint32_t ticks_ms = uwTick; - soft_timer_entry_t *heap = MP_STATE_PORT(soft_timer_heap); + soft_timer_entry_t *heap = soft_timer_heap; while (heap != NULL && TICKS_DIFF(heap->expiry_ms, ticks_ms) <= 0) { soft_timer_entry_t *entry = heap; heap = (soft_timer_entry_t *)mp_pairheap_pop(soft_timer_lt, &heap->pairheap); @@ -74,7 +93,7 @@ void soft_timer_handler(void) { heap = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &heap->pairheap, &entry->pairheap); } } - MP_STATE_PORT(soft_timer_heap) = heap; + soft_timer_heap = heap; if (heap == NULL) { // No more timers left, set largest delay possible soft_timer_next = uwTick; @@ -84,11 +103,37 @@ void soft_timer_handler(void) { } } -void soft_timer_insert(soft_timer_entry_t *entry) { - mp_pairheap_init_node(soft_timer_lt, &entry->pairheap); +void soft_timer_gc_mark_all(void) { + // Mark all soft timer nodes that are allocated on the GC-heap. + // To avoid deep C recursion, pop and recreate the pairheap as nodes are marked. uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); - MP_STATE_PORT(soft_timer_heap) = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &MP_STATE_PORT(soft_timer_heap)->pairheap, &entry->pairheap); - if (entry == MP_STATE_PORT(soft_timer_heap)) { + soft_timer_entry_t *heap_from = soft_timer_heap; + soft_timer_entry_t *heap_to = (soft_timer_entry_t *)mp_pairheap_new(soft_timer_lt); + while (heap_from != NULL) { + soft_timer_entry_t *entry = (soft_timer_entry_t *)mp_pairheap_peek(soft_timer_lt, &heap_from->pairheap); + heap_from = (soft_timer_entry_t *)mp_pairheap_pop(soft_timer_lt, &heap_from->pairheap); + if (entry->flags & SOFT_TIMER_FLAG_GC_ALLOCATED) { + gc_collect_root((void **)&entry, 1); + } + heap_to = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &heap_to->pairheap, &entry->pairheap); + } + soft_timer_heap = heap_to; + restore_irq_pri(irq_state); +} + +void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t delta_ms, void (*cb)(soft_timer_entry_t *)) { + entry->flags = 0; + entry->mode = mode; + entry->delta_ms = delta_ms; + entry->c_callback = cb; +} + +void soft_timer_insert(soft_timer_entry_t *entry, uint32_t initial_delta_ms) { + mp_pairheap_init_node(soft_timer_lt, &entry->pairheap); + entry->expiry_ms = mp_hal_ticks_ms() + initial_delta_ms; + uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + soft_timer_heap = (soft_timer_entry_t *)mp_pairheap_push(soft_timer_lt, &soft_timer_heap->pairheap, &entry->pairheap); + if (entry == soft_timer_heap) { // This new timer became the earliest one so set soft_timer_next soft_timer_schedule_systick(entry->expiry_ms); } @@ -97,6 +142,6 @@ void soft_timer_insert(soft_timer_entry_t *entry) { void soft_timer_remove(soft_timer_entry_t *entry) { uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); - MP_STATE_PORT(soft_timer_heap) = (soft_timer_entry_t *)mp_pairheap_delete(soft_timer_lt, &MP_STATE_PORT(soft_timer_heap)->pairheap, &entry->pairheap); + soft_timer_heap = (soft_timer_entry_t *)mp_pairheap_delete(soft_timer_lt, &soft_timer_heap->pairheap, &entry->pairheap); restore_irq_pri(irq_state); } diff --git a/ports/stm32/softtimer.h b/ports/stm32/softtimer.h index 6e06bbcc65..25d828a9fd 100644 --- a/ports/stm32/softtimer.h +++ b/ports/stm32/softtimer.h @@ -29,6 +29,7 @@ #include "py/pairheap.h" #define SOFT_TIMER_FLAG_PY_CALLBACK (1) +#define SOFT_TIMER_FLAG_GC_ALLOCATED (2) #define SOFT_TIMER_MODE_ONE_SHOT (1) #define SOFT_TIMER_MODE_PERIODIC (2) @@ -49,7 +50,10 @@ extern volatile uint32_t soft_timer_next; void soft_timer_deinit(void); void soft_timer_handler(void); -void soft_timer_insert(soft_timer_entry_t *entry); +void soft_timer_gc_mark_all(void); + +void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t delta_ms, void (*cb)(soft_timer_entry_t *)); +void soft_timer_insert(soft_timer_entry_t *entry, uint32_t initial_delta_ms); void soft_timer_remove(soft_timer_entry_t *entry); #endif // MICROPY_INCLUDED_STM32_SOFTTIMER_H