In an embedded device, during the initializing of memory locations, is there any convention that are being practiced.
I mean, say setting every byte to zero or 0xFF or any other value.
Software Engineering Stack Exchange is a question and answer site for professionals, academics, and students working within the systems development life cycle. It only takes a minute to sign up.Sign up to join this community
Modern embedded devices often have (at least) 3 kinds of memory. If you are in charge of the startup code, you might consider doing something like this:
At boot time (on power-up, and also whenever the watchdog times out), typical startup code ("
will zero out all the RAM, then copy a block of data data (the data segment) from program memory to RAM, and then call
This is the simplest way to conform to C requirements for (static and global) initialized variables, the ".bss segment" and the ".data segment", without wasting flash memory on variables initialized to all-zeros.
Assembly language programmers often write two loops in their code to do this early in the program; most other programmers don't write any specific code to do this, instead depending on the "
crt0" runtime which typically does this for them before their first line of code executes.
This doesn't mean that an uninitialized value (heap or stack) will be zero. If you assume that it is zero, it may seem to work the first time, but after the program runs for a while and re-uses heap and stack a few times, it probably will no longer be zero, leading to intermittent and difficult-to-debug errors.
As Autocracy pointed out, many debugging tools set un-initialized RAM to some other magic value, to more quickly detect this "incorrectly assumed it was zero" bug and other bugs.
A few bit-flip soft-error-tolerant programs re-initialize RAM from ROM as often as possible -- there is no "initialization" code, all the initialization stuff happens inside the main loop. The stack pointer is re-initialized to the beginning of stack RAM at the beginning of each and every pass through the main loop, the baud rate registers are re-set to give 9600 bps each pass through the main loop, the complete time and day is read from the RTCC once a second even when you're pretty sure it's still Wednesday, etc.
Flash data memory: A program that writes new data into a block of flash memory must first erase the entire block. Erasing a block typically takes orders of magnitude more time than reading a block of data from flash. Immediately after erasing, the value of such "blank" memory locations is -- for no good reason that I am aware of -- the all-ones pattern 0xFF. The common convention is to leave unused blocks as that 0xFF pattern, and to immediately "pre-erase" data blocks as soon as they are are no longer needed to that 0xFF pattern, so that later when we need to store data into that block, we can immediately write the data (or at least we only need to wait the remaining time-to-erase).
Program memory (often in ROM or flash memory):
At boot time, the CPU will start executing the startup code ("
crt0") in program memory.
When burning a new program into program memory,
many toolchains will erase the old program and leave memory that is unused by the new program in the unprogrammed state 0xFF.
As JThompson pointed out, other, more error-tolerant toolchains will use immunity aware programming techniques, filling up all unused program memory (on the off chance that the program counter somehow gets corrupted and then points into that unused memory) with jumps to the reset vector or a nop sled that eventually goes to the reset vector.
The answer is: It Depends.
If you program in C, the language definition states that only static variables that are not initialized will be set to 0 at program start time. If you look at the typical run-time system provided by embedded compiler makers, most include either some or all source code, and you can see there explicit zeroing of the section used for non-const variables.
Variables declared const are usually placed in a separate memory section, frequently ROM or flash EPROM. And initialized variables have the initial values gathered into a block of ROM or Flash, and at program start this block is copied to the ram section for "initialized variables".
If you program in other languages, then the behavior depends on the language and the startup behavior of the run-time.
If you consider an embedded micro with peripheral devices, for example UARTs or Capture blocks, these will always have a start-up definition provided by the chip maker. However my GOLDEN RULE is to never ever rely on this. If you want a peripheral to be initialized to a particular state, then initialize it. Every bit of every register, whether you think its is needed or not. This way YOU are always in control of the device state. [You know how sometimes a PC device turns to crap, eg USB won't work... and after a reboot it still won't work. But after a power cycle it does. Well guess what. Somebody made an assumption in the driver and did not initialise every bit of every register. Bad!]
Actually, I have a few golden rules for embedded device programming. There are more, but these are those about initialization, startup and running.
Never assume anything. Initialize all your peripherals (as above).
In every code unit, have a explicit exported "init" and "shutdown" functions - EVEN IF THESE ARE EMPTY. You can add stuff into them with no change elsewhere should you need to later.
In every code unit, the "init" function initialises ALL unit-local variables (ie statics declared in that unit and not seen by any other unit), INCLUDING setting to 0 those that should start out set to 0. This means you don't rely on the run-time setting things to 0. If you want it 0, set it 0. [I once used a non-compliant compiler that did not initialise... boy was that a learning experience.]
Separate devices and device behavior as far as possible into hardware abstraction layers that you write (even these are just macros that use something provided by the vendor). If you need to change hardware platform later, this will reduce your migration time to about 20% of what it might otherwise be. This is not always possible, but it is worth trying.
Make the main program small - it should simply call the init functions in EVERY code unit, and then loop forever processing the "main loop" stuff. You can then use flags or other things hidden inside APIs to make events happen, and timers and timed behaviour become easy - if the main loop gets run off a major (interrupt based) timer that fires at regular intervals (for example, 2, 5, 10, 12, 16, 20 milliseconds - are all common) then time-based events are easy, and delays are just countdown counters that work off multiples of the main-loop interval time. You main program should be somewhere in the region 50 - 200 lines of code, max.
Hope this helps. Say so if you want a few more practices that make embedded devices robust.
What to do with unused memory can also depend on the intended use. For safety critical software, it is common to fill unused memory with commands that force the system into a safe state. As a rudimentary example, software that controls an oven might have all of its unused memory locations filled with jump commands that jump to a safe state function which would ensure that the oven is turned off. This way, if the program counter is inadvertently corrupted (darn those pesky sunspots!), you could at least increase your chances of handling it safely.
So, not so much standardized... Expect there could be ANYTHING there if you didn't set it yourself.