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 <damien@micropython.org>
This commit is contained in:
Damien George 2021-04-29 15:21:22 +10:00
parent 89b64478c7
commit 647fa63f9c
5 changed files with 66 additions and 16 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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]; \
\

View File

@ -25,6 +25,8 @@
*/
#include <stdint.h>
#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);
}

View File

@ -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