Feedback to 'Start Me Up'- Murphy's Law, April 2004
return to Murphy's Law
Errata
In the article I
mistakenly state:
"When you forget
to initialize a global value, the chances are that you get a zero,
though this is not certain. The C standard dictates that any static
values that are not explicitly initialized in the code must be
initialized to zero, but non-static values are undefined. "
This is not the
case. All variables declared outside a function, whether the 'static'
keyword is used or not, will be initialised to zero.
Thanks to Victor
Kamensky, Chris Knight and Dave Sinkula for spotting this mistake.
Feedback
Niall,
I've read your recent
article on embedded.com with some interest. Your problem of not
initializing some memory is common.
I've solved it on my systems
by giving the variables I don't want initialized a separate storage
class and linking them outside of the BSS segment, which is the
only one that is guaranteed to be 0 after the system init code runs.
On some compilers, the
0 at init time is not even guaranteed if the startup code can be
built with an option to disable initializing BSS.
The linker is an often
overlooked source of tricks that can make embeddd systems programming
easier.
Best,
Ralph Hempel - P.Eng
At the beginning of
the article, you talk about how you can't declare variables in
C at the point where they're used, as you can in C++.
One trick I often use
is to create a local block with a do { ... } while(0); statement.
While the words "do" and "while" are not necessary in this context,
they help clarify that you want the "local" block to execute exactly
once and prevent questions during a code review about why you suddenly
introduce an empty brace in the middle of a function.
Kevin Becker
Software Engineer
Johnson Controls, Inc
Here's a different
way to backup and restore global variables. This kind of thing
can make for much leaner resource usage. It's a bit more work
to compress the backup buffers, but it can be done easily if you're
motivated.
Mark F. Haigh
/*
* How to backup and restore global variables. You will need to
know the
* details of your system if you expect to be able to get this
to work!
*
* Author: Mark F. Haigh (mfhaigh@acm.org)
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Customize for your linker. Try doing a nm on your finally-linked
executable or reading your linker documentation. */
extern int _data_start__;
extern int _data_end__;
extern int _bss_start__;
extern int _bss_end__;
/* Segment size and backup buffer */
size_t data_size, bss_size;
char *data_bak, *bss_bak;
/* Backup the values of all globals */
void backup_data_bss(void)
{
if(!data_bak) {
data_size = (char *) &_data_end__ - (char *) &_data_start__;
bss_size = (char *) &_bss_end__ - (char *) &_bss_start__;
data_bak = malloc(data_size); /* check return value */
bss_bak = malloc(bss_size); /* check return value */
}
memcpy(data_bak, (char *) &_data_start__, data_size);
memcpy(bss_bak, (char *) &_bss_start__, bss_size);
}
/* Restore all globals to original values */
void restore_data_bss(void)
{
memcpy(&_data_start__, data_bak, data_size);
memcpy(&_bss_start__, bss_bak, bss_size);
}
/* Test program */
int global0; /* .bss */
int global1 = 1; /* .data */
int global2 = 2; /* .data */
int main(void)
{
/* Print original values */
printf("original:\tglobal0: %d\tglobal1: %d\tglobal2: %d\n",
global0, global1, global2);
/* Backup these values */
backup_data_bss();
/* Overwrite with new values */
global0 = 42;
global1 = 43;
global2 = 44;
printf("modified:\tglobal0: %d\tglobal1: %d\tglobal2: %d\n",
global0, global1, global2);
/* Restore to original values */
restore_data_bss();
printf("restored:\tglobal0: %d\tglobal1: %d\tglobal2: %d\n",
global0, global1, global2);
return 0;
}
Hello Mr. Murphy,
I like your column in
embedded systems. At the very least, it makes me think. Your column
on system initialization was very good. There are some points I
would like to make.
In the section titled,
When is a reset, not a reset, you mentioned that you wold not have
had the problem you encountered, if you had jumped to the reset
vector. You could have had a different problem instead.
In many systems, after
reset, the RAM is in an unknown state at power up, so the system
initialization code will clear all of RAM on a reset. For instance,
the COSMIC C initialization code normally clears RAM. In the situation
you described, the system was to preserve some values across a reset.
The reset vector would have destroyed them. While your other variables
would have been properly initialized, your preserved variables would
not have been preserved.
Another common possibility,
is reset could have initiated a hardware test and testing RAM may
have destroyed your preserved variables.
I have been using a 3
stage initialization process for variables:
1. Set all of RAM to a
known value. Most systems use 0, but a value other than 0, like
all bits set or every other bit set (one of my favorites) make the
use of an unitinitalized value more obvious.
2. As you suggest, I then
have an initialization function for each module. Values used here
could be a reasonable value for the system, or if this will be a
slowly initialized variable (such as the output of a filter) I set
the variable to a strange (out of range) value. Again, this helps
me catch values that are used before they are ready.
3. The system then has
an initialization mode, where the "slow initialization" variables
are initialized. At the end of this initialization, all variables
should contain proper values and the system switches to run mode.
All my fuctions generaly
assert for reasonable values, so if a variable is used, before it
is initialized properly, an assertion will fire.
Reading an initial value
directly out of a sensor may be OK, but in a noisy system, you may
get an outlying value, which could affect your other calculations.
Probably not a big problem in a PID controlled, like your example
but if an alarm is set based on the outlier, this could cause a
problem.
Thanks for the article,
Bob Bailey
Senior Software Engineer
Midtronics
Niall's response:
I think you misunderstood
my point about branching to the reset vector. I intended this
to do a full initialisation, where you could not depend on variables
holding their values for the next run. Maybe I did not make this
clear enough in my piece. Your other point about initializing
variables to illegal values, so thay can be caught in an assert
is a good idea.
|