From 3ea78558e12c234adba6a19bae36c214ad9d73f6 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Sat, 18 Jan 2025 01:58:32 +1300 Subject: [PATCH 1/4] Reattempt failed flash operations --- .../InternalFileSytem/src/flash/flash_nrf5x.c | 96 ++++++++++++------- 1 file changed, 64 insertions(+), 32 deletions(-) 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; From 630668a071e7a243689f93254c084a9162414f4c Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 21 Jan 2025 03:28:54 +1300 Subject: [PATCH 2/4] Refactor for maintainability --- .../InternalFileSytem/src/flash/flash_nrf5x.c | 113 +++++++++++------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c index cfc0cd3e9..5ff090996 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,14 +45,14 @@ extern uint32_t __flash_arduino_start[]; // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ static SemaphoreHandle_t _sem = NULL; -static bool _flash_op_failed = false; +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_failed = (event == NRF_EVT_FLASH_OPERATION_ERROR); + _flash_op_result = event; // Signal to fal_erase or fal_program that our async flash op is now complete xSemaphoreGive(_sem); @@ -61,29 +62,35 @@ void flash_nrf5x_event_cb (uint32_t event) // 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 - } +// 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; - // If unspecified error - if (op_result != NRF_SUCCESS) - return true; // Retry + // Operation was queued successfully + if (initial_result == NRF_SUCCESS) { - // 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) { + // Wait for result via callback xSemaphoreTake(_sem, portMAX_DELAY); - if (_flash_op_failed) - return true; // Retry + + // 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); } - - // Success - return false; + + return result; } // Flash Abstraction Layer @@ -151,19 +158,24 @@ static bool fal_erase (uint32_t addr) uint8_t sd_en = 0; (void) sd_softdevice_is_enabled(&sd_en); - // 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 + // 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); + + 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); } - return true; // Success + 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) { - // Check if soft device is enabled - // If yes, flash operations are async, so we need to wait for the callback to give the semaphore + // wait for async event if SD is enabled uint8_t sd_en = 0; (void) sd_softdevice_is_enabled(&sd_en); @@ -173,26 +185,39 @@ 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 - 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 + // 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); } -#else + VERIFY_STATUS(err, 0); // Return 0 if all retries fail - // 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 +#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); - // 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 + 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 #endif return len; From facf938c74e8b349be18be698ca09c806105b350 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 21 Jan 2025 23:24:54 +1300 Subject: [PATCH 3/4] braces --- .../InternalFileSytem/src/flash/flash_nrf5x.c | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c index 5ff090996..39cb94a88 100644 --- a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c +++ b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c @@ -64,7 +64,8 @@ void flash_nrf5x_event_cb (uint32_t event) // 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) { +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; @@ -76,18 +77,21 @@ static uint32_t wait_for_async_flash_op_completion(uint32_t initial_result) { xSemaphoreTake(_sem, portMAX_DELAY); // If completed successfully - if (_flash_op_result == NRF_EVT_FLASH_OPERATION_SUCCESS) - result = NRF_SUCCESS; + 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; + 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); + else { + assert(false); + } } return result; @@ -147,8 +151,7 @@ bool flash_nrf5x_erase(uint32_t addr) static bool fal_erase (uint32_t addr) { // Init semaphore for first call - if ( _sem == NULL ) - { + if ( _sem == NULL ) { _sem = xSemaphoreCreateBinary(); VERIFY(_sem); } @@ -164,9 +167,15 @@ static bool fal_erase (uint32_t addr) 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 (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 @@ -190,9 +199,15 @@ static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) 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); + 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 @@ -202,9 +217,15 @@ static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) 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); + 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 @@ -213,9 +234,15 @@ static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len) 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); + 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 #endif From 275da60c803b438d008159dc132d11be3f30db90 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Thu, 13 Feb 2025 20:55:10 +1300 Subject: [PATCH 4/4] Apply patch from hathach https://github.com/adafruit/Adafruit_nRF52_Arduino/pull/838#pullrequestreview-2601718986 --- .../InternalFileSytem/src/flash/flash_nrf5x.c | 184 +++++------------- 1 file changed, 54 insertions(+), 130 deletions(-) diff --git a/libraries/InternalFileSytem/src/flash/flash_nrf5x.c b/libraries/InternalFileSytem/src/flash/flash_nrf5x.c index 39cb94a88..5ee730259 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; - - // 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 +// 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) { - 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); + 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); } - VERIFY_STATUS(err, 0); // Return 0 if all retries fail + 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 - // 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); + // 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. + 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 + dst += len/2; + src += len/2; + VERIFY(fal_sub_program(dst, src, len/2), 0); // 2nd half #endif return len;