diff --git a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c index f9fac1fdf..cfc0cd3e9 100644 --- a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c +++ b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c @@ -44,11 +44,46 @@ extern uint32_t __flash_arduino_start[]; // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ static SemaphoreHandle_t _sem = NULL; +static bool _flash_op_failed = false; 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_failed = (event == NRF_EVT_FLASH_OPERATION_ERROR); + + // 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 + +// Check whether a flash operation was successful, or should be repeated +static bool retry_flash_op (uint32_t op_result, bool sd_enabled) { + // If busy + if (op_result == NRF_ERROR_BUSY) { + delay(1); + return true; // Retry + } + + // If unspecified error + if (op_result != NRF_SUCCESS) + return true; // Retry + + // If the soft device is enabled, flash operations run async + // The callback (flash_nrf5x_event_cb) will give semaphore when the flash operation is complete + // The callback also checks for NRF_EVT_FLASH_OPERATION_ERROR, which is not available to us otherwise + if (sd_enabled) { + xSemaphoreTake(_sem, portMAX_DELAY); + if (_flash_op_failed) + return true; // Retry + } + + // Success + return false; } // Flash Abstraction Layer @@ -107,30 +142,28 @@ static bool fal_erase (uint32_t addr) // Init semaphore for first call if ( _sem == NULL ) { - _sem = xSemaphoreCreateCounting(10, 0); + _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); - - return true; + // Make multiple attempts to erase + uint8_t attempt = 0; + while (retry_flash_op(sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE), sd_en)) { + if (++attempt > MAX_RETRY) + return false; // Failure + } + return true; // Success } static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) { - // 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); @@ -140,27 +173,26 @@ 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); + uint8_t attempt = 0; + while (retry_flash_op(sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4), sd_en)) { + if (++attempt > MAX_RETRY) + return 0; // Failure } - VERIFY_STATUS(err, 0); - - 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); + + // First part of block + uint8_t attempt = 0; + while (retry_flash_op(sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/8), sd_en)) { + if (++attempt > MAX_RETRY) + return 0; // Failure } - VERIFY_STATUS(err, 0); - if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY); - while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8)) ) - { - delay(1); + // Second part of block + attempt = 0; + while (retry_flash_op(sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8), sd_en)) { + if (++attempt > MAX_RETRY) + return 0; // Failure } - VERIFY_STATUS(err, 0); - if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY); #endif return len;