Skip to content

Commit

Permalink
stm32/softtimer: Support static soft timer instances.
Browse files Browse the repository at this point in the history
Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed Apr 21, 2021
1 parent aa49c61 commit a454c8f
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 14 deletions.
4 changes: 4 additions & 0 deletions ports/stm32/gccollect.c
Expand Up @@ -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) {
Expand All @@ -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();

Expand Down
3 changes: 2 additions & 1 deletion ports/stm32/machine_timer.c
Expand Up @@ -69,7 +69,7 @@ STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar
}
self->expiry_ms = mp_hal_ticks_ms() + self->delta_ms;

self->callback = args[ARG_callback].u_obj;
self->py_callback = args[ARG_callback].u_obj;
soft_timer_insert(self);

return mp_const_none;
Expand All @@ -78,6 +78,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_ON_HEAP | SOFT_TIMER_FLAG_PY_CALLBACK;

// Get timer id (only soft timer (-1) supported at the moment)
mp_int_t id = -1;
Expand Down
2 changes: 0 additions & 2 deletions ports/stm32/mpconfigport.h
Expand Up @@ -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]; \
\
Expand Down
68 changes: 59 additions & 9 deletions ports/stm32/softtimer.c
Expand Up @@ -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"
Expand All @@ -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;
Expand All @@ -57,20 +60,40 @@ 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 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_ON_HEAP)) {
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);
mp_sched_schedule(entry->callback, MP_OBJ_FROM_PTR(entry));
if (entry->flags & SOFT_TIMER_FLAG_PY_CALLBACK) {
mp_sched_schedule(entry->py_callback, MP_OBJ_FROM_PTR(entry));
} else {
entry->c_callback(entry);
}
if (entry->mode == SOFT_TIMER_MODE_PERIODIC) {
entry->expiry_ms += entry->delta_ms;
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;
Expand All @@ -80,11 +103,38 @@ void soft_timer_handler(void) {
}
}

void soft_timer_gc_mark_all(void) {
// Mark all soft timer nodes that are allocated on the 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);
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_ON_HEAP) {
gc_collect_mark(entry);
}
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_init_static(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->expiry_ms = mp_hal_ticks_ms() + delta_ms;
entry->delta_ms = delta_ms;
entry->c_callback = cb;
soft_timer_insert(entry);
}

void soft_timer_insert(soft_timer_entry_t *entry) {
mp_pairheap_init_node(soft_timer_lt, &entry->pairheap);
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_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);
}
Expand All @@ -93,6 +143,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);
}
14 changes: 12 additions & 2 deletions ports/stm32/softtimer.h
Expand Up @@ -28,21 +28,31 @@

#include "py/pairheap.h"

#define SOFT_TIMER_FLAG_ON_HEAP (1)
#define SOFT_TIMER_FLAG_PY_CALLBACK (2)

#define SOFT_TIMER_MODE_ONE_SHOT (1)
#define SOFT_TIMER_MODE_PERIODIC (2)

typedef struct _soft_timer_entry_t {
mp_pairheap_t pairheap;
uint32_t mode;
uint16_t flags;
uint16_t mode;
uint32_t expiry_ms;
uint32_t delta_ms; // for periodic mode
mp_obj_t callback;
union {
void (*c_callback)(struct _soft_timer_entry_t *);
mp_obj_t py_callback;
};
} soft_timer_entry_t;

extern volatile uint32_t soft_timer_next;

void soft_timer_deinit(void);
void soft_timer_handler(void);
void soft_timer_gc_mark_all(void);

void soft_timer_init_static(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);
void soft_timer_remove(soft_timer_entry_t *entry);

Expand Down

0 comments on commit a454c8f

Please sign in to comment.