Make it compile and work with RP2040 and RP2350

Includes AMY-1782 workaround
This commit is contained in:
William Vinnicombe 2025-08-20 17:14:19 +01:00
parent 991fc65582
commit eb89a06c64
4 changed files with 83 additions and 48 deletions

View file

@ -41,6 +41,16 @@ extern "C" {
#include "hardware/clocks.h"
typedef enum {
DORMANT_CLOCK_SOURCE_XOSC,
DORMANT_CLOCK_SOURCE_ROSC,
#if !PICO_RP2040
DORMANT_CLOCK_SOURCE_LPOSC,
#endif
NUM_DORMANT_CLOCK_SOURCES
} dormant_clock_source_t;
// NOTE: Need to deinit usb before doing into any of these sleep states
// could keep usb clk_sys and clk_usb to usbctrl running during low_power_sleep. Although you'll get woken
// up pretty quickly
@ -49,7 +59,7 @@ extern "C" {
// sleep is really just calling a __wfi() until an irq, with some optional clock gating in the sleep_en register. Activated once processor goes to sleep
// So can just do it for arbitrary interrupts. processors implement their own internal clock gating, just leaving the wakeup interrupt controller running during
// __wfi()
int low_power_sleep_until_irq(const clock_dest_set_t *keep_enabled)
int low_power_sleep_until_irq(const clock_dest_set_t *keep_enabled);
// sleep until the given timer reaches the specified value; if the time passes then no sleep occurs
// keep_enabled defaults to none if NULL
@ -58,18 +68,21 @@ int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until, const
// Note bool above saying shall we only listen for timer irq or other irqs
// Need to defer handling of irqs to do clock setup etc
static inline int low_power_sleep_until_default_timer(absolute_time_t until, const clock_dest_set_t *keep_enabled) {
static inline int low_power_sleep_until_default_timer(absolute_time_t until, const clock_dest_set_t *keep_enabled, bool exclusive) {
// Need to assert (or add) ticks block and timer clocks to the keep_enabled list
return low_power_sleep_until_timer(PICO_DEFAULT_TIMER_INSTANCE(), until, keep_enabled);
return low_power_sleep_until_timer(PICO_DEFAULT_TIMER_INSTANCE(), until, keep_enabled, exclusive);
}
// ** LIAM BLESSED this for RP2040; why not RP2350 **
// This should work on both via io bank interrupts
void low_power_sleep_until_pin_state(uint gpio_pin, bool edge, bool high);
#if 0
// ** LIAM SAYS THIS IS NO MORE USEFUL THAN SLEEP_UNTIL TIMER... **
// There isn't much advantage to using the aon timer here as system timers are more accurate and on anyway
// ** WILL AGREES WITH LIAM ON THIS **
int low_power_sleep_until_aon_timer(absolute_time_t until, const clock_dest_set_t *keep_enabled);
#endif
// ** LIAM BLESSED but we need to impl it correctly (note not blessed for RP2040, but we should do it anyway for orthogonality **
// Only works for RP2350 as every clock will be stopped on RP2040 (unless you provide a clock for the RTC)
@ -78,18 +91,17 @@ int low_power_sleep_until_aon_timer(absolute_time_t until, const clock_dest_set_
// NOTE: Asserting that we will alway use rosc for dormant and simplifies the API
// Means if the user has sped up the rosc they should slow it down before going into dormant
// Need to re initialize clocks after this
int low_power_dormant_until_aon_timer(absolute_time_t until, uint src_hz, uint gpio_pin);
int low_power_dormant_until_aon_timer(absolute_time_t until, dormant_clock_source_t dormant_clock_source, uint src_hz, uint gpio_pin, const clock_dest_set_t *keep_enabled);
// ** LIAM BLESSED but we need to impl it correctly (note not blessed for RP2040, but we should do it anyway for orthogonality **
// This works on both
// Need to re initialize clocks after this
void low_power_dormant_until_pin_state(uint gpio_pin, bool edge, bool high);
void low_power_dormant_until_pin_state(uint gpio_pin, bool edge, bool high, dormant_clock_source_t dormant_clock_source, const clock_dest_set_t *keep_enabled);
#if !PICO_RP2040
#if !PICO_RP2040 && 0 // todo - implement these
// pstate functions should return to the pstate you were in
void low_power_pstate_until_aon_timer(pstate_bitset_t *pstate);
void low_power_pstate_until_pin_state(pstate_bitset_t *pstate);
#endif
// Or a function saying how did I boot?
// Would like to make it easy to get back to main after going to sleep
@ -103,8 +115,9 @@ void low_power_pstate_until_pin_state(pstate_bitset_t *pstate);
// Doesn't support powering down switched core domain
void low_power_pstate_set(pstate_bitset_t *pstate);
pstate_bitset_t low_power_pstate_get(void);
#endif
#if 0
#if 0 // todo - I think these are not being done?
void sleep_run_from_dormant_source(dormant_clock_source_t dormant_source);
/*! \brief Send system to sleep until a leading high edge is detected on GPIO
* \ingroup pico_sleep
@ -128,8 +141,6 @@ static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) {
sleep_goto_dormant_until_pin(gpio_pin, false, true);
}
#endif
/*! \brief Reconfigure clocks to wake up properly from sleep/dormant mode
* \ingroup pico_sleep
*
@ -138,6 +149,8 @@ static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) {
*/
void sleep_power_up(void);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -59,7 +59,9 @@ static void prepare_for_clock_gating(void) {
}
static void post_clock_gating(void) {
// restore all clocks in sleep mode, to prevent other __wfi from causing issues
clock_dest_set_t all = clock_dest_set_all();
clock_gate_sleep_en(&all);
}
static void prepare_for_clock_switch(void) {
@ -113,7 +115,7 @@ static void replace_null_enable_values(const clock_dest_set_t *keep_enabled,
// only the deep_sleep variant of this, as DORMANT cannot wake from TIMER
int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until,
const clock_dest_set_t *keep_enabled) {
const clock_dest_set_t *keep_enabled, __unused bool exclusive) {
int alarm_num = timer_hardware_alarm_claim_unused(timer, false);
if (alarm_num < 0) return PICO_ERROR_INSUFFICIENT_RESOURCES;
@ -131,7 +133,7 @@ int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until,
// we know that people in the wild (MicroPython) have wanted to do some mapping to also
// figure out what PLLs are still on via these bits
#if PICO_RP2040
clock_dests_add(&local_keep_enabled, CLK_DEST_SYS_TIMER);
clock_dest_set_add(&local_keep_enabled, CLK_DEST_SYS_TIMER);
#elif PICO_RP2350
clock_dest_set_add(&local_keep_enabled, timer_get_index(timer) ? CLK_DEST_SYS_TIMER1 : CLK_DEST_SYS_TIMER0);
clock_dest_set_add(&local_keep_enabled, CLK_DEST_REF_TICKS);
@ -155,6 +157,7 @@ int low_power_sleep_until_timer(timer_hw_t *timer, absolute_time_t until,
return 0;
}
#if 0
// todo note this has (not surprisingly a lot of commonality with the timer one)
int low_power_sleep_until_aon_timer(absolute_time_t until,
const clock_dest_set_t *keep_enabled) {
@ -172,7 +175,7 @@ int low_power_sleep_until_aon_timer(absolute_time_t until,
// fun, we probably want to have a "sparse" bitset macro encoded as 4 bytes or 8 bytes (for 4 or 7 indices
// between 0 and 254) - i say 7, to leave encoding space for maybe indices > 256 in the 8 byte variant
#if PICO_RP2040
clock_dests_add(&local_keep_enabled, CLOCK_DEST_RTC_RTC);
clock_dest_set_add(&local_keep_enabled, CLOCK_DEST_RTC_RTC);
#elif PICO_RP2350
clock_dest_set_add(&local_keep_enabled, CLK_DEST_REF_POWMAN);
#else
@ -198,7 +201,7 @@ int low_power_sleep_until_aon_timer(absolute_time_t until,
post_clock_gating();
return 0;
}
#endif
// In order to go into dormant mode we need to be running from a stoppable clock source:
// either the xosc or rosc with no PLLs running. This means we disable the USB and ADC clocks
@ -225,6 +228,7 @@ void low_power_setup_clocks_for_dormant(dormant_clock_source_t dormant_source) {
#endif
default:
hard_assert(false);
__builtin_unreachable();
}
clock_configure_undivided(clk_ref,
@ -314,7 +318,7 @@ int low_power_dormant_until_aon_timer(absolute_time_t until,
#if PICO_RP2040
// The RTC must be run from an external source, since the dormant source will be inactive
rtc_run_from_external_source(src_hz, gpio_pin);
clock_dests_add(&local_keep_enabled, CLOCK_DEST_RTC_RTC);
clock_dest_set_add(&local_keep_enabled, CLK_DEST_RTC_RTC);
#elif PICO_RP2350
// todo
((void)gpio_pin);
@ -393,3 +397,24 @@ void low_power_dormant_until_pin_state(uint gpio_pin, bool edge, bool high,
low_power_wake_from_dormant();
}
#if !PICO_RUNTIME_NO_INIT_RP2350_SLEEP_FIX
#include "hardware/sync.h"
void __weak runtime_init_rp2350_sleep_fix(void) {
if (watchdog_hw->reason && WATCHDOG_REASON_TIMER_BITS) { // detect rom_reboot() usage
int alarm_num = timer_hardware_alarm_claim_unused(timer_hw, false);
if (alarm_num < 0) return;
timer_hardware_alarm_set_callback(timer_hw, alarm_num, ((hardware_alarm_callback_t )low_power_wakeup));
timer_hardware_alarm_set_target(timer_hw, alarm_num, make_timeout_time_us(100));
__wfi();
timer_hardware_alarm_set_callback(timer_hw, alarm_num, NULL);
timer_hardware_alarm_unclaim(timer_hw, alarm_num);
}
}
#endif
#if !PICO_RUNTIME_SKIP_INIT_RP2350_SLEEP_FIX
PICO_RUNTIME_INIT_FUNC_RUNTIME(runtime_init_rp2350_sleep_fix, PICO_RUNTIME_INIT_RP2350_SLEEP_FIX);
#endif

View file

@ -416,6 +416,22 @@ void runtime_init_bootrom_locking_enable(void);
#endif
#endif
// ------------------------------------------------------------
// RP2350 sleep fix
// ------------------------------------------------------------
#ifndef PICO_RUNTIME_INIT_RP2350_SLEEP_FIX
#define PICO_RUNTIME_INIT_RP2350_SLEEP_FIX "11010"
#endif
#ifndef PICO_RUNTIME_SKIP_INIT_RP2350_SLEEP_FIX
#define PICO_RUNTIME_SKIP_INIT_RP2350_SLEEP_FIX !PICO_RP2350
#endif
#ifndef PICO_RUNTIME_NO_INIT_RP2350_SLEEP_FIX
#define PICO_RUNTIME_NO_INIT_RP2350_SLEEP_FIX !PICO_RP2350
#endif
// ------------------------------------------------------------------------------------------------
// stack guard; these are a special case as they take a parameter; however the normal defines apply
// ------------------------------------------------------------------------------------------------

View file

@ -26,12 +26,19 @@ int main() {
printf("Waiting 1 sec\n"); // so we can see some repeat printfs
busy_wait_ms(1100);
#endif
absolute_time_t start_time;
absolute_time_t wakeup_time;
int64_t diff;
uint64_t current_aon;
struct timespec ts;
printf("Going to sleep for 5 seconds via TIMER\n");
absolute_time_t start_time = get_absolute_time();
absolute_time_t wakeup_time = delayed_by_ms(start_time, 5000);
low_power_sleep_until_timer(timer_hw, wakeup_time, NULL);
int64_t diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
start_time = get_absolute_time();
wakeup_time = delayed_by_ms(start_time, 5000);
low_power_sleep_until_timer(timer_hw, wakeup_time, NULL, true);
diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
printf("Woken up now @%dus since target\n", (int)diff);
if (diff < 0) {
printf("ERROR: Woke up too soon\n");
@ -39,33 +46,7 @@ int main() {
}
busy_wait_ms(3000);
printf("Going to sleep for 5 seconds via AON TIMER\n");
// todo, ah; we should start the aon timer; still have to decide what to do about keeping them in sync
start_time = get_absolute_time();
struct timespec ts;
us_to_timespec(start_time, &ts);
aon_timer_start(&ts);
wakeup_time = delayed_by_ms(start_time, 5000);
low_power_sleep_until_aon_timer(wakeup_time, NULL);
diff = absolute_time_diff_us(get_absolute_time(), wakeup_time);
// need to use the AON timer for checking time, since the other timer is unclocked
diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
if (diff > -4000000) {
printf("ERROR: doesn't seem like timer was stopped\n");
return - 1;
}
aon_timer_get_time(&ts);
uint64_t current_aon = timespec_to_us(&ts);
diff = absolute_time_diff_us(wakeup_time, from_us_since_boot(current_aon));
printf("Woken up now @%dus since target\n", (int)diff);
if (diff < 0) {
printf("WARNING: Woke up too soon - is this within the resolution of the aon timer?\n");
}
printf("5 second pause to prove timer still running\n");
busy_wait_ms(5000);
#if !PICO_RP2040
printf("Going DORMANT for 5 seconds via AON TIMER\n");
// todo, ah; we should start the aon timer; still have to decide what to do about keeping them in sync
@ -77,7 +58,6 @@ int main() {
low_power_dormant_until_aon_timer(wakeup_time, DORMANT_CLOCK_SOURCE_LPOSC, XOSC_KHZ * 1000,
0, // gpio pin (unused with powman)
NULL);
low_power_sleep_until_aon_timer(wakeup_time, NULL);
diff = absolute_time_diff_us(get_absolute_time(), wakeup_time);
// need to use the AON timer for checking time, since the other timer is unclocked
diff = absolute_time_diff_us(wakeup_time, get_absolute_time());
@ -94,6 +74,7 @@ int main() {
}
printf("Final 5 second pause to prove timer still running\n");
busy_wait_ms(5000);
#endif
printf("SUCCESS\n");