-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add new pico_flash library with flash_safe_execute(func) #1412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
pico_add_library(pico_flash) | ||
|
||
target_sources(pico_flash INTERFACE | ||
${CMAKE_CURRENT_LIST_DIR}/flash.c | ||
) | ||
|
||
target_include_directories(pico_flash_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) | ||
|
||
# just include multicore headers, as we don't want to pull in the lib if it isn't pulled in already | ||
target_link_libraries(pico_flash INTERFACE pico_multicore_headers) | ||
|
||
pico_mirrored_target_link_libraries(pico_flash INTERFACE pico_time hardware_sync) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
/* | ||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd. | ||
* | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
#include "pico/flash.h" | ||
#include "hardware/exception.h" | ||
#include "hardware/sync.h" | ||
#if PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT | ||
#include "pico/multicore.h" | ||
#endif | ||
#if PICO_FLASH_SAFE_EXECUTE_SUPPORT_FREERTOS_SMP | ||
#include "FreeRTOS.h" | ||
#include "task.h" | ||
// now we have FreeRTOS header we can check core count... we can only use FreeRTOS SMP mechanism | ||
// with two cores | ||
#if configNUM_CORES == 2 | ||
#if configUSE_CORE_AFFINITY | ||
#define PICO_FLASH_SAFE_EXECUTE_USE_FREERTOS_SMP 1 | ||
#else | ||
#error configUSE_CORE_AFFINITY is required for PICO_FLASH_SAFE_EXECUTE_SUPPORT_FREERTOS_SMP | ||
#endif | ||
#endif | ||
#endif | ||
|
||
// There are multiple scenarios: | ||
// | ||
// 1. No use of core 1 - we just want to disable IRQs and not wait on core 1 to acquiesce | ||
// 2. Regular pico_multicore - we need to use multicore lockout. | ||
// 3. FreeRTOS on core 0, no use of core 1 - we just want to disable IRQs | ||
// 4. FreeRTOS SMP on both cores - we need to schedule a high priority task on the other core to disable IRQs. | ||
// 5. FreeRTOS on one core, but application is using the other core. ** WE CANNOT SUPPORT THIS TODAY ** without | ||
// the equivalent PICO_FLASH_ASSUME_COREx_SAFE (i.e. the user mkaing sure the other core is fine) | ||
|
||
static bool default_core_init_deinit(bool init); | ||
static int default_enter_safe_zone_timeout_ms(uint32_t timeout_ms); | ||
static int default_exit_safe_zone_timeout_ms(uint32_t timeout_ms); | ||
|
||
// note the default methods are combined, rather than having a separate helper for | ||
// FreeRTOS, as we may support mixed multicore and non SMP FreeRTOS in the future | ||
|
||
static flash_safety_helper_t default_flash_safety_helper = { | ||
.core_init_deinit = default_core_init_deinit, | ||
.enter_safe_zone_timeout_ms = default_enter_safe_zone_timeout_ms, | ||
.exit_safe_zone_timeout_ms = default_exit_safe_zone_timeout_ms | ||
}; | ||
|
||
#if PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT | ||
// note that these are not reset by core reset, however for now, I think people resetting cores | ||
// and then doing this again without re-initializing pico_flash for that core, is probably | ||
// something we can live with breaking. | ||
static bool core_initialized[NUM_CORES]; | ||
#endif | ||
|
||
#if PICO_FLASH_SAFE_EXECUTE_USE_FREERTOS_SMP | ||
enum { | ||
FREERTOS_LOCKOUT_NONE = 0, | ||
FREERTOS_LOCKOUT_LOCKER_WAITING, | ||
FREERTOS_LOCKOUT_LOCKEE_READY, | ||
FREERTOS_LOCKOUT_LOCKER_DONE, | ||
FREERTOS_LOCKOUT_LOCKEE_DONE, | ||
}; | ||
// state for the lockout operation launched from the corresponding core | ||
static volatile uint8_t lockout_state[NUM_CORES]; | ||
#endif | ||
|
||
__attribute__((weak)) flash_safety_helper_t *get_flash_safety_helper(void) { | ||
return &default_flash_safety_helper; | ||
} | ||
|
||
bool flash_safe_execute_core_init(void) { | ||
flash_safety_helper_t *helper = get_flash_safety_helper(); | ||
return helper ? helper->core_init_deinit(true) : false; | ||
} | ||
|
||
bool flash_safe_execute_core_deinit(void) { | ||
flash_safety_helper_t *helper = get_flash_safety_helper(); | ||
return helper ? helper->core_init_deinit(false) : false; | ||
} | ||
|
||
int flash_safe_execute(void (*func)(void *), void *param, uint32_t enter_exit_timeout_ms) { | ||
flash_safety_helper_t *helper = get_flash_safety_helper(); | ||
if (!helper) return PICO_ERROR_NOT_PERMITTED; | ||
int rc = helper->enter_safe_zone_timeout_ms(enter_exit_timeout_ms); | ||
if (!rc) { | ||
func(param); | ||
rc = helper->exit_safe_zone_timeout_ms(enter_exit_timeout_ms); | ||
} | ||
return rc; | ||
} | ||
|
||
static bool default_core_init_deinit(__unused bool init) { | ||
#if PICO_FLASH_ASSUME_CORE0_SAFE | ||
if (!get_core_num()) return true; | ||
#endif | ||
#if PICO_FLASH_ASSUME_CORE1_SAFE | ||
if (get_core_num()) return true; | ||
#endif | ||
#if PICO_FLASH_SAFE_EXECUTE_USE_FREERTOS_SMP | ||
return true; | ||
#endif | ||
#if PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT | ||
if (!init) { | ||
return false; | ||
} | ||
multicore_lockout_victim_init(); | ||
core_initialized[get_core_num()] = init; | ||
#endif | ||
return true; | ||
} | ||
|
||
// irq_state for the lockout operation launched from the corresponding core | ||
static uint32_t irq_state[NUM_CORES]; | ||
|
||
static bool use_irq_only(void) { | ||
#if PICO_FLASH_ASSUME_CORE0_SAFE | ||
if (get_core_num()) return true; | ||
#endif | ||
#if PICO_FLASH_ASSUME_CORE1_SAFE | ||
if (!get_core_num()) return true; | ||
#endif | ||
return false; | ||
} | ||
|
||
#if PICO_FLASH_SAFE_EXECUTE_USE_FREERTOS_SMP | ||
static void __not_in_flash_func(flash_lockout_task)(__unused void *vother_core_num) { | ||
uint other_core_num = (uintptr_t)vother_core_num; | ||
while (lockout_state[other_core_num] != FREERTOS_LOCKOUT_LOCKER_WAITING) { | ||
__wfe(); // we don't bother to try to let lower priority tasks run | ||
} | ||
uint32_t save = save_and_disable_interrupts(); | ||
lockout_state[other_core_num] = FREERTOS_LOCKOUT_LOCKEE_READY; | ||
__sev(); | ||
while (lockout_state[other_core_num] == FREERTOS_LOCKOUT_LOCKEE_READY) { | ||
__wfe(); // we don't bother to try to let lower priority tasks run | ||
} | ||
restore_interrupts(save); | ||
lockout_state[other_core_num] = FREERTOS_LOCKOUT_LOCKEE_DONE; | ||
__sev(); | ||
// bye bye | ||
vTaskDelete(NULL); | ||
} | ||
#endif | ||
|
||
static int default_enter_safe_zone_timeout_ms(__unused uint32_t timeout_ms) { | ||
int rc = PICO_OK; | ||
if (!use_irq_only()) { | ||
#if PICO_FLASH_SAFE_EXECUTE_USE_FREERTOS_SMP | ||
// Note that whilst taskENTER_CRITICAL sounds promising (and on non SMP it disabled IRQs), on SMP | ||
// it only prevents the other core from also entering a critical section. | ||
// Therefore, we must do our own handshake which starts a task on the other core and have it disable interrupts | ||
uint core_num = get_core_num(); | ||
// create at low priority | ||
TaskHandle_t task_handle; | ||
if (pdPASS != xTaskCreate(flash_lockout_task, "flash lockout", configMINIMAL_STACK_SIZE, (void *)core_num, 0, &task_handle)) { | ||
return PICO_ERROR_INSUFFICIENT_RESOURCES; | ||
} | ||
lockout_state[core_num] = FREERTOS_LOCKOUT_LOCKER_WAITING; | ||
__sev(); | ||
// bind to other core | ||
vTaskCoreAffinitySet(task_handle, 1u << (core_num ^ 1)); | ||
// and make it super high priority | ||
vTaskPrioritySet(task_handle, configMAX_PRIORITIES -1); | ||
absolute_time_t until = make_timeout_time_ms(timeout_ms); | ||
while (lockout_state[core_num] != FREERTOS_LOCKOUT_LOCKEE_READY && !time_reached(until)) { | ||
__wfe(); // we don't bother to try to let lower priority tasks run | ||
} | ||
if (lockout_state[core_num] != FREERTOS_LOCKOUT_LOCKEE_READY) { | ||
lockout_state[core_num] = FREERTOS_LOCKOUT_LOCKER_DONE; | ||
rc = PICO_ERROR_TIMEOUT; | ||
} | ||
// todo we may get preempted here, but I think that is OK unless what is pre-empts requires | ||
// the other core to be running. | ||
#elif PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT | ||
// we cannot mix multicore_lockout and FreeRTOS as they both use the multicore FIFO... | ||
// the user, will have to roll their own mechanism in this case. | ||
#if LIB_FREERTOS_KERNEL | ||
#if PICO_FLASH_ASSERT_ON_UNSAFE | ||
assert(false); // we expect the other core to have been initialized via flash_safe_execute_core_init() | ||
// unless PICO_FLASH_ASSUME_COREX_SAFE is set | ||
#endif | ||
rc = PICO_ERROR_NOT_PERMITTED; | ||
#else // !LIB_FREERTOS_KERNEL | ||
if (core_initialized[get_core_num()^1]) { | ||
if (!multicore_lockout_start_timeout_us(timeout_ms * 1000ull)) { | ||
rc = PICO_ERROR_TIMEOUT; | ||
} | ||
} else { | ||
#if PICO_FLASH_ASSERT_ON_UNSAFE | ||
assert(false); // we expect the other core to have been initialized via flash_safe_execute_core_init() | ||
// unless PICO_FLASH_ASSUME_COREX_SAFE is set | ||
#endif | ||
rc = PICO_ERROR_NOT_PERMITTED; | ||
} | ||
#endif // !LIB_FREERTOS_KERNEL | ||
#else | ||
// no support for making other core safe provided, so fall through to irq | ||
// note this is the case for a regular single core program | ||
#endif | ||
} | ||
if (rc == PICO_OK) { | ||
// we always want to disable IRQs on our core | ||
irq_state[get_core_num()] = save_and_disable_interrupts(); | ||
} | ||
return rc; | ||
} | ||
|
||
static int default_exit_safe_zone_timeout_ms(__unused uint32_t timeout_ms) { | ||
// assume if we're exiting we're called then entry happened successfully | ||
restore_interrupts(irq_state[get_core_num()]); | ||
if (!use_irq_only()) { | ||
#if PICO_FLASH_SAFE_EXECUTE_USE_FREERTOS_SMP | ||
uint core_num = get_core_num(); | ||
lockout_state[core_num] = FREERTOS_LOCKOUT_LOCKER_DONE; | ||
__sev(); | ||
absolute_time_t until = make_timeout_time_ms(timeout_ms); | ||
while (lockout_state[core_num] != FREERTOS_LOCKOUT_LOCKEE_DONE && !time_reached(until)) { | ||
__wfe(); // we don't bother to try to let lower priority tasks run | ||
} | ||
if (lockout_state[core_num] != FREERTOS_LOCKOUT_LOCKEE_DONE) { | ||
return PICO_ERROR_TIMEOUT; | ||
} | ||
#elif PICO_FLASH_SAFE_EXECUTE_PICO_SUPPORT_MULTICORE_LOCKOUT | ||
return multicore_lockout_end_timeout_us(timeout_ms * 1000ull) ? PICO_OK : PICO_ERROR_TIMEOUT; | ||
#endif | ||
} | ||
return PICO_OK; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.