summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngus Gratton <angus@redyak.com.au>2024-01-02 16:35:27 +1100
committerDamien George <damien@micropython.org>2024-05-31 16:44:09 +1000
commit83e82c5ad3fb9e8796d786e01509d264cfb205d3 (patch)
treeed4f8636eef2aaa77dded52f91c27149a5305f34
parent74fb42aa82d537b6fefb463907274bd660c6ce8d (diff)
rp2: Refactor to not use pico-sdk alarm pool functions for sleeping.
The best_effort_wfe_or_timeout() and sleep_us() pico-sdk functions use the pico-sdk alarm pool internally, and that has a bug. Some usages inside pico-sdk (notably multicore_lockout_start_blocking()) will still end up calling best_effort_wfe_or_timeout(), although usually with "end_of_time" as the timeout value so it should avoid any alarm pool race conditions. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
-rw-r--r--ports/rp2/mphalport.c40
-rw-r--r--ports/rp2/mphalport.h9
-rw-r--r--shared/runtime/softtimer.c2
3 files changed, 39 insertions, 12 deletions
diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c
index ac3a70822..a13a0edc3 100644
--- a/ports/rp2/mphalport.c
+++ b/ports/rp2/mphalport.c
@@ -198,17 +198,31 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
return did_write ? ret : 0;
}
+void mp_hal_delay_us(mp_uint_t us) {
+ // Avoid calling sleep_us() and invoking the alarm pool by splitting long
+ // sleeps into an optional longer sleep and a shorter busy-wait
+ uint64_t end = time_us_64() + us;
+ if (us > 1000) {
+ mp_hal_delay_ms(us / 1000);
+ }
+ while (time_us_64() < end) {
+ // Tight loop busy-wait for accurate timing
+ }
+}
+
void mp_hal_delay_ms(mp_uint_t ms) {
- absolute_time_t t = make_timeout_time_ms(ms);
+ mp_uint_t start = mp_hal_ticks_ms();
+ mp_uint_t elapsed = 0;
do {
- mp_event_handle_nowait();
- } while (!best_effort_wfe_or_timeout(t));
+ mp_event_wait_ms(ms - elapsed);
+ elapsed = mp_hal_ticks_ms() - start;
+ } while (elapsed < ms);
}
void mp_hal_time_ns_set_from_rtc(void) {
- // Delay at least one RTC clock cycle so it's registers have updated with the most
- // recent time settings.
- sleep_us(23);
+ // Outstanding RTC register writes need at least two RTC clock cycles to
+ // update. (See RP2040 datasheet section 4.8.4 "Reference clock").
+ mp_hal_delay_us(44);
// Sample RTC and time_us_64() as close together as possible, so the offset
// calculated for the latter can be as accurate as possible.
@@ -295,3 +309,17 @@ void soft_timer_init(void) {
hardware_alarm_claim(MICROPY_HW_SOFT_TIMER_ALARM_NUM);
hardware_alarm_set_callback(MICROPY_HW_SOFT_TIMER_ALARM_NUM, soft_timer_hardware_callback);
}
+
+void mp_wfe_or_timeout(uint32_t timeout_ms) {
+ soft_timer_entry_t timer;
+
+ // Note the timer doesn't have an associated callback, it just exists to create a
+ // hardware interrupt to wake the CPU
+ soft_timer_static_init(&timer, SOFT_TIMER_MODE_ONE_SHOT, 0, NULL);
+ soft_timer_insert(&timer, timeout_ms);
+
+ __wfe();
+
+ // Clean up the timer node if it's not already
+ soft_timer_remove(&timer);
+}
diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h
index d2d74d783..bc09f8c4a 100644
--- a/ports/rp2/mphalport.h
+++ b/ports/rp2/mphalport.h
@@ -62,23 +62,22 @@
if ((TIMEOUT_MS) < 0) { \
__wfe(); \
} else { \
- best_effort_wfe_or_timeout(make_timeout_time_ms(TIMEOUT_MS)); \
+ mp_wfe_or_timeout(TIMEOUT_MS); \
} \
} while (0)
extern int mp_interrupt_char;
extern ringbuf_t stdin_ringbuf;
+// Port-specific function to create a wakeup interrupt after timeout_ms and enter WFE
+void mp_wfe_or_timeout(uint32_t timeout_ms);
+
uint32_t mp_thread_begin_atomic_section(void);
void mp_thread_end_atomic_section(uint32_t);
void mp_hal_set_interrupt_char(int c);
void mp_hal_time_ns_set_from_rtc(void);
-static inline void mp_hal_delay_us(mp_uint_t us) {
- sleep_us(us);
-}
-
static inline void mp_hal_delay_us_fast(mp_uint_t us) {
busy_wait_us(us);
}
diff --git a/shared/runtime/softtimer.c b/shared/runtime/softtimer.c
index 13a6e7877..92a0d6d51 100644
--- a/shared/runtime/softtimer.c
+++ b/shared/runtime/softtimer.c
@@ -89,7 +89,7 @@ void soft_timer_handler(void) {
heap = (soft_timer_entry_t *)mp_pairheap_pop(soft_timer_lt, &heap->pairheap);
if (entry->flags & SOFT_TIMER_FLAG_PY_CALLBACK) {
mp_sched_schedule(entry->py_callback, MP_OBJ_FROM_PTR(entry));
- } else {
+ } else if (entry->c_callback) {
entry->c_callback(entry);
}
if (entry->mode == SOFT_TIMER_MODE_PERIODIC) {