nRF5 SDK v11.0.0
Usage

The following code examples show a typical usage of section variables in an application.

The examples are taken from the actual usage by Experimental: Flash Storage and the Experimental: Flash Data Storage module.

Registration

The following code registers a named section as a symbol. It is not required by all compilers, but it should be invoked to keep the code portable. This code should be placed in the code unit that accesses registered items in the named section:

The following code registers the type fs_config_t to be stored in the named section called fs_data. This code should be placed in the code unit that accesses variables in the named section:

In the header file of the module that is providing the section variables, you should generate a macro to simplify the registration. This macro should be defined in a header file that the registrants include:

// This macro is expected to be invoked in the code unit that requires
// flash storage. Invoking this places the registered configuration variable
// in a section named "fs_data" that the FStorage module uses during initialization
// and regular operation.
#define FS_SECTION_VARS_ADD(type_def) NRF_SECTION_VARS_ADD(fs_data, type_def)

This new macro will fill in the name of the named section for all registration calls. Every code unit that requires a registration must invoke this macro to place a configuration inside the named section. The call should be in the code unit.

Here is an example of how this is done in fds.c:

FS_SECTION_VARS_ADD( fs_config_t fs_config ) = { .cb = &fs_callback, .num_pages = FDS_MAX_PAGES };

This becomes a statically placed instance of fs_config that can be used to call into the module that uses section variables. Note that const data must be initialized using an initializer list. This safeguards the data from being corrupted.

If the user of the registered variables does not require to update data inside the registrations, it is possible to add a const modifier for the call. In this case, the named section will be placed in flash:

FS_SECTION_VARS_ADD( const fs_config_t fs_config ) = { .cb = &fs_callback, .num_pages = FDS_MAX_PAGES };

Calls

One of the benefits of using section variables is that it is no longer required to keep a registration token for all calls to be able to distinguish where the call came from, which the module needs to locate on every call. The registered entry in the named section acts as a registration token as well.

An example is the following call to erase all pages for a registration in FStorage:

retval = fs_erase(&fs_config, fs_config.p_start_addr, fs_config.num_pages * FS_PAGE_SIZE_WORDS);

Module usage

The module that provides section variables must know the start and end address of the named section and must be able to get the count to be able to iterate over all registered items.

Some convenience macros have been generated in fstorage.c for internal use. These macros point to the relevant macro declarations in section_vars.h:

#define FS_SECTION_VARS_GET(i) NRF_SECTION_VARS_GET(i, fs_config_t, fs_data) /**< Get section variable with FStorage configuration by index. */
#define FS_SECTION_VARS_COUNT NRF_SECTION_VARS_COUNT(fs_config_t, fs_data) /**< Get the number of registered section variables. */
#define FS_SECTION_VARS_START_ADDR NRF_SECTION_VARS_START_ADDR(fs_data) /**< Get the start address of the registered section variables. */
#define FS_SECTION_VARS_END_ADDR NRF_SECTION_VARS_END_ADDR(fs_data) /**< Get the end address of the registered section variables. */

Every function request requires the registered section variable as a parameter. To verify the call from a registered user, it is possible to use the macros for the start and end addresses of the named section to see that the registration is correct. Here is an example of such a function:

static bool check_config(fs_config_t const * const p_config)
{
if (p_config == NULL)
{
return false;
}
if ((FS_SECTION_VARS_START_ADDR <= (uint32_t)p_config) && ((uint32_t)p_config < FS_SECTION_VARS_END_ADDR))
{
return true;
}
else
{
return false;
}
}

You can iterate through the elements in an init function or similar in the following way (using the convenience macros described before):

static void init_func( void )
{
for( int i = 0; i < FS_SECTION_VARS_COUNT; i++)
{
fs_config * p_config = FS_SECTION_VARS_GET(i);
// Add initialization code here
}
}

Linker scripts

Keil does not require linker scripts. Named sections are generated automatically when registration macros are invoked.

For GCC compilations, you must add a definition for a linker script used in the compilation.This addition should be at the root level of the linker script file:

SECTIONS
{
.fs_data_out ALIGN(4):
{
PROVIDE( __start_fs_data = .);
KEEP(*(fs_data))
PROVIDE( __stop_fs_data = .);
} = 0
}

This definition will add a named section fs_data that is aligned to a word and that has __start_fs_data and __stop_fs_data as symbols that are used from the code.

For IAR compilations, you must make additions to an ilink file.


Documentation feedback | Developer Zone | Updated