// Register the event handler using the NRF_SDH_SOC_OBSERVER macro
NRF_SDH_SOC_OBSERVER(m_sys_evt_observer, 0, sys_evt_dispatch, NULL);
// Event handler function prototype
void sys_evt_dispatch(uint32_t sys_evt, void * p_context) {
// Your system event handling code here
// For example, handle the flashBusy flag or other system events
ilumi_debug("System event : %d\r\n",sys_evt);
if (sys_evt == NRF_EVT_FLASH_OPERATION_SUCCESS){
ilumi_debug("NV op ok\r\n");
setFlashIdle(1);
}else if (sys_evt == NRF_EVT_FLASH_OPERATION_ERROR){
// will get flash op error if too much command, will investigate later
ilumi_debug("NV op fail\r\n");
setFlashIdle(0);
}
}
//--------------------------------------------------------------------------------------------------//
ret_code_t disable_soft_device(void){
ret_code_t error_code = sd_softdevice_disable();
if (error_code != NRF_SUCCESS && error_code != NRF_ERROR_INVALID_STATE) {
ilumi_log("Failed to disable SoftDevice: 0x%x\r\n", error_code);
return 0;
}
return 1;
}
ret_code_t enable_soft_device(void){
nrf_clock_lf_cfg_t const clock_lf_cfg =
{
.source = NRF_SDH_CLOCK_LF_SRC,
.rc_ctiv = NRF_SDH_CLOCK_LF_RC_CTIV,
.rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
.accuracy = NRF_SDH_CLOCK_LF_ACCURACY
};
ret_code_t error_code = sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler);
if (error_code != NRF_SUCCESS && error_code != NRF_ERROR_INVALID_STATE) {
ilumi_log("Failed to enable SoftDevice: 0x%x\r\n", error_code);
return 0;
}
return 1;
}
//----------------------------------------------------------------------------------------------//
#define FLASH_PAGE_VALID ((uint32_t) 0xAABBCCEE)
#define USER_FLASH_START_ADDRESS 0x000FF000 //also tried fd0000, fe000
#define USER_FLASH_SIZE 0x1000
#define FLASH_RETRY_TIMES (8)
static volatile uint8_t flash_error = 0;
static volatile uint8_t flashBusy = 0;
extern ret_code_t enable_soft_device(void);
extern ret_code_t disable_soft_device(void);
void flashErasePage(uint8_t * addr){
uint32_t error_code;
disable_soft_device();
// uint32_t page_number = (uint32_t)addr >> 12; //nRF52 page size is 4096Byte
uint32_t page_number = (uint32_t)addr / USER_FLASH_SIZE;
ilumi_log("page number: %d\r\n",page_number);
int8_t retry = FLASH_RETRY_TIMES;
retry_flash_op:
while((error_code = sd_flash_page_erase(page_number)) == NRF_ERROR_BUSY); //if busy keeps trying, is this efficient?
// error_code = sd_flash_write( (uint32_t*) addr, buffer_ptr, word_len))
if(error_code == NRF_SUCCESS){
// this SUCCNSS just means that the erase command have been accpected by stack, we need to wait for erase finish
flashBusy = 1;
while(flashBusy) (void) sd_app_evt_wait();
if(flash_error){
flash_error = 0;
retry--;
if(retry){
goto retry_flash_op;
}else{
ilumi_log("nv page erase retry fail");
}
}
// LOG_INFO("nv page erase OK, addr=0x%x", addr);
}else{
ilumi_log("nv page erase cmd reject, err:0x%x addr=0x%x", error_code, addr);
}
}
int8_t flashWrite( uint8_t *addr, uint16_t len, uint8_t *buf ){
uint32_t error_code;
uint32_t word_len;
//uint32_t * buffer_ptr = NULL; // buf address could be not WORD-aligned need to create a buffer for it
int8_t ret = 0;
if(len == 0) return -1;
if((len & 0x3) == 0){ // len has to be divide by 4 because flash is WORD-aligned
word_len = len >> 2; // word size is 4x the len
}else{
word_len = (len >> 2) + 1;
}
uint32_t buffer_ptr[word_len];
//if((buffer_ptr = (uint32_t*)malloc(word_len<<2)))
{
memcpy(buffer_ptr, buf, word_len<<2);
disable_soft_device();
int8_t retry = FLASH_RETRY_TIMES;
retry_flash_op :
while((error_code = sd_flash_write( (uint32_t*) addr, buffer_ptr, word_len)) == NRF_ERROR_BUSY); //sd_flash_write size 32bit word size, addr must be word aligned
if(error_code != NRF_SUCCESS){
ret = -1;
ilumi_log("sd nv write fail, err=0x%x addr=0x%x len=0x%x wordlen=0x%x srcaddr=0x%x", error_code, addr, len, word_len, buf);
}else{
flashBusy = 1;
while (flashBusy) (void) sd_app_evt_wait();
if(flash_error){
flash_error = 0;
retry--;
if(retry){
goto retry_flash_op;
}else{
ret = -1;
ilumi_log("sd nv write retry fail");
}
}
// NRF_LOG_INFO("sd nv write OK, addr=0x%x len=0x%x wordlen=0x%x", addr, len, word_len);
}
}
return ret;
}
void setFlashIdle(uint8_t is_flash_op_success){
// previously this is a potential bug becasue no matter what value in is_flash_op_success,
// this function will set flashBusy to idle, while for NRF51 is_flash_op_success is not being used
// following path should fix the problem
flashBusy = 0;
if(is_flash_op_success == 1) flash_error = 0;
else flash_error = 1;
enable_soft_device();
}
/*********************************************************************
* Save user data to NV
* @param addr addr pointer of area
* @param data data buffer point
* @param length length of data buffer
* @param flash_page_size
*/
void update_nv_data(uint16_t length, uint8_t* data, uint8_t * addr, uint16_t flash_page_size) {
uint32_t signature;
int8_t ret;
if (length > flash_page_size) {
ilumi_log("data size > NV page size\r\n", flash_page_size, addr);
return;
}
// Get the current signature from the flash address
signature = *((uint32_t*) addr); // get 32bit signature
// If the current flash sector is valid, erase it first
if (FLASH_PAGE_VALID == signature) {
flashErasePage(addr);
}
// Write the updated data to the flash
ret = flashWrite(addr, length, data);
// If flashWrite failed, invalidate the current flash sector by setting the signature to 0
if (0 != ret) {
signature = 0x00;
flashErasePage(addr); // Always erase first
flashWrite(addr, sizeof(signature), (uint8_t *)&signature); // Write this last
}
}
/*********************************************************************
* clear the backup flags to user data to NV
* @param length length of data buffer
* @param data data buffer pointer
* @param offset offset to the data struct
* @param addr addr pointer of area
* @param flash_page_size
*/
void clear_reset_flag_data(uint16_t length, uint8_t* data, uint32_t offset, uint8_t * addr, uint16_t flash_page_size) {
uint32_t signature;
if (length > flash_page_size) {
ilumi_log("data size > NV page size\r\n", flash_page_size, addr);
return;
}
// Get the current signature from the flash address
signature = *((uint32_t*) addr); // get 32bit signature
// Write the updated flag to the flash if the current page is valid
if (FLASH_PAGE_VALID == signature) {
flashWrite(addr + offset, length, data);
}
}
/*
* if not valid data, return 0;
*/
uint8_t load_nv_data(uint16_t length, uint8_t* data, uint8_t * addr, uint16_t flash_page_size) {
uint32_t signature;
// Log the NV loading attempt
ilumi_log("load NV addr: 0x%x, page size: %d\r\n", addr, flash_page_size);
if (length > flash_page_size) {
ilumi_log("len: %d exceed pg size\r\n", length);
return 0;
}
// Get the current signature from the flash address
signature = *((uint32_t*) addr); // get 32bit signature
ilumi_log("signature : 0x%x\r\n",signature);
// Load from the valid segment
if (FLASH_PAGE_VALID == signature) {
ilumi_log("user data load ok\r\n");
memcpy(data, addr, length);
} else {
ilumi_log("addr bad\r\n");
return 0;
}
return 1;
}
void clear_nv_data(uint8_t * addr) {
flashErasePage(addr);
}
<!DOCTYPE Linker_Placement_File>
<Root name="Flash Section Placement">
<MemorySegment name="FLASH1" start="$(FLASH_PH_START)" size="$(FLASH_PH_SIZE)">
<ProgramSection load="no" name=".reserved_flash" start="$(FLASH_PH_START)" size="$(FLASH_START)-$(FLASH_PH_START)" />
<ProgramSection alignment="0x100" load="Yes" name=".vectors" start="$(FLASH_START)" />
<ProgramSection alignment="4" load="Yes" name=".init" />
<ProgramSection alignment="4" load="Yes" name=".init_rodata" />
<ProgramSection alignment="4" load="Yes" name=".text" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".crypto_data" inputsections="*(SORT(.crypto_data*))" address_symbol="__start_crypto_data" end_symbol="__stop_crypto_data" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".nrf_queue" inputsections="*(.nrf_queue*)" address_symbol="__start_nrf_queue" end_symbol="__stop_nrf_queue" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".dfu_trans" inputsections="*(SORT(.dfu_trans*))" address_symbol="__start_dfu_trans" end_symbol="__stop_dfu_trans" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".svc_data" inputsections="*(.svc_data*)" address_symbol="__start_svc_data" end_symbol="__stop_svc_data" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_const_data" inputsections="*(SORT(.log_const_data*))" address_symbol="__start_log_const_data" end_symbol="__stop_log_const_data" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".nrf_balloc" inputsections="*(.nrf_balloc*)" address_symbol="__start_nrf_balloc" end_symbol="__stop_nrf_balloc" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_ble_observers" inputsections="*(SORT(.sdh_ble_observers*))" address_symbol="__start_sdh_ble_observers" end_symbol="__stop_sdh_ble_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_backends" inputsections="*(SORT(.log_backends*))" address_symbol="__start_log_backends" end_symbol="__stop_log_backends" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_req_observers" inputsections="*(SORT(.sdh_req_observers*))" address_symbol="__start_sdh_req_observers" end_symbol="__stop_sdh_req_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_state_observers" inputsections="*(SORT(.sdh_state_observers*))" address_symbol="__start_sdh_state_observers" end_symbol="__stop_sdh_state_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_stack_observers" inputsections="*(SORT(.sdh_stack_observers*))" address_symbol="__start_sdh_stack_observers" end_symbol="__stop_sdh_stack_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_soc_observers" inputsections="*(SORT(.sdh_soc_observers*))" address_symbol="__start_sdh_soc_observers" end_symbol="__stop_sdh_soc_observers" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections" address_symbol="__start_nrf_sections" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_dynamic_data" inputsections="*(SORT(.log_dynamic_data*))" runin=".log_dynamic_data_run"/>
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_filter_data" inputsections="*(SORT(.log_filter_data*))" runin=".log_filter_data_run"/>
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".fs_data" inputsections="*(.fs_data*)" runin=".fs_data_run"/>
<ProgramSection alignment="4" load="Yes" name=".dtors" />
<ProgramSection alignment="4" load="Yes" name=".ctors" />
<ProgramSection alignment="4" load="Yes" name=".rodata" />
<ProgramSection alignment="4" load="Yes" name=".ARM.exidx" address_symbol="__exidx_start" end_symbol="__exidx_end" />
<ProgramSection alignment="4" load="Yes" runin=".fast_run" name=".fast" />
<ProgramSection alignment="4" load="Yes" runin=".data_run" name=".data" />
<ProgramSection alignment="4" load="Yes" runin=".tdata_run" name=".tdata" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".mbr_params_page" address_symbol="__start_mbr_params_page" end_symbol="__stop_mbr_params_page" start = "0x000FE000" size="0x1000" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".bootloader_settings_page" address_symbol="__start_bootloader_settings_page" end_symbol="__stop_bootloader_settings_page" start = "0x000FF000" size="0x1000" />
</MemorySegment>
<MemorySegment name="RAM1" start="$(RAM_PH_START)" size="$(RAM_PH_SIZE)">
<ProgramSection load="no" name=".reserved_ram" start="$(RAM_PH_START)" size="$(RAM_START)-$(RAM_PH_START)" />
<ProgramSection alignment="0x100" load="No" name=".vectors_ram" start="$(RAM_START)" address_symbol="__app_ram_start__"/>
<ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections_run" address_symbol="__start_nrf_sections_run" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".log_dynamic_data_run" address_symbol="__start_log_dynamic_data" end_symbol="__stop_log_dynamic_data" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".log_filter_data_run" address_symbol="__start_log_filter_data" end_symbol="__stop_log_filter_data" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".fs_data_run" address_symbol="__start_fs_data" end_symbol="__stop_fs_data" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections_run_end" address_symbol="__end_nrf_sections_run" />
<ProgramSection alignment="4" load="No" name=".fast_run" />
<ProgramSection alignment="4" load="No" name=".data_run" />
<ProgramSection alignment="4" load="No" name=".tdata_run" />
<ProgramSection alignment="4" load="No" name=".bss" />
<ProgramSection alignment="4" load="No" name=".tbss" />
<ProgramSection alignment="4" load="No" name=".non_init" />
<ProgramSection alignment="4" size="__HEAPSIZE__" load="No" name=".heap" />
<ProgramSection alignment="8" size="__STACKSIZE__" load="No" place_from_segment_end="Yes" name=".stack" address_symbol="__StackLimit" end_symbol="__StackTop"/>
<ProgramSection alignment="8" size="__STACKSIZE_PROCESS__" load="No" name=".stack_process" />
</MemorySegment>
<MemorySegment name="uicr_bootloader_start_address" start="0x10001014" size="0x4">
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".uicr_bootloader_start_address" address_symbol="__start_uicr_bootloader_start_address" end_symbol="__stop_uicr_bootloader_start_address" start = "0x10001014" size="0x4" />
</MemorySegment>
<MemorySegment name="uicr_mbr_params_page" start="0x10001018" size="0x4">
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".uicr_mbr_params_page" address_symbol="__start_uicr_mbr_params_page" end_symbol="__stop_uicr_mbr_params_page" start = "0x10001018" size="0x4" />
</MemorySegment>
</Root>
<!DOCTYPE Linker_Placement_File>
<Root name="Flash Section Placement">
<MemorySegment name="FLASH1" start="$(FLASH_PH_START)" size="$(FLASH_PH_SIZE)">
<ProgramSection load="no" name=".reserved_flash" start="$(FLASH_PH_START)" size="$(FLASH_START)-$(FLASH_PH_START)" />
<ProgramSection alignment="0x100" load="Yes" name=".vectors" start="$(FLASH_START)" />
<ProgramSection alignment="4" load="Yes" name=".init" />
<ProgramSection alignment="4" load="Yes" name=".init_rodata" />
<ProgramSection alignment="4" load="Yes" name=".text" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_soc_observers" inputsections="*(SORT(.sdh_soc_observers*))" address_symbol="__start_sdh_soc_observers" end_symbol="__stop_sdh_soc_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".pwr_mgmt_data" inputsections="*(SORT(.pwr_mgmt_data*))" address_symbol="__start_pwr_mgmt_data" end_symbol="__stop_pwr_mgmt_data" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_ble_observers" inputsections="*(SORT(.sdh_ble_observers*))" address_symbol="__start_sdh_ble_observers" end_symbol="__stop_sdh_ble_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_req_observers" inputsections="*(SORT(.sdh_req_observers*))" address_symbol="__start_sdh_req_observers" end_symbol="__stop_sdh_req_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_state_observers" inputsections="*(SORT(.sdh_state_observers*))" address_symbol="__start_sdh_state_observers" end_symbol="__stop_sdh_state_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_stack_observers" inputsections="*(SORT(.sdh_stack_observers*))" address_symbol="__start_sdh_stack_observers" end_symbol="__stop_sdh_stack_observers" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".nrf_queue" inputsections="*(.nrf_queue*)" address_symbol="__start_nrf_queue" end_symbol="__stop_nrf_queue" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".nrf_balloc" inputsections="*(.nrf_balloc*)" address_symbol="__start_nrf_balloc" end_symbol="__stop_nrf_balloc" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".cli_command" inputsections="*(.cli_command*)" address_symbol="__start_cli_command" end_symbol="__stop_cli_command" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".crypto_data" inputsections="*(SORT(.crypto_data*))" address_symbol="__start_crypto_data" end_symbol="__stop_crypto_data" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_const_data" inputsections="*(SORT(.log_const_data*))" address_symbol="__start_log_const_data" end_symbol="__stop_log_const_data" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_backends" inputsections="*(SORT(.log_backends*))" address_symbol="__start_log_backends" end_symbol="__stop_log_backends" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections" address_symbol="__start_nrf_sections" />
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".cli_sorted_cmd_ptrs" inputsections="*(.cli_sorted_cmd_ptrs*)" runin=".cli_sorted_cmd_ptrs_run"/>
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".fs_data" inputsections="*(.fs_data*)" runin=".fs_data_run"/>
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_dynamic_data" inputsections="*(SORT(.log_dynamic_data*))" runin=".log_dynamic_data_run"/>
<ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_filter_data" inputsections="*(SORT(.log_filter_data*))" runin=".log_filter_data_run"/>
<ProgramSection alignment="4" load="Yes" name=".dtors" />
<ProgramSection alignment="4" load="Yes" name=".ctors" />
<ProgramSection alignment="4" load="Yes" name=".rodata" />
<ProgramSection alignment="4" load="Yes" name=".ARM.exidx" address_symbol="__exidx_start" end_symbol="__exidx_end" />
<ProgramSection alignment="4" load="Yes" runin=".fast_run" name=".fast" />
<ProgramSection alignment="4" load="Yes" runin=".data_run" name=".data" />
<ProgramSection alignment="4" load="Yes" runin=".tdata_run" name=".tdata" />
</MemorySegment>
<MemorySegment name="RAM1" start="$(RAM_PH_START)" size="$(RAM_PH_SIZE)">
<ProgramSection load="no" name=".reserved_ram" start="$(RAM_PH_START)" size="$(RAM_START)-$(RAM_PH_START)" />
<ProgramSection alignment="0x100" load="No" name=".vectors_ram" start="$(RAM_START)" address_symbol="__app_ram_start__"/>
<ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections_run" address_symbol="__start_nrf_sections_run" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".cli_sorted_cmd_ptrs_run" address_symbol="__start_cli_sorted_cmd_ptrs" end_symbol="__stop_cli_sorted_cmd_ptrs" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".fs_data_run" address_symbol="__start_fs_data" end_symbol="__stop_fs_data" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".log_dynamic_data_run" address_symbol="__start_log_dynamic_data" end_symbol="__stop_log_dynamic_data" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".log_filter_data_run" address_symbol="__start_log_filter_data" end_symbol="__stop_log_filter_data" />
<ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections_run_end" address_symbol="__end_nrf_sections_run" />
<ProgramSection alignment="4" load="No" name=".fast_run" />
<ProgramSection alignment="4" load="No" name=".data_run" />
<ProgramSection alignment="4" load="No" name=".tdata_run" />
<ProgramSection alignment="4" load="No" name=".bss" />
<ProgramSection alignment="4" load="No" name=".tbss" />
<ProgramSection alignment="4" load="No" name=".non_init" />
<ProgramSection alignment="4" size="__HEAPSIZE__" load="No" name=".heap" />
<ProgramSection alignment="8" size="__STACKSIZE__" load="No" place_from_segment_end="Yes" name=".stack" address_symbol="__StackLimit" end_symbol="__StackTop"/>
<ProgramSection alignment="8" size="__STACKSIZE_PROCESS__" load="No" name=".stack_process" />
</MemorySegment>
</Root>
Unable to write to flash memory using direct memory access functions like sd_flash_page_erase & sd_flash_write (nrf52840 nrf SDK 17.1.0)
Also attached application and bootloader flash placement file
Application :
Memory segment:
FLASH1 RX 0x0 0x100000;RAM1 RWX 0x20000000 0x40000
Section placement macros:
FLASH_PH_START=0x0
FLASH_PH_SIZE=0x100000
RAM_PH_START=0x20000000
RAM_PH_SIZE=0x40000
FLASH_START=0x27000
FLASH_SIZE=0xd9000
RAM_START=0x20003F08
RAM_SIZE=0x3C0F8
Bootloader :
Memory segment:
FLASH1 RX 0x0 0x100000;RAM1 RWX 0x20000000 0x40000;mbr_params_page RX 0x000FE000 0x1000;bootloader_settings_page RX 0x000FF000 0x1000;uicr_bootloader_start_address RX 0x10001014 0x4;uicr_mbr_params_page RX 0x10001018 0x4
Section placement macros:
FLASH_PH_START=0x0
FLASH_PH_SIZE=0x100000
RAM_PH_START=0x20000000
RAM_PH_SIZE=0x40000
FLASH_START=0xf4000
FLASH_SIZE=0x6000
RAM_START=0x20005978
RAM_SIZE=0x3a688