From 516cc280e03b35469096db5f7de141a1018a185f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 14 Nov 2023 13:19:31 +1100 Subject: [PATCH] shared/runtime/softtimer: Generalise soft_timer to work without SysTick. If a port defines MICROPY_SOFT_TIMER_TICKS_MS then soft_timer assumes a SysTick back end, and provides a soft_timer_next variable that sets when the next call to soft_timer_handler() should occur. Otherwise, a port should provide soft_timer_get_ms() and soft_timer_schedule_at_ms() with appropriate semantics (see comments). Existing users of soft_timer should continue to work as they did. Signed-off-by: Damien George --- ports/renesas-ra/mphalport.h | 3 ++ ports/samd/mphalport.h | 3 ++ shared/runtime/softtimer.c | 75 ++++++++++++++++++------------------ shared/runtime/softtimer.h | 20 ++++++++++ 4 files changed, 63 insertions(+), 38 deletions(-) diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h index 2b25a36fbf..e29a7fec7b 100644 --- a/ports/renesas-ra/mphalport.h +++ b/ports/renesas-ra/mphalport.h @@ -29,6 +29,9 @@ #include "pin.h" #include "py/ringbuf.h" +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV) +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state) + #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) extern const unsigned char mp_hal_status_to_errno_table[4]; diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h index 83ccf44222..ee33e74462 100644 --- a/ports/samd/mphalport.h +++ b/ports/samd/mphalport.h @@ -35,6 +35,9 @@ #include "hpl_time_measure.h" #include "sam.h" +#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV) +#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state) + #define MICROPY_HW_USB_CDC_TX_TIMEOUT (500) extern int mp_interrupt_char; diff --git a/shared/runtime/softtimer.c b/shared/runtime/softtimer.c index 267610b269..468fa95b72 100644 --- a/shared/runtime/softtimer.c +++ b/shared/runtime/softtimer.c @@ -30,42 +30,43 @@ #include "py/runtime.h" #include "softtimer.h" +#ifdef MICROPY_SOFT_TIMER_TICKS_MS + extern __IO uint32_t MICROPY_SOFT_TIMER_TICKS_MS; volatile uint32_t soft_timer_next; +static inline uint32_t soft_timer_get_ms(void) { + return MICROPY_SOFT_TIMER_TICKS_MS; +} + +static void soft_timer_schedule_at_ms(uint32_t ticks_ms) { + uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + uint32_t uw_tick = MICROPY_SOFT_TIMER_TICKS_MS; + if (soft_timer_ticks_diff(ticks_ms, uw_tick) <= 0) { + soft_timer_next = uw_tick + 1; + } else { + soft_timer_next = ticks_ms; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); +} + +#endif + // 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 inline int32_t ticks_diff(uint32_t t1, uint32_t t0) { - // t1 is after t0 (i.e. positive result) if there exists a uint32_t X <= INT_MAX - // such that t0 + X = t1. Otherwise t1 is interpreted to be earlier than - // t0 (negative result). - return t1 - t0; -} - STATIC int soft_timer_lt(mp_pairheap_t *n1, mp_pairheap_t *n2) { soft_timer_entry_t *e1 = (soft_timer_entry_t *)n1; soft_timer_entry_t *e2 = (soft_timer_entry_t *)n2; - return ticks_diff(e1->expiry_ms, e2->expiry_ms) < 0; -} - -STATIC void soft_timer_schedule_systick(uint32_t ticks_ms) { - uint32_t irq_state = disable_irq(); - uint32_t uw_tick = MICROPY_SOFT_TIMER_TICKS_MS; - if (ticks_diff(ticks_ms, uw_tick) <= 0) { - soft_timer_next = uw_tick + 1; - } else { - soft_timer_next = ticks_ms; - } - enable_irq(irq_state); + return soft_timer_ticks_diff(e1->expiry_ms, e2->expiry_ms) < 0; } 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); + MICROPY_PY_PENDSV_ENTER; 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) { @@ -76,14 +77,14 @@ void soft_timer_deinit(void) { } } soft_timer_heap = heap_to; - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } // Must be executed at IRQ_PRI_PENDSV void soft_timer_handler(void) { - uint32_t ticks_ms = MICROPY_SOFT_TIMER_TICKS_MS; + uint32_t ticks_ms = soft_timer_get_ms(); soft_timer_entry_t *heap = soft_timer_heap; - while (heap != NULL && ticks_diff(heap->expiry_ms, ticks_ms) <= 0) { + while (heap != NULL && soft_timer_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); if (entry->flags & SOFT_TIMER_FLAG_PY_CALLBACK) { @@ -97,19 +98,17 @@ void soft_timer_handler(void) { } } soft_timer_heap = heap; - if (heap == NULL) { - // No more timers left, set largest delay possible - soft_timer_next = MICROPY_SOFT_TIMER_TICKS_MS; - } else { - // Set soft_timer_next so SysTick calls us back at the correct time - soft_timer_schedule_systick(heap->expiry_ms); + + // Schedule the port's timer to call us back at the correct time. + if (heap != NULL) { + soft_timer_schedule_at_ms(heap->expiry_ms); } } 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); + MICROPY_PY_PENDSV_ENTER; 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) { @@ -121,7 +120,7 @@ void soft_timer_gc_mark_all(void) { 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); + MICROPY_PY_PENDSV_EXIT; } void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t delta_ms, void (*cb)(soft_timer_entry_t *)) { @@ -134,18 +133,18 @@ void soft_timer_static_init(soft_timer_entry_t *entry, uint16_t mode, uint32_t d 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 = MICROPY_SOFT_TIMER_TICKS_MS + initial_delta_ms; - uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + entry->expiry_ms = soft_timer_get_ms() + initial_delta_ms; + MICROPY_PY_PENDSV_ENTER; 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); + // This new timer became the earliest one so schedule a callback. + soft_timer_schedule_at_ms(entry->expiry_ms); } - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } void soft_timer_remove(soft_timer_entry_t *entry) { - uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); + MICROPY_PY_PENDSV_ENTER; soft_timer_heap = (soft_timer_entry_t *)mp_pairheap_delete(soft_timer_lt, &soft_timer_heap->pairheap, &entry->pairheap); - restore_irq_pri(irq_state); + MICROPY_PY_PENDSV_EXIT; } diff --git a/shared/runtime/softtimer.h b/shared/runtime/softtimer.h index 8603ea13f0..fe5d02b907 100644 --- a/shared/runtime/softtimer.h +++ b/shared/runtime/softtimer.h @@ -48,6 +48,13 @@ typedef struct _soft_timer_entry_t { extern volatile uint32_t soft_timer_next; +static inline int32_t soft_timer_ticks_diff(uint32_t t1, uint32_t t0) { + // t1 is after t0 (i.e. positive result) if there exists a uint32_t X <= INT_MAX + // such that t0 + X = t1. Otherwise t1 is interpreted to be earlier than + // t0 (negative result). + return t1 - t0; +} + void soft_timer_deinit(void); void soft_timer_handler(void); void soft_timer_gc_mark_all(void); @@ -63,4 +70,17 @@ static inline void soft_timer_reinsert(soft_timer_entry_t *entry, uint32_t initi soft_timer_insert(entry, initial_delta_ms); } +#if !defined(MICROPY_SOFT_TIMER_TICKS_MS) +// IF MICROPY_SOFT_TIMER_TICKS_MS is not defined then the port must provide the +// following timer functions: +// - soft_timer_get_ms() must return a 32-bit millisecond counter that wraps around. +// - soft_timer_schedule_at_ms(ticks_ms) must schedule a callback of soft_timer_handler() +// when the above millisecond counter reaches the given ticks_ms value. If ticks_ms +// is behind the current counter (using int32_t arithmetic) then the callback should +// be scheduled immediately. The callback of soft_timer_handler() should be made at +// pend-SV IRQ level, or equivalent. +uint32_t soft_timer_get_ms(void); +void soft_timer_schedule_at_ms(uint32_t ticks_ms); +#endif + #endif // MICROPY_INCLUDED_SHARED_RUNTIME_SOFTTIMER_H