This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to make FDS blocking

Hi,

I am using an nRF52840 with SDK 17.0.2 and S140 7.2.0. I am running a scheduler based on app_scheduler.

I am trying to make my FDS implementation blocking as it is too complex to run a synchronously as there are quite a few calls to it that are causing havoc with the queues. I have set up a flag that I set to true before executing an FDS operation and then I want to wait for that flag to become false before continuing. I have tried the following:

Simple, empty while loop. This just locks the processor up and the command is never received

while( _fdsBusy ) ;

NRF Delay. This has a similar effect to above

while( _fdsBusy )
{
    nrf_delay_ms(1);
}

Using my scheduler to wait. While the event is received in the Event Handler, the code never carries on executing.

while( _fdsBusy )
{
   app_sched_execute( );
   nrf_pwr_mgmt_run( );
}

Is there any way to achieve what I want?

Thanks,

Parents
  • Hi,

    I still do not have a complete picture of the problem based on the info but have 2 small observations.

    1. _fdsBusy seems like a global (.bss) level flag which is not an atomic operation so this flag wherever used might need to fenced into a critical section (enter and exit).
    2. nrf_delay_ms is very power inefficient, and I would suggest sleeping instead of busy waiting (__WFE or sd_app_evt_wait depending on whether you are using BLE stack, softdevice or not)
  • Hi Susheel,

    Here is a snippet of my code including initialisation and an example write. The function "Storage_Wait" is the function I am referring to about making the code blocking. I hope this helps shed some light on the situation?

    I tried "sd_app_evt_wait()" but that also did not work. The event handler is never invoked after the write or garbage collection.

    #define MAX_EVENT_SIZE 128
    #define QUEUE_SIZE	   180
    
    static uint32_t _eventBuffer[CEIL_DIV( APP_SCHED_BUF_SIZE( MAX_EVENT_SIZE, QUEUE_SIZE ), sizeof( uint32_t ) )] __ALIGN( 4 );
    
    #define TEST_FILE ( 0xBFFE )
    #define TEST_KEY  ( 0x0001 )
    
    static bool volatile _fdsBusy = false;
    static bool volatile m_fds_initialised = false;
    
    static char const* fds_evt_str[] = {
    	"FDS_EVT_INIT", "FDS_EVT_WRITE", "FDS_EVT_UPDATE", "FDS_EVT_DEL_RECORD", "FDS_EVT_DEL_FILE", "FDS_EVT_GC",
    };
    
    static void fds_evt_handler( fds_evt_t const* p_evt );
    static void Storage_Wait( void );
    const char* fds_err_str( ret_code_t ret );
    
    const char* fds_err_str( ret_code_t ret )
    {
    	// Array to map FDS return values to strings.
    	static char const* err_str[] = {
    		"FDS_ERR_OPERATION_TIMEOUT", "FDS_ERR_NOT_INITIALIZED",
    		"FDS_ERR_UNALIGNED_ADDR",	 "FDS_ERR_INVALID_ARG",
    		"FDS_ERR_NULL_ARG",			 "FDS_ERR_NO_OPEN_RECORDS",
    		"FDS_ERR_NO_SPACE_IN_FLASH", "FDS_ERR_NO_SPACE_IN_QUEUES",
    		"FDS_ERR_RECORD_TOO_LARGE",	 "FDS_ERR_NOT_FOUND",
    		"FDS_ERR_NO_PAGES",			 "FDS_ERR_USER_LIMIT_REACHED",
    		"FDS_ERR_CRC_CHECK_FAILED",	 "FDS_ERR_BUSY",
    		"FDS_ERR_INTERNAL",
    	};
    
    	return err_str[ret - NRF_ERROR_FDS_ERR_BASE];
    }
    
    static void fds_evt_handler( fds_evt_t const* p_evt )
    {
    	if( p_evt->result == NRF_SUCCESS )
    	{
    		Log_Info( "Event: %s received (NRF_SUCCESS)", fds_evt_str[p_evt->id] );
    	}
    	else
    	{
    		Log_Info( "Event: %s received (%s)", fds_evt_str[p_evt->id], fds_err_str( p_evt->result ) );
    	}
    
    	switch( p_evt->id )
    	{
    		case FDS_EVT_INIT:
    			if( p_evt->result == NRF_SUCCESS )
    			{
    				m_fds_initialised = true;
    			}
    			break;
    		default:
    			break;
    	}
    
    	_fdsBusy = false;
    
    	while( nrf_log_frontend_dequeue( ) )
    		;
    }
    
    static void Storage_Wait( void )
    {
    	while( _fdsBusy )
    	{
    		sd_app_evt_wait( );
    	}
    }
    
    int main( void )
    {
    	// Initialise clocks and power
    	ret_code_t rc = nrf_drv_clock_init( );
    	APP_ERROR_CHECK( rc );
    
    	if( !nrf_drv_power_init_check( ) )
    	{
    		nrf_drv_power_init( NULL );
    	}
    
    	// Initialise Scheduler
    	rc = nrf_pwr_mgmt_init( );
    	APP_ERROR_CHECK( rc );
    
    	rc = app_sched_init( MAX_EVENT_SIZE, QUEUE_SIZE, ( void* )_eventBuffer );
    	APP_ERROR_CHECK( rc );
    
    	uint32_t   ram_start;
    
    	// Enable the SoftDevice
    	rc = nrf_sdh_enable_request( );
    	APP_ERROR_CHECK( rc );
    
    	rc = nrf_sdh_ble_default_cfg_set( 1, &ram_start );
    	APP_ERROR_CHECK( rc );
    
    	rc = nrf_sdh_ble_enable( &ram_start );
    	APP_ERROR_CHECK( rc );
    
    	// Initialise Logging
    	rc = NRF_LOG_INIT( NULL );
    	APP_ERROR_CHECK( rc );
    
    	// Initialise the default backends as enabled in the configuration
    	NRF_LOG_DEFAULT_BACKENDS_INIT( );
    
    	NRF_LOG_INFO( "Application Started" );
    
    	// Initialise Storage
    
    	rc = fds_register( fds_evt_handler );
    	APP_ERROR_CHECK( rc );
    
    	rc = fds_init( );
    	APP_ERROR_CHECK( rc );
    
    	while( !m_fds_initialised )
    	{
    		sd_app_evt_wait();
    	}
    
    	Log_Info( "FDS Initialised" );
    
    	fds_stat_t stat = { 0 };
    
    	rc = fds_stat( &stat );
    	APP_ERROR_CHECK( rc );
    
    	NRF_LOG_INFO( "Found %d valid records.", stat.valid_records );
    	NRF_LOG_INFO( "Found %d dirty records (ready to be garbage collected).", stat.dirty_records );
    
    	if( stat.dirty_records > 0 )
    	{
    		_fdsBusy = true;
    
    		rc = fds_gc( );
    		APP_ERROR_CHECK( rc );
    
    		Storage_Wait( );
    	}
    
    	fds_record_desc_t desc = { 0 };
    	fds_find_token_t  tok  = { 0 };
    
    
    	// Test Write
    
    	static uint32_t const m_deadbeef = 0xDEADBEEF;
    
    	fds_record_t	  record;
    	fds_record_desc_t record_desc;
    
    	record.file_id			 = TEST_FILE;
    	record.key				 = TEST_KEY;
    	record.data.p_data		 = &m_deadbeef;
    	record.data.length_words = 1;
    
    	_fdsBusy = true;
    	rc = fds_record_write( &record_desc, &record );
    	APP_ERROR_CHECK( rc );
    	
    	Storage_Wait( );
    
    	while(1)
    	{
    		app_sched_execute( );
    
    		if( nrf_log_frontend_dequeue( ) == false )
    		{
    			nrf_pwr_mgmt_run( );
    		}
    	}
    }

    Thanks

Reply
  • Hi Susheel,

    Here is a snippet of my code including initialisation and an example write. The function "Storage_Wait" is the function I am referring to about making the code blocking. I hope this helps shed some light on the situation?

    I tried "sd_app_evt_wait()" but that also did not work. The event handler is never invoked after the write or garbage collection.

    #define MAX_EVENT_SIZE 128
    #define QUEUE_SIZE	   180
    
    static uint32_t _eventBuffer[CEIL_DIV( APP_SCHED_BUF_SIZE( MAX_EVENT_SIZE, QUEUE_SIZE ), sizeof( uint32_t ) )] __ALIGN( 4 );
    
    #define TEST_FILE ( 0xBFFE )
    #define TEST_KEY  ( 0x0001 )
    
    static bool volatile _fdsBusy = false;
    static bool volatile m_fds_initialised = false;
    
    static char const* fds_evt_str[] = {
    	"FDS_EVT_INIT", "FDS_EVT_WRITE", "FDS_EVT_UPDATE", "FDS_EVT_DEL_RECORD", "FDS_EVT_DEL_FILE", "FDS_EVT_GC",
    };
    
    static void fds_evt_handler( fds_evt_t const* p_evt );
    static void Storage_Wait( void );
    const char* fds_err_str( ret_code_t ret );
    
    const char* fds_err_str( ret_code_t ret )
    {
    	// Array to map FDS return values to strings.
    	static char const* err_str[] = {
    		"FDS_ERR_OPERATION_TIMEOUT", "FDS_ERR_NOT_INITIALIZED",
    		"FDS_ERR_UNALIGNED_ADDR",	 "FDS_ERR_INVALID_ARG",
    		"FDS_ERR_NULL_ARG",			 "FDS_ERR_NO_OPEN_RECORDS",
    		"FDS_ERR_NO_SPACE_IN_FLASH", "FDS_ERR_NO_SPACE_IN_QUEUES",
    		"FDS_ERR_RECORD_TOO_LARGE",	 "FDS_ERR_NOT_FOUND",
    		"FDS_ERR_NO_PAGES",			 "FDS_ERR_USER_LIMIT_REACHED",
    		"FDS_ERR_CRC_CHECK_FAILED",	 "FDS_ERR_BUSY",
    		"FDS_ERR_INTERNAL",
    	};
    
    	return err_str[ret - NRF_ERROR_FDS_ERR_BASE];
    }
    
    static void fds_evt_handler( fds_evt_t const* p_evt )
    {
    	if( p_evt->result == NRF_SUCCESS )
    	{
    		Log_Info( "Event: %s received (NRF_SUCCESS)", fds_evt_str[p_evt->id] );
    	}
    	else
    	{
    		Log_Info( "Event: %s received (%s)", fds_evt_str[p_evt->id], fds_err_str( p_evt->result ) );
    	}
    
    	switch( p_evt->id )
    	{
    		case FDS_EVT_INIT:
    			if( p_evt->result == NRF_SUCCESS )
    			{
    				m_fds_initialised = true;
    			}
    			break;
    		default:
    			break;
    	}
    
    	_fdsBusy = false;
    
    	while( nrf_log_frontend_dequeue( ) )
    		;
    }
    
    static void Storage_Wait( void )
    {
    	while( _fdsBusy )
    	{
    		sd_app_evt_wait( );
    	}
    }
    
    int main( void )
    {
    	// Initialise clocks and power
    	ret_code_t rc = nrf_drv_clock_init( );
    	APP_ERROR_CHECK( rc );
    
    	if( !nrf_drv_power_init_check( ) )
    	{
    		nrf_drv_power_init( NULL );
    	}
    
    	// Initialise Scheduler
    	rc = nrf_pwr_mgmt_init( );
    	APP_ERROR_CHECK( rc );
    
    	rc = app_sched_init( MAX_EVENT_SIZE, QUEUE_SIZE, ( void* )_eventBuffer );
    	APP_ERROR_CHECK( rc );
    
    	uint32_t   ram_start;
    
    	// Enable the SoftDevice
    	rc = nrf_sdh_enable_request( );
    	APP_ERROR_CHECK( rc );
    
    	rc = nrf_sdh_ble_default_cfg_set( 1, &ram_start );
    	APP_ERROR_CHECK( rc );
    
    	rc = nrf_sdh_ble_enable( &ram_start );
    	APP_ERROR_CHECK( rc );
    
    	// Initialise Logging
    	rc = NRF_LOG_INIT( NULL );
    	APP_ERROR_CHECK( rc );
    
    	// Initialise the default backends as enabled in the configuration
    	NRF_LOG_DEFAULT_BACKENDS_INIT( );
    
    	NRF_LOG_INFO( "Application Started" );
    
    	// Initialise Storage
    
    	rc = fds_register( fds_evt_handler );
    	APP_ERROR_CHECK( rc );
    
    	rc = fds_init( );
    	APP_ERROR_CHECK( rc );
    
    	while( !m_fds_initialised )
    	{
    		sd_app_evt_wait();
    	}
    
    	Log_Info( "FDS Initialised" );
    
    	fds_stat_t stat = { 0 };
    
    	rc = fds_stat( &stat );
    	APP_ERROR_CHECK( rc );
    
    	NRF_LOG_INFO( "Found %d valid records.", stat.valid_records );
    	NRF_LOG_INFO( "Found %d dirty records (ready to be garbage collected).", stat.dirty_records );
    
    	if( stat.dirty_records > 0 )
    	{
    		_fdsBusy = true;
    
    		rc = fds_gc( );
    		APP_ERROR_CHECK( rc );
    
    		Storage_Wait( );
    	}
    
    	fds_record_desc_t desc = { 0 };
    	fds_find_token_t  tok  = { 0 };
    
    
    	// Test Write
    
    	static uint32_t const m_deadbeef = 0xDEADBEEF;
    
    	fds_record_t	  record;
    	fds_record_desc_t record_desc;
    
    	record.file_id			 = TEST_FILE;
    	record.key				 = TEST_KEY;
    	record.data.p_data		 = &m_deadbeef;
    	record.data.length_words = 1;
    
    	_fdsBusy = true;
    	rc = fds_record_write( &record_desc, &record );
    	APP_ERROR_CHECK( rc );
    	
    	Storage_Wait( );
    
    	while(1)
    	{
    		app_sched_execute( );
    
    		if( nrf_log_frontend_dequeue( ) == false )
    		{
    			nrf_pwr_mgmt_run( );
    		}
    	}
    }

    Thanks

Children
  • I looked deeper into your code and it looks clean except the usage of while( nrf_log_frontend_dequeue( ) ); in your fds_evt_handler. It is wrong to wait endless like this for dequeue in an event handler which is in the interrupt context. remove this line and check if the deadlock breaks on waiting.

  • Hi Susheel,

    I have tried just running the snippet above only, with the nrf_log_frontend_dequeue() removed and I am getting some strange behaviour. Sometimes the event handler is never invoked (even for INIT), and sometimes it is. Even if it gets through Init, as soon as it gets to the Garbage Collection, it enters the while loop and then gets stuck. If I step through the while loop, I get the error "<error> app: SOFTDEVICE: ASSERTION FAILED".

    Do you have any ideas on this?

  • I just tested your code with only the snippet you provided which turned out to be complete in itself and remove the dequeue function made the program run normally without any issues. I think there is something else going on in other parts of the code which is not shown here.

  • Hi Susheel,

    I commented out everything in "main.c" and pasted the snippet in so there is only this code running. I did a chip erase and programmed the SD and then this code and I am still seeing the same errors. Sometimes the event handler is never invoked (not even for Init) and it never invokes after the GC call.

    I have attached my sdk_config.h snippet for the FDS part:

    // <e> FDS_ENABLED - fds - Flash data storage module
    //==========================================================
    #ifndef FDS_ENABLED
    #define FDS_ENABLED 1
    #endif
    // <h> Pages - Virtual page settings
    
    // <i> Configure the number of virtual pages to use and their size.
    //==========================================================
    // <o> FDS_VIRTUAL_PAGES - Number of virtual flash pages to use. 
    // <i> One of the virtual pages is reserved by the system for garbage collection.
    // <i> Therefore, the minimum is two virtual pages: one page to store data and one page to be used by the system for garbage collection.
    // <i> The total amount of flash memory that is used by FDS amounts to @ref FDS_VIRTUAL_PAGES * @ref FDS_VIRTUAL_PAGE_SIZE * 4 bytes.
    
    #ifndef FDS_VIRTUAL_PAGES
    #define FDS_VIRTUAL_PAGES 5
    #endif
    
    // <o> FDS_VIRTUAL_PAGE_SIZE  - The size of a virtual flash page.
     
    
    // <i> Expressed in number of 4-byte words.
    // <i> By default, a virtual page is the same size as a physical page.
    // <i> The size of a virtual page must be a multiple of the size of a physical page.
    // <1024=> 1024 
    // <2048=> 2048 
    
    #ifndef FDS_VIRTUAL_PAGE_SIZE
    #define FDS_VIRTUAL_PAGE_SIZE 1024
    #endif
    
    // <o> FDS_VIRTUAL_PAGES_RESERVED - The number of virtual flash pages that are used by other modules. 
    // <i> FDS module stores its data in the last pages of the flash memory.
    // <i> By setting this value, you can move flash end address used by the FDS.
    // <i> As a result the reserved space can be used by other modules.
    
    #ifndef FDS_VIRTUAL_PAGES_RESERVED
    #define FDS_VIRTUAL_PAGES_RESERVED 4
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> Backend - Backend configuration
    
    // <i> Configure which nrf_fstorage backend is used by FDS to write to flash.
    //==========================================================
    // <o> FDS_BACKEND  - FDS flash backend.
     
    
    // <i> NRF_FSTORAGE_SD uses the nrf_fstorage_sd backend implementation using the SoftDevice API. Use this if you have a SoftDevice present.
    // <i> NRF_FSTORAGE_NVMC uses the nrf_fstorage_nvmc implementation. Use this setting if you don't use the SoftDevice.
    // <1=> NRF_FSTORAGE_NVMC 
    // <2=> NRF_FSTORAGE_SD 
    
    #ifndef FDS_BACKEND
    #define FDS_BACKEND 2
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> Queue - Queue settings
    
    //==========================================================
    // <o> FDS_OP_QUEUE_SIZE - Size of the internal queue. 
    // <i> Increase this value if you frequently get synchronous FDS_ERR_NO_SPACE_IN_QUEUES errors.
    
    #ifndef FDS_OP_QUEUE_SIZE
    #define FDS_OP_QUEUE_SIZE 500
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> CRC - CRC functionality
    
    //==========================================================
    // <e> FDS_CRC_CHECK_ON_READ - Enable CRC checks.
    
    // <i> Save a record's CRC when it is written to flash and check it when the record is opened.
    // <i> Records with an incorrect CRC can still be 'seen' by the user using FDS functions, but they cannot be opened.
    // <i> Additionally, they will not be garbage collected until they are deleted.
    //==========================================================
    #ifndef FDS_CRC_CHECK_ON_READ
    #define FDS_CRC_CHECK_ON_READ 1
    #endif
    // <o> FDS_CRC_CHECK_ON_WRITE  - Perform a CRC check on newly written records.
     
    
    // <i> Perform a CRC check on newly written records.
    // <i> This setting can be used to make sure that the record data was not altered while being written to flash.
    // <1=> Enabled 
    // <0=> Disabled 
    
    #ifndef FDS_CRC_CHECK_ON_WRITE
    #define FDS_CRC_CHECK_ON_WRITE 0
    #endif
    
    // </e>
    
    // </h> 
    //==========================================================
    
    // <h> Users - Number of users
    
    //==========================================================
    // <o> FDS_MAX_USERS - Maximum number of callbacks that can be registered. 
    #ifndef FDS_MAX_USERS
    #define FDS_MAX_USERS 1
    #endif
    
    // </h> 
    //==========================================================
    
    // </e>

    and fstorage:

    // <e> NRF_FSTORAGE_ENABLED - nrf_fstorage - Flash abstraction library
    //==========================================================
    #ifndef NRF_FSTORAGE_ENABLED
    #define NRF_FSTORAGE_ENABLED 1
    #endif
    // <h> nrf_fstorage - Common settings
    
    // <i> Common settings to all fstorage implementations
    //==========================================================
    // <q> NRF_FSTORAGE_PARAM_CHECK_DISABLED  - Disable user input validation
     
    
    // <i> If selected, use ASSERT to validate user input.
    // <i> This effectively removes user input validation in production code.
    // <i> Recommended setting: OFF, only enable this setting if size is a major concern.
    
    #ifndef NRF_FSTORAGE_PARAM_CHECK_DISABLED
    #define NRF_FSTORAGE_PARAM_CHECK_DISABLED 0
    #endif
    
    // </h> 
    //==========================================================
    
    // <h> nrf_fstorage_sd - Implementation using the SoftDevice
    
    // <i> Configuration options for the fstorage implementation using the SoftDevice
    //==========================================================
    // <o> NRF_FSTORAGE_SD_QUEUE_SIZE - Size of the internal queue of operations 
    // <i> Increase this value if API calls frequently return the error @ref NRF_ERROR_NO_MEM.
    
    #ifndef NRF_FSTORAGE_SD_QUEUE_SIZE
    #define NRF_FSTORAGE_SD_QUEUE_SIZE 8
    #endif
    
    // <o> NRF_FSTORAGE_SD_MAX_RETRIES - Maximum number of attempts at executing an operation when the SoftDevice is busy 
    // <i> Increase this value if events frequently return the @ref NRF_ERROR_TIMEOUT error.
    // <i> The SoftDevice might fail to schedule flash access due to high BLE activity.
    
    #ifndef NRF_FSTORAGE_SD_MAX_RETRIES
    #define NRF_FSTORAGE_SD_MAX_RETRIES 8
    #endif
    
    // <o> NRF_FSTORAGE_SD_MAX_WRITE_SIZE - Maximum number of bytes to be written to flash in a single operation 
    // <i> This value must be a multiple of four.
    // <i> Lowering this value can increase the chances of the SoftDevice being able to execute flash operations in between radio activity.
    // <i> This value is bound by the maximum number of bytes that can be written to flash in a single call to @ref sd_flash_write.
    // <i> That is 1024 bytes for nRF51 ICs and 4096 bytes for nRF52 ICs.
    
    #ifndef NRF_FSTORAGE_SD_MAX_WRITE_SIZE
    #define NRF_FSTORAGE_SD_MAX_WRITE_SIZE 1024
    #endif
    
    // </h> 
    //==========================================================
    
    // </e>

  • I have used your SDK_config.h file and still able to build and run your code without any issues. Seems like the program is stuck in your case not because of the code snippet you produced. I am attaching my whole project for you to run and test if you want to see.

    7673.ble_app_gls.zip

Related