www.panelsoft.com

 

 

 

Home

Training

Reading

PanelSoft

User Interfaces and Usability for Embedded Systems


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.

 


[PanelSoft Home | Training Courses ]