Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Comments on How to perform initialization of static storage variables in embedded systems?

Parent

How to perform initialization of static storage variables in embedded systems?

+5
−0

Whenever declaring a variable in C outside a function at file scope or when specifying it as static, it gets assigned a life time known as static storage duration. Meaning it will be accessible throughout the whole execution of the program.

Such variables are typically allocated in one of two RAM segments most often named .data or .bss:

  • .data is used for all static storage duration variables that were explicitly initialized by the programmer to a value other than zero.
  • .bss is used for all static storage duration variables that are initialized to zero.

Also as per C language rules, any static storage duration variable which is not initialized to a value explicitly, gets implicitly initialized to zero and therefore ends up in .bss. In case of pointers, they are initialized as null pointers.

On a PC/hosted system, everything including the executable code is simply loaded into RAM at once by the OS. But in case of microcontrollers we have no such thing and often prefer to execute code out of flash/non-volatile memory.

When starting up a new software project in any microcontroller IDE, I am often given an option "minimal/fast start-up" versus "ANSI/ISO/standard start-up". This seems to refer to if these initialization rules should be used or not.

How are these variables initialized in case I pick "minimal" start-up? What are the advantages and disadvantages of this feature?

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

Post
+5
−0

Things happening/things we want to happen before main() is called:

The code executed before main() is often referred to as "the C runtime (CRT)" or just "start-up code". This code is typically provided by the microcontroller-specific libraries and among other things, it initializes .data and .bss - since the C standard requires that static storage duration variables are initialized at some point before main() is called. This is carried out by copying a chunk of initializer values from flash into .data, sometimes referred to as "copy-down", or in case of .bss, the CRT simply sets the whole region to zero.

This initialization potentially takes a lot of time from the point where the microcontroller is started until main() is called. Be aware that some dysfunctionally written CRTs do not set-up funamental things like watchdog and clock settings before running the initialization code, but relies on the application programmer doing that from main(). This is the wrong way to do things - it is both dangerous and also potentially needlessly slow. Suppose that a CPU/software error happens during start-up - then we want the watchdog to be enabled. Or suppose the MCU is running on a slow internal oscillator out of reset, but we would like to use an external one or set up some PLL/FLL, in order to speed up the system clock. If so, we would naturally like to do that way before running the whole .data/.bss initialization.

Either way, there might be lots of other reasons as well why we don't want a lot of slow start-up code executing out of reset:

  • The application might need to perform something quickly when it starts, or
  • In case we encountered critical errors during previous execution that merited a MCU reboot, we might want to get back up running as soon as possible, or
  • In case we are writing safety-related software, RAM could be considered too unreliable to hold default values for long time periods, unless continuously refreshed from non-volatile memory. There could be any amount of time passing from the point where the variable was initialized until it was first used.

Minimal/fast start-up

The minimal start-up option provides a way to skip all initialization and thereby create a significantly shorter start-up time. There are disadvantages of using the minimal start-up, however. First of all, our static storage duration variables end up not initialized. At all. Not to a specific value, not to zero. We have sacrificed C language standard compliance in favour of execution speed.

So if we have a file scope variable declared as int x=5;, we can't actually know what value it has - it is not 5 but indeterminate. If we aren't aware of this, minimal start-up is obviously very dangerous.

Whenever a minimal/fast start-up option is used, variables with static storage duration must be initialized in run-time!

"Run-time" as in not by the CRT, but by the application program. This means that in the above example we should just have declared int x; and then later inside some init() function assign x=5;. Every single variable in the program must be initialized in run-time before use.

Now as it happens, every module in our program is likely to have some initialization function anyway. For example any driver for a hardware peripheral will likely need to initialize a bunch of hardware registers before it does anything else. And then it might as well initialize all variables used by that driver at the same time. This way we move the initialization execution time away from start-up and into the specific code module where the variable is used.

This is the correct way of initializing such variables anyway, because of the previously mentioned "RAM is considered unreliable" reason. Sure, modern microcontroller RAM is way more reliable than what it used to be some decades ago. But like with any electronic component, it is also subject to EMI. And in case of RAM specifically, it is also sensitive to cosmic rays. Plain software bugs is a far more likely reason for RAM corruption however, so not relying on default initialization values is also a method of defensive programming.

(Similarly, executing code out of RAM is considered bad practice on high reliability systems, at least not without error detection mechanisms like ECC and/or CRC checksums.)

Summary

It is considered good practice in any embedded system to never rely on default or explicit initialization of static storage duration variables.

Doing so enables the option for using a minimal start-up, including portability to compilers that may use such a start-up. But it also makes the variables somewhat less vulnerable to problems like EMI, cosmic rays or plain bugs. It is perfectly possible to write fully standard compliant C code still - the minimal start-up makes the CRT non-compliant, not our own source code.

Also, relying on implicit initialization of any variable is considered bad practice, for example static int x;, simply because it is confusing and sloppy. Did the programmer intend to zero-initialize this variable or not? Writing static int x = 0; is self-documenting. Now of course the programmer could also have been an embedded systems veteran who adhere to the best practices default initialization mentioned above and therefore left out the initializer for that purpose.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

2 comment threads

Minimal CRT startup code is not dysfunctional. (7 comments)
Some details to also consider (2 comments)
Some details to also consider
Lundin‭ wrote about 2 years ago

It should be noted that in case of C++, default constructors of objects with static storage duration will also get called by the CRT, adding even more execution time to start-up. C++ has very intricate rules for initialization, especially past C++11, so the standard compliant start-up time in C++ might end up considerably slower than the C equivalent. This could be avoided in various ways too, like ensuring that constructors are only executed when called upon explicitly in run-time.

Lundin‭ wrote about 2 years ago

In case of mid- to high-end MCUs that provide data cache, the .data/.bss initialization will get carried out much faster if done by the CRT than if done by individual application modules. This is simply because the MCU can utilize the cache much more efficiently if chewing to through a chunk of contiguously allocated RAM. Still, you might not want this to happen during start-up however.