This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

fds_record_update and fds_record_write events not firing and results in a never-ending loop after SoftDevice initialization

I am running my application on nRF SDK 16.0 and SoftDevice 7.2.0 but had this problem since 15.2 and 6.1.1 respectively. I thought it was resolved in an update since this issue is somewhat old and known, see: Data not updated to fds with GATT & FDS: Updating existing records with fds_record_update

My application is somewhat similar to the first link, I have two GATT characteristics being initialized with pre-processor macros and which can be updated from a mobile phone via nRFConnect. The values need to be stored in NVM and thus I am using FDS to write and update them. However, when any of the two methods are called, nothing happens; it doesn't enter my FDS event handler to even give a NRF error message. Reading works fine.

Here is how I initialize my SoftDevice:

bool softdevice_initialize()
{
	ret_code_t err_code;
	
	err_code = nrf_sdh_enable_request();
	APP_ERROR_CHECK(err_code);
	
	// Configure the BLE stack using the default settings.
	// Fetch the start address of the application RAM.
	uint32_t ram_start = 0;
	err_code = nrf_sdh_ble_default_cfg_set(1, &ram_start);
	APP_ERROR_CHECK(err_code);
	err_code = nrf_sdh_ble_enable(&ram_start);
	APP_ERROR_CHECK(err_code);
	/**/
	ble_dfu_buttonless_init_t dfus_init =
	{
		.evt_handler = ble_dfu_evt_handler
	};
	
	err_code = ble_dfu_buttonless_async_svci_init();
	APP_ERROR_CHECK(err_code);
	
	err_code = ble_dfu_buttonless_init(&dfus_init);
	APP_ERROR_CHECK(err_code);
	
	// Register a handler for BLE events.
	NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, handle_gap_event, NULL);
	
	return err_code == NRF_SUCCESS;
}

Here is how I initialize my FDS:

bool fds_initialize(void)
{
	//Initialize FDS
	ret_code_t ret = fds_register(fds_event_handler);
	VERIFY_SUCCESS(ret);
	ret = fds_init();
	VERIFY_SUCCESS(ret);
	
	while (!m_fds_ready) ;
	
	fds_record_t record;
	fds_record_desc_t record_desc;
	record.file_id = NET_ID_FILE; // 0x0002 if not defined
	record.key = RECORD_KEY; // 0x1112 if not defined
	
	u_int32_t  value_of_net_id_addr = get_netId(); // FDS read, should get 0xFFFF=-1 if not initialized
	
	if (value_of_net_id_addr == -1)
	{
		ret = fds_gc();
		
		u_int32_t m_net_id = NET_ID; // small integer defined in pre-processor macro
		
		// Set up record
		record.data.p_data = &m_net_id;
		record.data.length_words = 1; /* one word is four bytes */
		write_flag = 0;
		ret_code_t rc;
		rc = fds_record_write(&record_desc, &record);
		if (rc != NRF_SUCCESS)
		{
			/* Handle error. */
			printf("Failed to flash write\r\n");
		}
		while (write_flag == 0) ;
	}
	
	memset(&record, 0, sizeof(record));
	memset(&record_desc, 0, sizeof(record_desc));
	record.file_id = NET_ID_FILE;
	record.key = OP_ID_RECORD_KEY;
	/* Initialize Operational ID */
	u_int32_t value_of_op_id = get_opId();  // FDS read, should get 0xFFFF=-1 if not initialized
	if (value_of_op_id == -1)
	{
		ret = fds_gc();
		u_int32_t m_op_id = OP_ID; // small integer defined in pre-processor macro
		
		record.data.p_data = &m_op_id;
		record.data.length_words = 1;
		write_flag = 0;
		ret = fds_record_write(&record_desc, &record);
		APP_ERROR_CHECK(ret);
		while (write_flag == 0) ;
	}
	
	return ret == NRF_SUCCESS;
}

Execution is stuck at line 35 which is supposed to be cleared in the event handler. write_flag and m_fds_ready are a values that are cleared in the event handler. I put error checks and breakpoints in my event handler but no events are being fired.

static void fds_event_handler(fds_evt_t const * p_fds_evt)
{
	switch (p_fds_evt->id)
	{
	case FDS_EVT_INIT:
		APP_ERROR_CHECK(p_fds_evt->result);
		m_fds_ready = true;
		break;
	case FDS_EVT_WRITE:
		if (p_fds_evt->result == NRF_SUCCESS)
		{
			write_flag = 1;
		}
		break;
	case FDS_EVT_UPDATE:
		if (p_fds_evt->result == NRF_SUCCESS)
		{
			write_flag = 1;
		}
		break;
	default:
		NRF_LOG_INFO("%d", p_fds_evt->id);
		break;
	}
}

I eventually got it to work by doing the FDS initialization first, then the SoftDevice's. In the update function below, I have to disable the SoftDevice, writing or updating and then re-enable it. Events then fire correctly.

ret_code_t fds_update(uint16_t file_id, uint16_t key, u_int32_t data, uint32_t data_length)
{	
	fds_record_t record;
	fds_record_desc_t record_desc;
	fds_find_token_t ftok;
	ret_code_t rc;
	
	// Fill in the record
	record.file_id = file_id;
	record.key = key;
	record.data.p_data = &data;
	record.data.length_words = data_length;
	write_flag = 0;
	
	// Find the record desc and update
	ftok.page = 0;
	ftok.p_addr = NULL;
	APP_ERROR_CHECK(nrf_sdh_disable_request());   // FIXME SoftDevice has to be disabled and re-enabled for write events during code execution
	while(fds_record_find(file_id, key, &record_desc, &ftok) == NRF_SUCCESS)
	{
		rc = fds_record_update(&record_desc, &record);
		if (rc == FDS_ERR_NO_SPACE_IN_FLASH)
		{
			NRF_LOG_INFO("FDS has no space left, garbage collector triggered.\n");
			rc = fds_gc();
			APP_ERROR_CHECK(rc);
			rc = fds_record_update(&record_desc, &record);
			APP_ERROR_CHECK(rc);
		}
		while (write_flag == 0) ;	
	}
		
	rc = fds_gc();
	if (rc != NRF_SUCCESS)
	{
		APP_ERROR_CHECK(rc);
		/* Handle Error */
	}
	APP_ERROR_CHECK(nrf_sdh_enable_request());
	
	return rc;
}

I have read that the peer manager stores data in flash using FDS also but it seems to work fine without tinkering with the SoftDevice unlike what I have to do so it makes me think the problem is with my implementation. Another potential pointer to the issue could be that FDS_ERR_NO_SPACE_IN_FLASH is always triggered in the above code.

These workarounds are okay for me but is against documentation in the nRF SDK which says FDS can work with the SoftDevice enabled. I'd like to have some peace of mind at least before production.

Parents
  • Maybe before you execute any fds calls check if there are any pending by calling nrf_fstorage_is_busy(NULL);, because this may interfere with your fds_event_handler() and handling of write_flag. It may be other flash operations that cause write_flag to be set to 1.

    Also, make sure to look through the documentation one more time for the fds api, instance for the fds_record_update() it says:

    * Record data can consist of multiple chunks. The data must be aligned to a 4 byte boundary, and
    * because it is not buffered internally, it must be kept in memory until the callback for the
    * operation has been received. The length of the data must not exceed @ref FDS_VIRTUAL_PAGE_SIZE
    * words minus 14 bytes.

    Do you ensure that the data is kept in memory until flash operation is complete? Typically this must be done by making the variable static or global. I can for instance see the while(fds_record_find(file_id, key, &record_desc, &ftok) == NRF_SUCCESS) seems scary, the write_flag is set to 0 before the loop, and the write_flag check is inside the while() loop. If it exists multiple times this would likely be problematic.

  • "Maybe before you execute any fds calls check if there are any pending by calling nrf_fstorage_is_busy(NULL);,"

    Excellent suggestion, I've added a case for the fds_gc event, a flag and blocking code.

    It clears the block so that's not the problem. I don't have any custom flash operation before this.

    As for your other comment, I've looked through the documentation as you said and I agree. write_flag and m_fds_ready are static volatile global variables but record and record_desc aren't. I think I got this part from the SDK actually without knowing why the data variables were static and not the records. I see that the fds and ndef message examples use the proper approach.

    I also agree that the while loop is scary and I don't need it. Update events don't happen often but I'll change it. For reference this is where it came from. Here's how part of the initialization looks like now: 

    ret = fds_gc();
    while (gc_flag == 0);
    
    if (nrf_fstorage_is_busy(NULL))
    {
    	NRF_LOG_INFO("FStorage is busy");
    }
    
    rc = fds_record_write(&m_record_desc, &m_record); // Where m_record_desc and m_record are static global variables
    
    if (rc != NRF_SUCCESS)
    {
    	/* Handle error. */
    	printf("Failed to flash write\r\n");
    }
    while (write_flag == 0) ;
    

    Still getting stuck on the last line. Perhaps the SoftDevice is not registering the event handler?

  • Can you do an APP_ERROR_CHECK() in fds_initialize() instead of VERIFY_SUCCESS()?

Reply Children
Related