nrf: fix internal flash writes

This commit is contained in:
Dan Halbert 2019-03-18 09:11:40 -04:00
parent 60f76941ef
commit 9cceea0d68
4 changed files with 83 additions and 43 deletions

View File

@ -39,12 +39,16 @@
nrf_nvic_state_t nrf_nvic_state = { 0 }; nrf_nvic_state_t nrf_nvic_state = { 0 };
// Flag indicating progress of internal flash operation.
sd_flash_operation_status_t sd_flash_operation_status;
__attribute__((aligned(4))) __attribute__((aligned(4)))
static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (BLE_GATT_ATT_MTU_DEFAULT)]; static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (BLE_GATT_ATT_MTU_DEFAULT)];
void ble_drv_reset() { void ble_drv_reset() {
// Linked list items will be gc'd. // Linked list items will be gc'd.
MP_STATE_VM(ble_drv_evt_handler_entries) = NULL; MP_STATE_VM(ble_drv_evt_handler_entries) = NULL;
sd_flash_operation_status = SD_FLASH_OPERATION_DONE;
} }
void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) { void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) {
@ -85,8 +89,6 @@ extern void tusb_hal_nrf_power_event (uint32_t event);
void SD_EVT_IRQHandler(void) { void SD_EVT_IRQHandler(void) {
uint32_t evt_id; uint32_t evt_id;
while (sd_evt_get(&evt_id) != NRF_ERROR_NOT_FOUND) { while (sd_evt_get(&evt_id) != NRF_ERROR_NOT_FOUND) {
// sd_evt_handler(evt_id);
switch (evt_id) { switch (evt_id) {
// usb power event // usb power event
case NRF_EVT_POWER_USB_DETECTED: case NRF_EVT_POWER_USB_DETECTED:
@ -100,7 +102,16 @@ void SD_EVT_IRQHandler(void) {
} }
break; break;
default: break; // Set flag indicating that a flash operation has finished.
case NRF_EVT_FLASH_OPERATION_SUCCESS:
sd_flash_operation_status = SD_FLASH_OPERATION_DONE;
break;
case NRF_EVT_FLASH_OPERATION_ERROR:
sd_flash_operation_status = SD_FLASH_OPERATION_ERROR;
break;
default:
break;
} }
} }

View File

@ -50,6 +50,15 @@
typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*); typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*);
typedef enum {
SD_FLASH_OPERATION_DONE,
SD_FLASH_OPERATION_IN_PROGRESS,
SD_FLASH_OPERATION_ERROR,
} sd_flash_operation_status_t;
// Flag indicating progress of internal flash operation.
extern sd_flash_operation_status_t sd_flash_operation_status;
typedef struct ble_drv_evt_handler_entry { typedef struct ble_drv_evt_handler_entry {
struct ble_drv_evt_handler_entry *next; struct ble_drv_evt_handler_entry *next;
void *param; void *param;

@ -1 +1 @@
Subproject commit b96950abf27229c2a3b1719d60691321246bfc94 Subproject commit 3d268263be2390ab760f75a3da72689ef13031a4

View File

@ -38,6 +38,7 @@
#include "nrf_nvmc.h" #include "nrf_nvmc.h"
#ifdef BLUETOOTH_SD #ifdef BLUETOOTH_SD
#include "ble_drv.h"
#include "nrf_sdm.h" #include "nrf_sdm.h"
#endif #endif
@ -71,26 +72,15 @@ uint32_t supervisor_flash_get_block_count(void) {
} }
#ifdef BLUETOOTH_SD #ifdef BLUETOOTH_SD
STATIC bool wait_for_flash_operation(void) { STATIC void sd_flash_operation_start(void) {
do { sd_flash_operation_status = SD_FLASH_OPERATION_IN_PROGRESS;
}
STATIC sd_flash_operation_status_t sd_flash_operation_wait_until_done(void) {
while (sd_flash_operation_status == SD_FLASH_OPERATION_IN_PROGRESS) {
sd_app_evt_wait(); sd_app_evt_wait();
uint32_t evt_id;
uint32_t result = sd_evt_get(&evt_id);
if (result == NRF_SUCCESS) {
switch (evt_id) {
case NRF_EVT_FLASH_OPERATION_SUCCESS:
return true;
case NRF_EVT_FLASH_OPERATION_ERROR:
return false;
default:
// Some other event. Wait for a flash event.
continue;
} }
} return sd_flash_operation_status;
return false;
} while (true);
} }
#endif #endif
@ -105,10 +95,36 @@ void supervisor_flash_flush(void) {
(void) sd_softdevice_is_enabled(&sd_en); (void) sd_softdevice_is_enabled(&sd_en);
if (sd_en) { if (sd_en) {
sd_flash_page_erase(_flash_page_addr / FL_PAGE_SZ); uint32_t err_code;
wait_for_flash_operation(); // TODO: handle error return. sd_flash_operation_status_t status;
sd_flash_write((uint32_t *)_flash_page_addr, (uint32_t *)_flash_cache, FL_PAGE_SZ / sizeof(uint32_t));
wait_for_flash_operation(); sd_flash_operation_start();
err_code = sd_flash_page_erase(_flash_page_addr / FL_PAGE_SZ);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Flash erase failed to start, err 0x%04x"), err_code);
}
status = sd_flash_operation_wait_until_done();
if (status == SD_FLASH_OPERATION_ERROR) {
mp_raise_OSError_msg(translate("Flash erase failed"));
}
// Divide a full page into parts, because writing a full page causes an assertion failure.
// See https://devzone.nordicsemi.com/f/nordic-q-a/40088/sd_flash_write-cause-nrf_fault_id_sd_assert/
const size_t BLOCK_PARTS = 2;
size_t words_to_write = FL_PAGE_SZ / sizeof(uint32_t) / BLOCK_PARTS;
for (size_t i = 0; i < BLOCK_PARTS; i++) {
sd_flash_operation_start();
err_code = sd_flash_write(((uint32_t *)_flash_page_addr) + i * words_to_write,
(uint32_t *)_flash_cache + i * words_to_write,
words_to_write);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Flash write failed to start, err 0x%04x"), err_code);
}
status = sd_flash_operation_wait_until_done();
if (status == SD_FLASH_OPERATION_ERROR) {
mp_raise_OSError_msg(translate("Flash write failed"));
}
}
} else { } else {
#endif #endif
nrf_nvmc_page_erase(_flash_page_addr); nrf_nvmc_page_erase(_flash_page_addr);
@ -117,11 +133,13 @@ void supervisor_flash_flush(void) {
} }
#endif #endif
_flash_page_addr = NO_CACHE;
} }
_flash_page_addr = NO_CACHE;
} }
mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) { mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {
// Must write out anything in cache before trying to read.
supervisor_flash_flush();
uint32_t src = lba2addr(block); uint32_t src = lba2addr(block);
memcpy(dest, (uint8_t*) src, FILESYSTEM_BLOCK_SIZE*num_blocks); memcpy(dest, (uint8_t*) src, FILESYSTEM_BLOCK_SIZE*num_blocks);
return 0; // success return 0; // success
@ -139,9 +157,11 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32
supervisor_flash_flush(); supervisor_flash_flush();
_flash_page_addr = page_addr; _flash_page_addr = page_addr;
// Copy the current contents of the entire page into the cache.
memcpy(_flash_cache, (void *)page_addr, FL_PAGE_SZ); memcpy(_flash_cache, (void *)page_addr, FL_PAGE_SZ);
} }
// Overwrite part or all of the page cache with the src data.
memcpy(_flash_cache + (addr & (FL_PAGE_SZ - 1)), src, count * FILESYSTEM_BLOCK_SIZE); memcpy(_flash_cache + (addr & (FL_PAGE_SZ - 1)), src, count * FILESYSTEM_BLOCK_SIZE);
// adjust for next run // adjust for next run