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>
pull/13329/head
Angus Gratton 2024-01-02 16:35:27 +11:00
rodzic 20c866de07
commit fccab87fc7
4 zmienionych plików z 40 dodań i 12 usunięć

Wyświetl plik

@ -263,6 +263,7 @@ typedef intptr_t mp_off_t;
#define BINARY_INFO_ID_MP_FROZEN 0x4a99d719
#define MICROPY_FROZEN_LIST_ITEM(name, file) bi_decl(bi_string(BINARY_INFO_TAG_MICROPYTHON, BINARY_INFO_ID_MP_FROZEN, name))
extern uint32_t rosc_random_u32(void);
extern void lwip_lock_acquire(void);
extern void lwip_lock_release(void);

Wyświetl plik

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

Wyświetl plik

@ -57,23 +57,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);
}

Wyświetl plik

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