diff --git a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c index 39cb94a8..5ee73025 100644 --- a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c +++ b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c @@ -28,8 +28,6 @@ #include "nrf_soc.h" #include "delay.h" #include "rtos.h" -#include "assert.h" - #ifdef NRF52840_XXAA #define BOOTLOADER_ADDR 0xF4000 @@ -37,6 +35,9 @@ #define BOOTLOADER_ADDR 0x74000 #endif +// How many retry attempts when performing flash operations +#define MAX_RETRY 20 + // defined in linker script extern uint32_t __flash_arduino_start[]; //extern uint32_t __flash_arduino_end[]; @@ -47,56 +48,6 @@ extern uint32_t __flash_arduino_start[]; static SemaphoreHandle_t _sem = NULL; static uint32_t _flash_op_result = NRF_EVT_FLASH_OPERATION_SUCCESS; -void flash_nrf5x_event_cb (uint32_t event) -{ - if ( _sem ) { - // Record the result, for consumption by fal_erase or fal_program - // Used to reattempt failed operations - _flash_op_result = event; - - // Signal to fal_erase or fal_program that our async flash op is now complete - xSemaphoreGive(_sem); - } -} - -// How many retry attempts when performing flash operations -#define MAX_RETRY 20 - -// When soft device is enabled, flash ops are async -// Eventual success is reported via callback, which we await -static uint32_t wait_for_async_flash_op_completion(uint32_t initial_result) -{ - // If initial result not NRF_SUCCESS, no need to await callback - // We will pass the initial result (failure) straight through - int32_t result = initial_result; - - // Operation was queued successfully - if (initial_result == NRF_SUCCESS) { - - // Wait for result via callback - xSemaphoreTake(_sem, portMAX_DELAY); - - // If completed successfully - if (_flash_op_result == NRF_EVT_FLASH_OPERATION_SUCCESS) { - result = NRF_SUCCESS; - } - - // If general failure. - // The comment on NRF_EVT_FLASH_OPERATION_ERROR describes it as a timeout, - // so we're using a similar error when translating from NRF_SOC_EVTS type to the global NRF52 error defines - else if (_flash_op_result == NRF_EVT_FLASH_OPERATION_ERROR) { - result = NRF_ERROR_TIMEOUT; - } - - // If this assert triggers, we need to implement a new NRF_SOC_EVTS value - else { - assert(false); - } - } - - return result; -} - // Flash Abstraction Layer static bool fal_erase (uint32_t addr); static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len); @@ -116,6 +67,31 @@ static flash_cache_t _cache = .cache_buf = _cache_buffer }; +void flash_nrf5x_event_cb (uint32_t event) { + if ( _sem ) { + // Record the result, for consumption by fal_erase or fal_program + // Used to reattempt failed operations + _flash_op_result = event; + + // Signal to fal_erase or fal_program that our async flash op is now complete + xSemaphoreGive(_sem); + } +} + +// When soft device is enabled, flash ops are async +// Eventual success is reported via callback, which we await +static uint32_t wait_for_async_flash_op_completion(void) { + uint8_t sd_en = 0; + (void) sd_softdevice_is_enabled(&sd_en); + + if (sd_en) { + xSemaphoreTake(_sem, portMAX_DELAY); + return (_flash_op_result == NRF_EVT_FLASH_OPERATION_SUCCESS) ? NRF_SUCCESS : NRF_ERROR_TIMEOUT; + } else { + return NRF_SUCCESS; + } +} + //--------------------------------------------------------------------+ // Application API //--------------------------------------------------------------------+ @@ -156,95 +132,43 @@ static bool fal_erase (uint32_t addr) VERIFY(_sem); } - // Check if soft device is enabled - // If yes, flash operations are async, so we need to wait for the callback to give the semaphore - uint8_t sd_en = 0; - (void) sd_softdevice_is_enabled(&sd_en); - - // Erase the page - // Multiple attempts if needed - uint32_t err; + // Erase the page: Multiple attempts if needed for (uint8_t attempt = 0; attempt < MAX_RETRY; ++attempt) { - err = sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE); - - if (sd_en) { - err = wait_for_async_flash_op_completion(err); // Only async if soft device enabled - } - if (err == NRF_SUCCESS) { - break; - } - if (err == NRF_ERROR_BUSY) { - delay(1); + if (NRF_SUCCESS == sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE)) { + if (NRF_SUCCESS == wait_for_async_flash_op_completion()) { + return true; + } } + delay(1); } - VERIFY_STATUS(err, false); // Return false if all retries fail - - return true; // Successfully erased + return false; } -static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) -{ - // wait for async event if SD is enabled - uint8_t sd_en = 0; - (void) sd_softdevice_is_enabled(&sd_en); - - uint32_t err; +// helper for fal_program() +static bool fal_sub_program(uint32_t dst, void const * src, uint32_t len) { + for (uint8_t attempt = 0; attempt < MAX_RETRY; ++attempt) { + if (NRF_SUCCESS == sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4)) { + if (NRF_SUCCESS == wait_for_async_flash_op_completion()) { + return true; + } + } + delay(1); + } + return false; +} +static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) { +#if NRF52832_XXAA + VERIFY(fal_sub_program(dst, src, len), 0); +#else // Somehow S140 v6.1.1 assert an error when writing a whole page // https://devzone.nordicsemi.com/f/nordic-q-a/40088/sd_flash_write-cause-nrf_fault_id_sd_assert // Workaround: write half page at a time. -#if NRF52832_XXAA - // Write the page - // Multiple attempts, if needed - for (uint8_t attempt = 0; attempt < MAX_RETRY; ++attempt) { - err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4); + VERIFY(fal_sub_program(dst, src, len/2), 0); // 1st half - if (sd_en) { - err = wait_for_async_flash_op_completion(err); // Only async if soft device enabled - } - if (err == NRF_SUCCESS) { - break; - } - if (err == NRF_ERROR_BUSY) { - delay(1); - } - } - VERIFY_STATUS(err, 0); // Return 0 if all retries fail - -#else - // Write first part of page - // Multiple attempts, if needed - for (uint8_t attempt = 0; attempt < MAX_RETRY; ++attempt) { - err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/8); - - if (sd_en) { - err = wait_for_async_flash_op_completion(err); // Only async if soft device enabled - } - if (err == NRF_SUCCESS) { - break; - } - if (err == NRF_ERROR_BUSY) { - delay(1); - } - } - VERIFY_STATUS(err, 0); // Return 0 if all retries fail - - // Write second part of page - // Multiple attempts, if needed - for (uint8_t attempt = 0; attempt < MAX_RETRY; ++attempt) { - err = sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8); - - if (sd_en) { - err = wait_for_async_flash_op_completion(err); // Only async if soft device enabled - } - if (err == NRF_SUCCESS) { - break; - } - if (err == NRF_ERROR_BUSY) { - delay(1); - } - } - VERIFY_STATUS(err, 0); // Return 0 if all retries fail + dst += len/2; + src += len/2; + VERIFY(fal_sub_program(dst, src, len/2), 0); // 2nd half #endif return len;