Add skips of data_copy in arm crt0

This commit is contained in:
William Vinnicombe 2025-09-01 10:28:31 +01:00
parent d9cf06ba33
commit 42f33036bd
5 changed files with 196 additions and 16 deletions

View file

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

View file

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

View file

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

View file

@ -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(&current_pstate);
bool valid_state = powman_configure_wakeup_state(pstate_bitset_to_powman_power_state(pstate), pstate_bitset_to_powman_power_state(&current_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;

View file

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