Skip to content

Commit

Permalink
Reattempt failed flash operations
Browse files Browse the repository at this point in the history
  • Loading branch information
todd-herbert committed Jan 17, 2025
1 parent 4dcfa3b commit 3ea7855
Showing 1 changed file with 64 additions and 32 deletions.
96 changes: 64 additions & 32 deletions libraries/InternalFileSytem/src/flash/flash_nrf5x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down

0 comments on commit 3ea7855

Please sign in to comment.