Help on Prevention of heap being cleared as result of Reset!

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Again Friends?

I have been told i have to use .noninit attribute when declaring a certain variable
of which I won't want to be initialized as result of Reset such as WDT.
Actually if i remember well RAM isn't cleared as result of Reset such as WDT but This rather have something to do with GCC itself at start code where a piece of code is run before the "main" function Although I don't remember deep into this now ':oops:'

Well the question was simple. Depending on my project's structure and OS, i have quite few time-out variables (which are being decremented or incremented) to keep in track of certain events. These variables have to remain active (without being initialized) for quite few seconds unless otherwise they are initialized manually somewhere within the code!

The Problem was then that there is a WDT which is reseting an MCU in about every 500ms! which clears means none of these time- out variable will ever make it as they took far longer that the reseting time. I also think that many resets from WDT reset will probably create some delaying offset on these time- out variable values but again this is far better than starting to zero ':twisted:'

I have been reading on quite few forums online but not all quite answered my actual question with example. So briefly i needed a quick help and maybe a snippet or piece of code showing how to simply declare the .noninit variable that would warn the GCC to not initialize that particular variable at the start up.

Many Thanks Again.

Regards.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

This rather have something to do with GCC itself at start code where a piece of code is run before the "main" function Although I don't remember deep into this now

Your memory is correct. When built the compiler knows how many variables you have in .bss (globals without initialisation) and how many are in .data. So if there are .data variables it adds a loop to the startup code called _do_copy_data and this arranges to use an LPM loop to read the initial values from flash (they are tagged onto the end of the flash image after .text). If there are uninitialised globals then there is also a _do_clear_bss loop added that sets up a pointer to the start of .bss in RAM and for the (known) length of .bss it writes 0 to those locations. These two thus guarantee what's promised by the C standard that globals with initialisers will be found to hold those initial values and globals without will be guaranteed to hold 0x00. (when I say "globals" in this I also mean "statics" even though they don't have global name scope - in terms of the storage they are the same).

GCC (unlike some other C compilers) does not have code that would change any other RAM locations. So a heap will be left as it was before(*) and so will variables in private sections and also the default section called .noinit if you choose to use that.

(*) the heap itself may remain intact but the base pointers (4 16 bit values ISTR) are.bss/.data variables and will be initialised - so this is why the heap will appear to be "empty". Also the pointers you used to point to the malloc'd blocks will likely either have been autos or .bss variables and wil equally have lost their contents.

Your core problem here is not how to paper over things when an unexpected WDT reset occurs but to prevent it happening in the first place! You use the WDT to dig the code out of a hole. The way to "fix" this is not to allow it to get into the hole in the first place.

#include 

int var_in_data = 12345;
char buffer_in_bss[10];

int main(void) {
	static unsigned int also_in_data = 0xDEAD;
	static int in_bss_too;
}

Code to copy .data vars from flash to RAM, starts at 0x0060, ends before 0x0068:

00000060 <__do_copy_data>:
  60:	10 e0       	ldi	r17, 0x00	; 0
  62:	a0 e6       	ldi	r26, 0x60	; 96
  64:	b0 e0       	ldi	r27, 0x00	; 0
  66:	ee ea       	ldi	r30, 0xAE	; 174
  68:	f0 e0       	ldi	r31, 0x00	; 0
  6a:	02 c0       	rjmp	.+4      	; 0x70 <.do_copy_data_start>

0000006c <.do_copy_data_loop>:
  6c:	05 90       	lpm	r0, Z+
  6e:	0d 92       	st	X+, r0

00000070 <.do_copy_data_start>:
  70:	a8 36       	cpi	r26, 0x68	; 104
  72:	b1 07       	cpc	r27, r17
  74:	d9 f7       	brne	.-10     	; 0x6c <.do_copy_data_loop>

Code to wipe .bss:

00000076 <__do_clear_bss>:
  76:	10 e0       	ldi	r17, 0x00	; 0
  78:	a8 e6       	ldi	r26, 0x68	; 104
  7a:	b0 e0       	ldi	r27, 0x00	; 0
  7c:	01 c0       	rjmp	.+2      	; 0x80 <.do_clear_bss_start>

0000007e <.do_clear_bss_loop>:
  7e:	1d 92       	st	X+, r1

00000080 <.do_clear_bss_start>:
  80:	a4 37       	cpi	r26, 0x74	; 116
  82:	b1 07       	cpc	r27, r17
  84:	e1 f7       	brne	.-8      	; 0x7e <.do_clear_bss_loop>

Then from the map (only globals not statics listed):

 .data          0x00800060        0x8 test.o
                0x00800060                var_in_data
 .data          0x00800068

and

 .bss           0x00800068        0x2 test.o
 .bss           0x0080006a

as well as:

 *(.bss*)
 *(COMMON)
 COMMON         0x0080006a        0xa test.o
                0x0080006a                buffer_in_bss
                0x00800074

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Preserving data during a watchdog reset might be useful to figure out what caused the reset.
Note that if one wants, one can distinguish between a power-on reset and other resets.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@ Sleeve
Thank you for your advise too
But would we really care about what is the cause of the reset in this case
As the variables are being initialized to zero upon any reset which is what we don't want? forgiving me if you think this is silly But i dont see what why we do need to know the cause of the reset in this manner.

@ Cliff
Thank you for your extended examples too. I do understand that variables located into .bss section such as global and local are being zero'ed when at start up. right?
so will declaring something like this solve seal the deal?

uint16_t time_out __attribute__ ((section (".noinit")));

If I understood well, time_out is still within .bss section but in this case we are forcing the compiler not to initialize to zero on start up?

Many Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can imagine one would like to change the behaviour depending on the reset source.

E.g. if was done by the watchdog, chances are that the stored values are bogus, because of code that got in trouble.

On powerup the values are bogus anyway so you might need separate handling of that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

If I understood well, time_out is still within .bss section

Err no, it's in the .noinit section:

#include 

uint16_t time_out __attribute__ ((section (".noinit"))); 
uint16_t data_var = 12345;
uint16_t bss_var;

int main(void) {
}
 .data          0x00800060        0x2 test.o
                0x00800060                data_var
 .data          0x00800062
...
 *(.bss*)
 *(COMMON)
 COMMON         0x00800062        0x2 test.o
                0x00800062                bss_var
                0x00800064
...
 .noinit        0x00800064        0x2 test.o
                0x00800064                time_out
                0x00800066

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks everyone for your contribution, also forgive me if i am being silly in these FAQs, I really have a lot to learn here.
By saying time_out was still within .bss was because i have been reading somewhere at www.nongnu.org/avr-libc/user-man...
and it was saying something like

This sections is a part of the .bss section. 
What makes the .noinit section special is that variables which are defined as such:
int foo __attribute__ ((section (".noinit"))); will not be initialized to zero during startup as would normal .bss data.

I therefore thought that the two might be sharing the same memory section which obviously wasn't the case as cliff's output code made it clear now.

So Other than declaring the variable you don't want to be initialized on start as

uint16_t foo __attribute__ ((section (".noinit")));

Is there anything else needed?

Cheers.