diff --git a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c index f9fac1fdf..39cb94a88 100644 --- a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c +++ b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c @@ -28,6 +28,7 @@ #include "nrf_soc.h" #include "delay.h" #include "rtos.h" +#include "assert.h" #ifdef NRF52840_XXAA @@ -44,11 +45,56 @@ extern uint32_t __flash_arduino_start[]; // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ 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 (event != NRF_EVT_FLASH_OPERATION_SUCCESS) LOG_LV1("IFLASH", "Flash op Failed"); - if ( _sem ) xSemaphoreGive(_sem); + 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 @@ -105,27 +151,35 @@ bool flash_nrf5x_erase(uint32_t addr) static bool fal_erase (uint32_t addr) { // Init semaphore for first call - if ( _sem == NULL ) - { - _sem = xSemaphoreCreateCounting(10, 0); + if ( _sem == NULL ) { + _sem = xSemaphoreCreateBinary(); VERIFY(_sem); } - // retry if busy - uint32_t err; - while ( NRF_ERROR_BUSY == (err = sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE)) ) - { - delay(1); - } - VERIFY_STATUS(err, false); - - // wait for async event if SD is enabled + // 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); - if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY); + // Erase the page + // Multiple attempts if needed + uint32_t err; + for (uint8_t attempt = 0; attempt < MAX_RETRY; ++attempt) { + err = sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE); - return true; + 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, false); // Return false if all retries fail + + return true; // Successfully erased } static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) @@ -140,27 +194,57 @@ static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) // 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 - while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4)) ) - { - delay(1); + // 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); + + 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); + VERIFY_STATUS(err, 0); // Return 0 if all retries fail - if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY); #else - while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/8)) ) - { - delay(1); + // 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); - if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY); + 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); - while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8)) ) - { - delay(1); + 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); - if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY); + VERIFY_STATUS(err, 0); // Return 0 if all retries fail #endif return len;