Skip to content

stm32: Support static soft timer instances. #7143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ports/stm32/gccollect.c
Original file line number Diff line number Diff line change
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
10 changes: 5 additions & 5 deletions ports/stm32/machine_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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->callback = args[ARG_callback].u_obj;
self->py_callback = args[ARG_callback].u_obj;
}

if (self->callback != mp_const_none) {
soft_timer_insert(self);
if (self->py_callback != mp_const_none) {
soft_timer_insert(self, self->delta_ms);
}

return mp_const_none;
Expand All @@ -89,8 +88,9 @@ 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 | SOFT_TIMER_FLAG_GC_ALLOCATED;
self->delta_ms = 1000;
self->callback = mp_const_none;
self->py_callback = mp_const_none;

// 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
Original file line number Diff line number Diff line change
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
69 changes: 59 additions & 10 deletions ports/stm32/softtimer.c
Original file line number Diff line number Diff line change
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 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);
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,37 @@ void soft_timer_handler(void) {
}
}

void soft_timer_insert(soft_timer_entry_t *entry) {
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);
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);
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 +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);
}
16 changes: 13 additions & 3 deletions ports/stm32/softtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,32 @@

#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)

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