Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedded C++11 code — do I need volatile?

Embedded device with Cortex M3 MCU(STM32F1). It has embedded flash(64K). MCU firmware can reprogram flash sectors at runtime; this is done by Flash Memory Controller(FMC) registers (so it's not as easy as a=b). FMC gets buffer pointer and burns data to some flash sector.

I want to use the last flash sector for device configuration parameters. Parameters are stored in a packed struct with arrays and contain some custom classes.

Parameters can be changed at runtime (copy to RAM, change and burn back to flash using FMC).

So there are some questions about that:

  1. State (bitwise) of parameters struct is changed by FMC hardware. C++ compiler does not know if it was changed or not. Does this mean I should declare all struct members as volatile? I think YES.

  2. Struct should be statically initialized (default parameters) at compile time. Struct should be POD (TriviallyCopyable and has standard layout). Remember, there are some custom classes in there, so I keep in mind these classes should be POD too. BUT there are some problems: cppreference.com

    The only trivially copyable types are scalar types, trivially copyable classes, and arrays of such types/classes (possibly const-qualified, but not volatile-qualified).

That means I can't keep my class both POD and volatile? So how would I solve the problem?

It is possible to use only scalar types in parameters struct but it may result in much less clean code around config processing...

P.S. It works even without volatile, but I am afraid someday, some smart LTO compiler will see static initialized, not changing (by C++) struct and optimize out some access to underlying memory adresses. That means fresh programmed parameters will not be applied because they were inlined by the compiler.

EDIT: It is possible to solve problem without using volatile. And it seems to be more correct.

You need define config struct variable in separate translation unit(.cpp file) and do not initialize variable to avoid values substitution during LTO. If not using LTO - all be OK because optimizations are done in one translation unit at a time, so variables with static storage duration and external linkage defined in dedicated translation unit should not be optimized out. Only LTO can throw it away or make values substitution without issuing memory fetches. Especially when defining variable as a const. I think it is OK to initialize variable if not using LTO.

like image 776
sigmaN Avatar asked Sep 27 '22 09:09

sigmaN


2 Answers

You have some choices depending on your compiler:

  • You can declare a pointer to the structure and initialize the pointer to the region.
  • Tell the compiler where the variable should reside

Pointer to Flash

Declare a pointer, of the structure.
Assign the pointer to the proper address in Flash.
Access the variables by dereferencing the pointer.
The pointer should be declared, and assigned, as a constant pointer to constant data.

Telling compiler address of variable.

Some compilers allow you to place a variable in a specific memory region. The first step is to create a region in the linker command file. Next step is to tell the compiler that the variable is in that region.

Again, the variable should be declared as "static const". The "static" because there is only 1 instance. The "const" because Flash memory is read-only for most of the time.

Flash Memory: Volatile vs. Const

In most cases, the Flash memory, however programmed, is read-only. In fact, the only way you can read the data in Flash is to lock it, a.k.a. make it read-only. In general, it won't be changed without concensus of the program.

Most Flash memories are programmed by the software. Normally, this is your program. If your program is going to reprogram the Flash, it knows the values have been changed. This is akin to writing to RAM. The program changed the value, not the hardware. Thus the Flash is not volatile.

My experience is that Flash can be programmed by another means, usually when your program is not running. In that case, it is still not volatile because your program is not running. The Flash is still read-only.

The Flash will be volatile, if and only if, another task or thread of execution programs the flash while your thread of execution is active. I still would not consider this case as volatile. This would be a case in syncronicity -- if the flash is modified, then some listeners should be notified.

Summary

The Flash memory is best treated as read-only memory. Variables residing in Flash are accessed via pointer for best portability, although some compilers and linkers allow you to declare variables at specific, hard-coded addresses. The variables should be declared as const static so that the compiler can emit code to access the variables directly, vs. copying on the stack. If the Flash is programmed by another task or thread of execution, this is a synchronicity issue, not one of volatile. In rare cases, the Flash is programmed by an external source while your program is executed.

Your program should provide checksums or other methods to determine if the content has changed, since the last time it was checked.

DO NOT HAVE COMPILER INITIALIZE VARIABLES FROM FLASH.
This is not really portable. A better method is to have your initialization code load the variable(s) from flash. Making the compiler load your variable from a different segment requires a lot of work with the internals of the compiler and linker; a lot more than initializing a pointer to the address in the Flash.

like image 56
Thomas Matthews Avatar answered Oct 19 '22 23:10

Thomas Matthews


By reprogramming the flash, you are changing the underlying object's representation. The volatile qualifier is the appropriate solution for the situation to ensure the changes in data are not optimized away.

You would like a declaration to be: const volatile Settings settings;

The drawback is that volatile prevents static initialization of your object. This stops you from using the linker to put the initialized object in its appropriate memory address.

You would like the definition to be: const Settings settings = { ... };

Luckily, you can initialize a const object and access it as a const volatile.


// Header file
struct Settings { ... };
extern const volatile Settings& settings;

// Source file
static const Settings init_settings = { ... };
const volatile Settings& settings = init_settings;

The init_settings object is statically initialized, but all accesses through the settings reference are treated as volatile.

Please note, though, modifying an object defined as const is undefined behavior.

like image 37
D Krueger Avatar answered Oct 20 '22 00:10

D Krueger