From 42f33036bd86cc4bafdc84c82fb242ff8bdaff23 Mon Sep 17 00:00:00 2001 From: William Vinnicombe Date: Mon, 1 Sep 2025 10:28:31 +0100 Subject: [PATCH] Add skips of data_copy in arm crt0 --- .../hardware_powman/include/hardware/powman.h | 11 +- src/rp2_common/pico_crt0/crt0.S | 62 +++++++++ .../pico_low_power/include/pico/low_power.h | 2 +- src/rp2_common/pico_low_power/low_power.c | 11 +- test/hello_sleep/hello_sleep.c | 126 +++++++++++++++++- 5 files changed, 196 insertions(+), 16 deletions(-) diff --git a/src/rp2_common/hardware_powman/include/hardware/powman.h b/src/rp2_common/hardware_powman/include/hardware/powman.h index 0d0cc1c2..659ec6c2 100644 --- a/src/rp2_common/hardware_powman/include/hardware/powman.h +++ b/src/rp2_common/hardware_powman/include/hardware/powman.h @@ -205,14 +205,13 @@ static inline bool pstate_bitset_none_set(pstate_bitset_t *domains) { return bitset_equal(&domains->bitset, &none.bitset); } -static inline pstate_bitset_t pstate_bitset_from_powman_power_state(powman_power_state pstate) { - pstate_bitset_t bitset; - bitset_init(&bitset, pstate_bitset_t, POWMAN_POWER_DOMAIN_COUNT, pstate); - return bitset; +static inline pstate_bitset_t *pstate_bitset_from_powman_power_state(pstate_bitset_t *domains, powman_power_state pstate) { + bitset_write_word(&domains->bitset, 0, pstate); + return domains; } -static inline powman_power_state pstate_bitset_to_powman_power_state(pstate_bitset_t *pstate) { - return pstate->bitset.words[0]; +static inline powman_power_state pstate_bitset_to_powman_power_state(pstate_bitset_t *domains) { + return bitset_read_word(&domains->bitset, 0); } /*! \brief Get the current power state diff --git a/src/rp2_common/pico_crt0/crt0.S b/src/rp2_common/pico_crt0/crt0.S index ea3b99a5..2193d5fa 100644 --- a/src/rp2_common/pico_crt0/crt0.S +++ b/src/rp2_common/pico_crt0/crt0.S @@ -15,6 +15,10 @@ #include "boot/picobin.h" #include "pico/bootrom.h" +#if !PICO_RP2040 +#include "hardware/regs/powman.h" +#endif + // PICO_CONFIG: PICO_CRT0_NEAR_CALLS, Whether calls from crt0 into the binary are near (<16M away) - ignored for PICO_COPY_TO_RAM, default=0, type=bool, group=pico_crt0 #ifndef PICO_CRT0_NEAR_CALLS #define PICO_CRT0_NEAR_CALLS 0 @@ -478,6 +482,11 @@ _call_xip_setup: // In a NO_FLASH binary, don't perform .data etc copy, since it's loaded // in-place by the SRAM load. Still need to clear .bss #if !PICO_NO_FLASH +#if LIB_PICO_LOW_POWER && !PICO_RP2040 + // Load previous power state into r6 + ldr r6, =(POWMAN_BASE+POWMAN_SCRATCH6_OFFSET) + ldr r6, [r6] +#endif adr r4, data_cpy_table // assume there is at least one entry @@ -485,6 +494,11 @@ _call_xip_setup: ldmia r4!, {r1-r3} cmp r1, #0 beq 2f +#if LIB_PICO_LOW_POWER && !PICO_RP2040 + bl data_cpy_check + cmp r0, #1 + beq 1b +#endif bl data_cpy b 1b 2: @@ -530,6 +544,54 @@ data_cpy: cmp r2, r3 blo data_cpy_loop bx lr + +#if LIB_PICO_LOW_POWER && !PICO_RP2040 +data_cpy_check: + // uses r5 and r7 as scratch + // uses the powman_power_state in r6 + // returns in r0 + mov r0, #0 + // First check start of copy (r2) + mov r7, r2 +check_r7: + ldr r5, =(SRAM_BASE >> 16) + cmp r5, r7, lsr #16 + bgt check_xip_sram + ldr r5, =(SRAM4_BASE >> 16) + cmp r5, r7, lsr #16 + bgt check_sram0 + b check_sram1 +check_xip_sram: + mov r5, #(1 << 2) // POWMAN_POWER_DOMAIN_XIP_CACHE + and r5, r6 + cmp r5, #(1 << 2) + beq skip + b noskip +check_sram0: + mov r5, #(1 << 1) // POWMAN_POWER_DOMAIN_SRAM_BANK0 + and r5, r6 + cmp r5, #(1 << 1) + beq skip + b noskip +check_sram1: + mov r5, #(1 << 0) // POWMAN_POWER_DOMAIN_SRAM_BANK1 + and r5, r6 + cmp r5, #(1 << 0) + beq skip + b noskip +skip: + cmp r0, #1 + bne check_end + bx lr +check_end: + add r0, #1 + // If start was skipped, check end of copy + mov r7, r3 + b check_r7 +noskip: + mov r0, #0 + bx lr +#endif #endif // Note the data copy table is still included for NO_FLASH builds, even though diff --git a/src/rp2_common/pico_low_power/include/pico/low_power.h b/src/rp2_common/pico_low_power/include/pico/low_power.h index ad565b88..5ac06857 100644 --- a/src/rp2_common/pico_low_power/include/pico/low_power.h +++ b/src/rp2_common/pico_low_power/include/pico/low_power.h @@ -122,7 +122,7 @@ int low_power_pstate_until_pin_state(uint gpio_pin, bool edge, bool high, pstate // Go to a pstate // Doesn't support powering down switched core domain int low_power_pstate_set(pstate_bitset_t *pstate); -pstate_bitset_t low_power_pstate_get(void); +pstate_bitset_t *low_power_pstate_get(pstate_bitset_t *pstate); #endif #ifdef __cplusplus diff --git a/src/rp2_common/pico_low_power/low_power.c b/src/rp2_common/pico_low_power/low_power.c index f51c51a8..91055757 100644 --- a/src/rp2_common/pico_low_power/low_power.c +++ b/src/rp2_common/pico_low_power/low_power.c @@ -500,15 +500,17 @@ int low_power_pstate_set(pstate_bitset_t *pstate) { return powman_set_power_state(pstate_bitset_to_powman_power_state(pstate)); } -pstate_bitset_t low_power_pstate_get(void) { - return pstate_bitset_from_powman_power_state(powman_get_power_state()); +pstate_bitset_t *low_power_pstate_get(pstate_bitset_t *pstate) { + pstate_bitset_from_powman_power_state(pstate, powman_get_power_state()); + return pstate; } int low_power_go_pstate(pstate_bitset_t *pstate, low_power_pstate_resume_func resume_func) { prepare_for_pstate_change(); // Configure the wakeup state - pstate_bitset_t current_pstate = low_power_pstate_get(); + pstate_bitset_t current_pstate = pstate_bitset_none(); + low_power_pstate_get(¤t_pstate); bool valid_state = powman_configure_wakeup_state(pstate_bitset_to_powman_power_state(pstate), pstate_bitset_to_powman_power_state(¤t_pstate)); if (!valid_state) { return PICO_ERROR_INVALID_STATE; @@ -556,7 +558,8 @@ void __weak runtime_init_low_power_reboot_check(void) { if (powman_hw->chip_reset & POWMAN_CHIP_RESET_HAD_SWCORE_PD_BITS) { // we came from powman reboot, so execute the resume function if (powman_hw->scratch[7]) { - pstate_bitset_t pstate = pstate_bitset_from_powman_power_state(powman_hw->scratch[6]); + pstate_bitset_t pstate = pstate_bitset_none(); + pstate_bitset_from_powman_power_state(&pstate, powman_hw->scratch[6]); ((low_power_pstate_resume_func)powman_hw->scratch[7])(&pstate); // clear the scratch registers powman_hw->scratch[6] = 0; diff --git a/test/hello_sleep/hello_sleep.c b/test/hello_sleep/hello_sleep.c index 2a8f1404..40d27263 100644 --- a/test/hello_sleep/hello_sleep.c +++ b/test/hello_sleep/hello_sleep.c @@ -30,9 +30,13 @@ bool repeater(repeating_timer_t *timer) { static bool came_from_pstate = false; static char powman_last_pwrup[100]; static char powman_last_pstate[100]; +static char powman_last_pstate_val[10]; void pstate_resume_func(pstate_bitset_t *pstate) { came_from_pstate = true; + memset(powman_last_pwrup, 0, sizeof(powman_last_pwrup)); + memset(powman_last_pstate, 0, sizeof(powman_last_pstate)); + memset(powman_last_pstate_val, 0, sizeof(powman_last_pstate_val)); switch (powman_hw->last_swcore_pwrup) { // 0 = chip reset, for the source of the last reset see case 1 << 0: strcpy(powman_last_pwrup, "Chip reset"); break; @@ -50,6 +54,8 @@ void pstate_resume_func(pstate_bitset_t *pstate) { if (pstate_bitset_is_set(pstate, POWMAN_POWER_DOMAIN_SRAM_BANK1)) strcat(powman_last_pstate, "SRAM_BANK1, "); if (pstate_bitset_none_set(pstate)) strcat(powman_last_pstate, "NONE, "); } + +static volatile int my_number = 12345; #endif int main() { @@ -73,7 +79,13 @@ int main() { if (came_from_pstate) { printf("Came from powerup %s with (%s) memory kept on - skipping to end\n", powman_last_pwrup, powman_last_pstate); - goto post_pstate_timer; + if (strstr(powman_last_pstate, "SRAM_BANK0") != NULL) { + goto post_pstate_sram0_on; + } else if (strstr(powman_last_pstate, "SRAM_BANK1") != NULL) { + goto post_pstate_sram1_on; + } else { + goto post_pstate_sram_off; + } } pstate_bitset_t pstate; @@ -88,6 +100,9 @@ int main() { struct timespec ts; int ret; + + + // exclusive sleep printf("Going to sleep for %d seconds via TIMER\n", SLEEP_TIME_S); start_time = get_absolute_time(); @@ -102,6 +117,9 @@ int main() { printf("Doing %d second pause to prove timer running\n", SLEEP_TIME_S); busy_wait_ms(SLEEP_TIME_MS); + + + // non-exclusive sleep printf("Going to non-exclusive sleep for %d seconds via TIMER\n", SLEEP_TIME_S); start_time = get_absolute_time(); @@ -116,6 +134,9 @@ int main() { printf("Doing %d second pause to prove timer running\n", SLEEP_TIME_S); busy_wait_ms(SLEEP_TIME_MS); + + + // dormant printf("Going DORMANT for %d seconds via AON TIMER\n", SLEEP_TIME_S); // todo, ah; we should start the aon timer; still have to decide what to do about keeping them in sync @@ -146,8 +167,98 @@ int main() { printf("Doing %d second pause to prove timer running\n", SLEEP_TIME_S); busy_wait_ms(SLEEP_TIME_MS); + + + // powman states #if !PICO_RP2040 - printf("Going to PSTATE for %d seconds\n", SLEEP_TIME_S); + // pstate with sram0 on + printf("Going to PSTATE with SRAM0 on for %d seconds\n", SLEEP_TIME_S); + + if (my_number != 12345) { + printf("ERROR: my_number is %d not 12345 - initialisation issue?\n", my_number); + return -1; + } + my_number = 67890; + + start_time = aon_timer_get_absolute_time(); + + wakeup_time = delayed_by_ms(start_time, SLEEP_TIME_MS); + powman_hw->scratch[0] = to_us_since_boot(wakeup_time) & 0xFFFFFFFF; + powman_hw->scratch[1] = to_us_since_boot(wakeup_time) >> 32; + pstate = pstate_bitset_none(); + pstate_bitset_add(&pstate, POWMAN_POWER_DOMAIN_SRAM_BANK0); + printf("pstate: %08x\n", pstate_bitset_to_powman_power_state(&pstate)); + ret = low_power_pstate_until_aon_timer(wakeup_time, &pstate, pstate_resume_func); + + printf("%d low_power_pstate_until_aon_timer returned\n", ret); + while (true) { + printf("Waiting\n"); + busy_wait_ms(1000); + } + +post_pstate_sram0_on: + // restore from scratch + wakeup_time = from_us_since_boot((uint64_t)powman_hw->scratch[1] << 32 | (uint64_t)powman_hw->scratch[0]); + diff = absolute_time_diff_us(wakeup_time, aon_timer_get_absolute_time()); + 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"); + } + + if (my_number != 67890) { + printf("ERROR: my_number is %d not 67890 - SRAM has been re-loaded\n", my_number); + return -1; + } else { + printf("my_number in sram: %d\n", my_number); + } + + printf("Doing %d second pause to prove timer running\n", SLEEP_TIME_S); + busy_wait_ms(SLEEP_TIME_MS); + + + + // pstate with sram1 on + printf("Going to PSTATE with SRAM1 on for %d seconds\n", SLEEP_TIME_S); + + start_time = aon_timer_get_absolute_time(); + + wakeup_time = delayed_by_ms(start_time, SLEEP_TIME_MS); + powman_hw->scratch[0] = to_us_since_boot(wakeup_time) & 0xFFFFFFFF; + powman_hw->scratch[1] = to_us_since_boot(wakeup_time) >> 32; + pstate = pstate_bitset_none(); + pstate_bitset_add(&pstate, POWMAN_POWER_DOMAIN_SRAM_BANK1); + printf("pstate: %08x\n", pstate_bitset_to_powman_power_state(&pstate)); + ret = low_power_pstate_until_aon_timer(wakeup_time, &pstate, pstate_resume_func); + + printf("%d low_power_pstate_until_aon_timer returned\n", ret); + while (true) { + printf("Waiting\n"); + busy_wait_ms(1000); + } + +post_pstate_sram1_on: + // restore from scratch + wakeup_time = from_us_since_boot((uint64_t)powman_hw->scratch[1] << 32 | (uint64_t)powman_hw->scratch[0]); + diff = absolute_time_diff_us(wakeup_time, aon_timer_get_absolute_time()); + 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"); + } + + if (my_number != 12345) { + printf("ERROR: my_number is %d not 12345 - SRAM has not been re-loaded\n", my_number); + return -1; + } else { + printf("my_number in sram: %d\n", my_number); + } + + printf("Doing %d second pause to prove timer running\n", SLEEP_TIME_S); + busy_wait_ms(SLEEP_TIME_MS); + + + + // pstate with sram off + printf("Going to PSTATE with SRAM off for %d seconds\n", SLEEP_TIME_S); start_time = aon_timer_get_absolute_time(); @@ -163,17 +274,22 @@ int main() { busy_wait_ms(1000); } -post_pstate_timer: - +post_pstate_sram_off: // restore from scratch wakeup_time = from_us_since_boot((uint64_t)powman_hw->scratch[1] << 32 | (uint64_t)powman_hw->scratch[0]); - diff = absolute_time_diff_us(wakeup_time, aon_timer_get_absolute_time()); 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"); } + if (my_number != 12345) { + printf("ERROR: my_number is not 12345 - SRAM has not been re-loaded\n"); + return -1; + } else { + printf("my_number in sram: %d\n", my_number); + } + printf("Doing %d second pause to prove timer running\n", SLEEP_TIME_S); busy_wait_ms(SLEEP_TIME_MS); #endif