Persistent variables

One day when developing some embedded C software I came across a problem which required persistent variables. Usually to make a variable persistent in the program, you make it global, or declare it as 'static' inside a function. However, what I was really after was a variable that retained its value after a software reset. Variables reside in RAM, which is often zeroed by an startup assembly script before the program branches to the main() function. The specific task was to log the last state my code was in, if it got stuck (and had to 'rescued' via a watch dog reset).

As it turns out, making a variable persistent between software resets is not difficult. In some toolchains (such as the gcc-avr/WinAVR etc) this functionality is there already, and you just need to tell the compiler to make this variable persistent. If you are using the yagarto toolchain (or another toolchain for ARM processors), you probably need to add this functionality. To do so, declare a special 'section' in the linker script. I called this '.noinit', and based it off the '.bss' section that was already there. For example, I put the following after the '.bss' section in my project's linker script: 

.noinit :
{
  __noinit_start = . ;
  __noinit_start__ = . ;
  *(.noinit*)
} >DATA
. = ALIGN(4);
__noinit_end__ = . ;
__noinit_end__ = . ;

Note that the dot is a special variable in the linker script referred to the 'current output location counter'. It is handy as the linker decides how big it needs to make each of the sections (it defines the start and end address variables of the sections, which is used in the startup assembly code), rather than you having to manually change things. Finally, to make a global variable v persistent in your C program:

uint32_t v __attribute__((section(".noinit")));

Note that you only need to declare variable's attribute once where it is instantiated. For instance, if v is global and declared in a *.c file, but is used in another *.c file, you just need to tell this second file of its existence by 'extern uint32_t v'. This also goes for assigning attributes to functions as well - just do this for its prototype.

The variable in RAM might also persist after a hard-reset. This depends on how long the power was off for and the specific memory in your microcontroller. To determine if the contents of RAM has been retained, I assign a magic number to persistent variable. This number is just a recognisable bit pattern (101101001...) i.e., 0x5A5A... 

Share |