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