2828#include "nrf_soc.h"
2929#include "delay.h"
3030#include "rtos.h"
31+ #include "assert.h"
32+
3133
3234#ifdef NRF52840_XXAA
3335 #define BOOTLOADER_ADDR 0xF4000
@@ -43,46 +45,56 @@ extern uint32_t __flash_arduino_start[];
4345// MACRO TYPEDEF CONSTANT ENUM DECLARATION
4446//--------------------------------------------------------------------+
4547static SemaphoreHandle_t _sem = NULL ;
46- static bool _flash_op_failed = false ;
48+ static uint32_t _flash_op_result = NRF_EVT_FLASH_OPERATION_SUCCESS ;
4749
4850void flash_nrf5x_event_cb (uint32_t event )
4951{
5052 if ( _sem ) {
5153 // Record the result, for consumption by fal_erase or fal_program
5254 // Used to reattempt failed operations
53- _flash_op_failed = ( event == NRF_EVT_FLASH_OPERATION_ERROR ) ;
55+ _flash_op_result = event ;
5456
5557 // Signal to fal_erase or fal_program that our async flash op is now complete
5658 xSemaphoreGive (_sem );
5759 }
5860}
5961
60- // How many retry attempts when performing flash write operations
62+ // How many retry attempts when performing flash operations
6163#define MAX_RETRY 20
6264
63- // Check whether a flash operation was successful, or should be repeated
64- static bool retry_flash_op ( uint32_t op_result , bool sd_enabled ) {
65- // If busy
66- if ( op_result == NRF_ERROR_BUSY ) {
67- delay ( 1 );
68- return true; // Retry
69- }
65+ // When soft device is enabled, flash ops are async
66+ // Eventual success is reported via callback, which we await
67+ static uint32_t wait_for_async_flash_op_completion ( uint32_t initial_result )
68+ {
69+ // If initial result not NRF_SUCCESS, no need to await callback
70+ // We will pass the initial result (failure) straight through
71+ int32_t result = initial_result ;
7072
71- // Unspecified error
72- if (op_result != NRF_SUCCESS )
73- return true; // Retry
73+ // Operation was queued successfully
74+ if (initial_result == NRF_SUCCESS ) {
7475
75- // If the soft device is enabled, flash operations run async
76- // The callback (flash_nrf5x_event_cb) will give semaphore when the flash operation is complete
77- // The callback also checks for NRF_EVT_FLASH_OPERATION_ERROR, which is not available to us otherwise
78- if (sd_enabled ) {
76+ // Wait for result via callback
7977 xSemaphoreTake (_sem , portMAX_DELAY );
80- if (_flash_op_failed )
81- return true; // Retry
78+
79+ // If completed successfully
80+ if (_flash_op_result == NRF_EVT_FLASH_OPERATION_SUCCESS ) {
81+ result = NRF_SUCCESS ;
82+ }
83+
84+ // If general failure.
85+ // The comment on NRF_EVT_FLASH_OPERATION_ERROR describes it as a timeout,
86+ // so we're using a similar error when translating from NRF_SOC_EVTS type to the global NRF52 error defines
87+ else if (_flash_op_result == NRF_EVT_FLASH_OPERATION_ERROR ) {
88+ result = NRF_ERROR_TIMEOUT ;
89+ }
90+
91+ // If this assert triggers, we need to implement a new NRF_SOC_EVTS value
92+ else {
93+ assert (false);
94+ }
8295 }
83-
84- // Success
85- return false;
96+
97+ return result ;
8698}
8799
88100// Flash Abstraction Layer
@@ -139,8 +151,7 @@ bool flash_nrf5x_erase(uint32_t addr)
139151static bool fal_erase (uint32_t addr )
140152{
141153 // Init semaphore for first call
142- if ( _sem == NULL )
143- {
154+ if ( _sem == NULL ) {
144155 _sem = xSemaphoreCreateBinary ();
145156 VERIFY (_sem );
146157 }
@@ -150,19 +161,30 @@ static bool fal_erase (uint32_t addr)
150161 uint8_t sd_en = 0 ;
151162 (void ) sd_softdevice_is_enabled (& sd_en );
152163
153- // Make multiple attempts to erase
154- uint8_t attempt = 0 ;
155- while (retry_flash_op (sd_flash_page_erase (addr / FLASH_NRF52_PAGE_SIZE ), sd_en )) {
156- if (++ attempt > MAX_RETRY )
157- return false; // Failure
164+ // Erase the page
165+ // Multiple attempts if needed
166+ uint32_t err ;
167+ for (uint8_t attempt = 0 ; attempt < MAX_RETRY ; ++ attempt ) {
168+ err = sd_flash_page_erase (addr / FLASH_NRF52_PAGE_SIZE );
169+
170+ if (sd_en ) {
171+ err = wait_for_async_flash_op_completion (err ); // Only async if soft device enabled
172+ }
173+ if (err == NRF_SUCCESS ) {
174+ break ;
175+ }
176+ if (err == NRF_ERROR_BUSY ) {
177+ delay (1 );
178+ }
158179 }
159- return true; // Success
180+ VERIFY_STATUS (err , false); // Return false if all retries fail
181+
182+ return true; // Successfully erased
160183}
161184
162185static uint32_t fal_program (uint32_t dst , void const * src , uint32_t len )
163186{
164- // Check if soft device is enabled
165- // If yes, flash operations are async, so we need to wait for the callback to give the semaphore
187+ // wait for async event if SD is enabled
166188 uint8_t sd_en = 0 ;
167189 (void ) sd_softdevice_is_enabled (& sd_en );
168190
@@ -172,26 +194,57 @@ static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len)
172194 // https://devzone.nordicsemi.com/f/nordic-q-a/40088/sd_flash_write-cause-nrf_fault_id_sd_assert
173195 // Workaround: write half page at a time.
174196#if NRF52832_XXAA
175- uint8_t attempt = 0 ;
176- while (retry_flash_op (sd_flash_write ((uint32_t * ) dst , (uint32_t const * ) src , len /4 ), sd_en )) {
177- if (++ attempt > MAX_RETRY )
178- return 0 ; // Failure
197+ // Write the page
198+ // Multiple attempts, if needed
199+ for (uint8_t attempt = 0 ; attempt < MAX_RETRY ; ++ attempt ) {
200+ err = sd_flash_write ((uint32_t * ) dst , (uint32_t const * ) src , len /4 );
201+
202+ if (sd_en ) {
203+ err = wait_for_async_flash_op_completion (err ); // Only async if soft device enabled
204+ }
205+ if (err == NRF_SUCCESS ) {
206+ break ;
207+ }
208+ if (err == NRF_ERROR_BUSY ) {
209+ delay (1 );
210+ }
179211 }
180- #else
212+ VERIFY_STATUS ( err , 0 ); // Return 0 if all retries fail
181213
182- // First part of block
183- uint8_t attempt = 0 ;
184- while (retry_flash_op (sd_flash_write ((uint32_t * ) dst , (uint32_t const * ) src , len /8 ), sd_en )) {
185- if (++ attempt > MAX_RETRY )
186- return 0 ; // Failure
214+ #else
215+ // Write first part of page
216+ // Multiple attempts, if needed
217+ for (uint8_t attempt = 0 ; attempt < MAX_RETRY ; ++ attempt ) {
218+ err = sd_flash_write ((uint32_t * ) dst , (uint32_t const * ) src , len /8 );
219+
220+ if (sd_en ) {
221+ err = wait_for_async_flash_op_completion (err ); // Only async if soft device enabled
222+ }
223+ if (err == NRF_SUCCESS ) {
224+ break ;
225+ }
226+ if (err == NRF_ERROR_BUSY ) {
227+ delay (1 );
228+ }
187229 }
188-
189- // Second part of block
190- attempt = 0 ;
191- while (retry_flash_op (sd_flash_write ((uint32_t * ) (dst + len /2 ), (uint32_t const * ) (src + len /2 ), len /8 ), sd_en )) {
192- if (++ attempt > MAX_RETRY )
193- return 0 ; // Failure
230+ VERIFY_STATUS (err , 0 ); // Return 0 if all retries fail
231+
232+ // Write second part of page
233+ // Multiple attempts, if needed
234+ for (uint8_t attempt = 0 ; attempt < MAX_RETRY ; ++ attempt ) {
235+ err = sd_flash_write ((uint32_t * ) (dst + len /2 ), (uint32_t const * ) (src + len /2 ), len /8 );
236+
237+ if (sd_en ) {
238+ err = wait_for_async_flash_op_completion (err ); // Only async if soft device enabled
239+ }
240+ if (err == NRF_SUCCESS ) {
241+ break ;
242+ }
243+ if (err == NRF_ERROR_BUSY ) {
244+ delay (1 );
245+ }
194246 }
247+ VERIFY_STATUS (err , 0 ); // Return 0 if all retries fail
195248#endif
196249
197250 return len ;
0 commit comments