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 »

Review Suggested Edit

You can't approve or reject suggested edits because you haven't yet earned the Edit Posts ability.

Approved.
This suggested edit was approved and applied to the post over 1 year ago by Lundin‭.

65 / 255
  • **Things happening/things we want to happen before main() is called:**
  • The code executed before `main()` is called 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.
  • **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.

Suggested over 1 year ago by Lorenzo Donati‭