// 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