how to not initialize variables

Suppose I want to implement a "hard reset counter" for none power cycling resets. In order to do so I need a variable that will not get automatically initialized. Without Arduino this is pretty simple. I follow the documentation http://www.nongnu.org/avr-libc/user-manual/mem_sections.html and thus implement something like

uint8_t reset_counter __attribute__ ((section (".noinit")));

void setup() {                
  ++reset_counter;
}

Of course I know the dangers of not initialized variables and I am aware that this is not reliable at all. However if I compile from the command line then this works as expected. If I use the Arduino Ide though it does not.

If I implement

uint8_t reset_counter __attribute__ ((section (".noinit"))) = 1;

and try to compile from the command line it will lead to an error (as expected). However with the Arduino IDE I can compile it. Thus I suspect that the IDE overwrites the section attribute somehow. Hence the variable ends up in the wrong section and thus gets initialized.

Has anyone a hint how to tell the Arduino IDE to not mess with my declarations?

Udo

Normally for tasks like that people use the chips EEPROM memory to write and then read back values they want to survive resets and power cycles. The standard Arduino uploading does not erase EEPROM contents so you are free to use them for such tasks.

Lefty

Of course I know how to do this by EEPROM. The question was more like an example and the code is for my understanding. What does the IDE do to my code? And why does it need to mess with it that way? This is the real question.

[quote author=Udo Klein link=topic=56809.msg407995#msg407995 date=1301330313] Of course I know how to do this by EEPROM. The question was more like an example and the code is for my understanding. What does the IDE do to my code? And why does it need to mess with it that way? This is the real question.

[/quote]

I think it's a compiler requirement and part of the 'C/C++' standards rather then a Arduino IDE caused issue. However when it comes to software issues like this I'm more talking through my a$$ then my brain. :D

Lefty

I suspect it's because Arduino builds your code with --gc-sections which will throw away "unused" sections. I think I read problems on avrfreaks.net of the linker being too aggressive about throwing away sections. You could try asking there. You could also try playing with different options by modifying /app/src/processing/app/debug/Compiler.java

I’m not sure I understand the problem. The first example builds fine for me. So far as I can work out the object code looks about the way I would expect. I guess it does not do what you expect – is that it? What actually happens to reset_counter across resets?

Disassembly of section .text.setup:

00000000 <setup>:
   0:   80 91 00 00     lds     r24, 0x0000
                        2: R_AVR_16     .noinit
   4:   8f 5f           subi    r24, 0xFF       ; 255
   6:   80 93 00 00     sts     0x0000, r24
                        8: R_AVR_16     .noinit
   a:   08 95           ret

Disassembly of section .text.loop:

00000000 <loop>:
   0:   08 95           ret

Disassembly of section .noinit:

00000000 <reset_counter>:
        ...

the ELF output looks about what I would expect:

Disassembly of section .bss:

00800200 <__bss_start>:
  800200:	00 00       	nop
	...

00800204 <timer0_millis>:
  800204:	00 00       	nop
	...

00800208 <timer0_fract>:
	...

Disassembly of section .noinit:

00800209 <reset_counter>:
	...

         :
         :

0000012c <setup>:
 12c:	80 91 09 02 	lds	r24, 0x0209
 130:	8f 5f       	subi	r24, 0xFF	; 255
 132:	80 93 09 02 	sts	0x0209, r24
 136:	08 95       	ret
uint8_t reset_counter __attribute__ ((section (".noinit")));

void setup() {                  ++reset_counter; }



if I compile from the command line then this works as expected. If I use the Arduino Ide though it does not.

In what way does it not work when using the Arduino IDE? It worked for me; even incremented on each reset, as expected (but all I did was add this to Blink, which is pretty trivial...)

As I said it initialized the variable. Of course to 0. From the command line it worked fine. As soon as I can spare some time I will disassemble and see what the compiler created.

Are you forgetting that Sram will get trashed with no power? I understand that you are counting soft resets that happen with power on, but then, if a programm needs to reset itself to work...

No, I do not forget that SRAM requires power. The example is to count any reset but a power on reset. I am fully aware that there are absolutely no guarantees about the contents of this variable at power up. I can confirm that I am just fooling around and not building any mission critical system based on this approach ;) However I expect it to increase consistently.

It worked for me too, as described by westfw.

I uploaded this sketch (using the normal IDE):

uint8_t reset_counter __attribute__ ((section (".noinit")));

void setup ()
{
  Serial.begin (115200);
  ++reset_counter;
  Serial.println (reset_counter, DEC);
}

void loop () {}

Then I sat there hitting the reset button on the board:

12
13
14
15
16
17

So, it counted resets.

Interesting. I am currently running Arduino 0021 on Ubuntu Linux and the sketch consistently returns 1. So obviously my idea works for other people but not for me. I definitely need to have a look at my IDE's compiler setup.

[quote author=Udo Klein link=topic=56809.msg409275#msg409275 date=1301433593] Arduino 0021 on Ubuntu Linux [/quote]

Read this thread and go update your AVR-GCC...

http://arduino.cc/forum/index.php/topic,56894.0.html

I will try but I doubt that this is the case. Notice that the code works if I compile it from the commandline.

Same thing here. Compilation with the hacked text editor returns “1” all the time. Using the other method works.

I used code::blocks with the following settings:

a) for building the core.a file:

		<Compiler>
			<Add option="-Os" />
			<Add option="-Wmain" />
			<Add option="-Wall" />
			<Add option="-g" />
			<Add option="-mmcu=atmega168p" />
			<Add option="-fno-exceptions -ffunction-sections -fdata-sections" />
			<Add option="-DF_CPU=16000000UL" />
		</Compiler>
		<Linker>
			<Add option="-Os" />
			<Add option="-mmcu=atmega168p" />
			<Add option="-Wl,-Map=$(TARGET_OUTPUT_FILE).map,--cref" />
		</Linker>
		<ExtraCommands>
			<Add after="$(build_dir)/make_core.sh $(obj_dir)" />
			<Mode after="always" />
		</ExtraCommands>

b) for building the hex-file of main.cpp:

		<Compiler>
			<Add option="-Os" />
			<Add option="-Wmain" />
			<Add option="-Wall" />
			<Add option="-g" />
			<Add option="-mmcu=atmega168p" />
			<Add option="-fno-exceptions -ffunction-sections -fdata-sections" />
			<Add option="-DF_CPU=16000000UL" />
			<Add directory="../arduino_core_libs" />
			<Add directory="../arduino_core_libs/Wire" />
		</Compiler>
		<Linker>
			<Add option="-Os" />
			<Add option="-mmcu=atmega168p" />
			<Add option="-Wl,-Map=$(TARGET_OUTPUT_FILE).map,--cref" />
			<Add option="-Wl,--gc-sections" />
			<Add library="../arduino_core_libs/core.a" />
			<Add library="/usr/local/avr/avr/lib/libm.a" />
			<Add directory="../arduino_core_libs" />
			<Add directory="../arduino_core_libs/Wire" />
		</Linker>

I have put the compiler options to verbose

avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/WInterrupts.c -o/tmp/build2560460169561155056.tmp/WInterrupts.c.o 
avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/pins_arduino.c -o/tmp/build2560460169561155056.tmp/pins_arduino.c.o 
avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/wiring_pulse.c -o/tmp/build2560460169561155056.tmp/wiring_pulse.c.o 
avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/wiring_shift.c -o/tmp/build2560460169561155056.tmp/wiring_shift.c.o 
avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/wiring_digital.c -o/tmp/build2560460169561155056.tmp/wiring_digital.c.o 
avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/wiring_analog.c -o/tmp/build2560460169561155056.tmp/wiring_analog.c.o 
avr-gcc -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/wiring.c -o/tmp/build2560460169561155056.tmp/wiring.c.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/Print.cpp -o/tmp/build2560460169561155056.tmp/Print.cpp.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/main.cpp -o/tmp/build2560460169561155056.tmp/main.cpp.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/Tone.cpp -o/tmp/build2560460169561155056.tmp/Tone.cpp.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/WMath.cpp -o/tmp/build2560460169561155056.tmp/WMath.cpp.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/HardwareSerial.cpp -o/tmp/build2560460169561155056.tmp/HardwareSerial.cpp.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino/WString.cpp -o/tmp/build2560460169561155056.tmp/WString.cpp.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/WInterrupts.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/pins_arduino.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/wiring_pulse.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/wiring_shift.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/wiring_digital.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/wiring_analog.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/wiring.c.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/Print.cpp.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/main.cpp.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/Tone.cpp.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/WMath.cpp.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/HardwareSerial.cpp.o 
avr-ar rcs /tmp/build2560460169561155056.tmp/core.a /tmp/build2560460169561155056.tmp/WString.cpp.o 
avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=21 -I/home/udo/Desktop/electronics/arduino-0021/hardware/arduino/cores/arduino /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp -o/tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.o 
avr-gcc -Os -Wl,--gc-sections -mmcu=atmega328p -o /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.elf /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.o /tmp/build2560460169561155056.tmp/core.a -L/tmp/build2560460169561155056.tmp -lm 
avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.elf /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.eep 
avr-objcopy -O ihex -R .eeprom /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.elf /tmp/build2560460169561155056.tmp/sketch_mar31a.cpp.hex 
Binary sketch size: 2436 bytes (of a 30720 byte maximum)

I still do not get it though. Any hints for me?

Is it possible you have more than one "avr-gcc" on the computer? Is the Arduino IDE using one compiler version and the makefile using a different compiler version?

Ah, good question. Never thought of that.
Double checked:
find, locate, whereis all agree that I have only one version.
→ this is not the reason for the strange behaviour.

[quote author=Udo Klein link=topic=56809.msg410946#msg410946 date=1301602257] Any hints for me? [/quote]

Have you built the project both ways and dumped the final elf with avr-objdump to get a feel for what the substance of the difference is? There might be a clue there.

Have you in fact brought your avr-gcc installation up to date?

[quote author=Udo Klein link=topic=56809.msg410946#msg410946 date=1301602257] I still do not get it though. Any hints for me? [/quote]

What is your ultimate goal here? To resolve this issue with the compiler? Or to count resets? Personally I had never heard of :

uint8_t reset_counter __attribute__ ((section (".noinit")));

... until I saw this thread.

You are basically wanting to take some RAM with undefined data in it, and add 1 to it on reset, yes? And if there is a way of detecting a power-on reset, as opposed to a "reset button" reset, you might conceivably initialize it on power-on.

Well, here is how to get unitialized RAM:

int * counter;

void setup() { 

  Serial.begin (115200);
  
  // get some uninitialized RAM  
  counter = (int *) malloc (sizeof (int));  
  
  // add 1 to it
  *counter++;
  
  Serial.println (*counter);
  
}

void loop() { }

Now I'm assuming here that identical code running after a reset will allocate the same memory address each time (why would it not?). And since malloc doesn't clear memory to zero it won't be initialized. So far so good. But these are my results (hitting reset):

14649
14649
14649
14649
14649
14649

It's not zero, but it's not incrementing either. Why? My guess is that the part of the heap that is allocated in this case happens to have been used previously (as part of reset processing) and happens to have 14648 left over in it (hex 0x3938).

But if this doesn't work, how can you really say that using section (".noinit") will work any better? An uninitialized variable has undefined data in it, right? This isn't guaranteed to persist over resets.

Let me put it like this, as I read the docs on .noinit, they specify the the variable is not initialized to zero. But they don't specify that the variable is untouched (eg. during initializing of other things).

The very fact that the .noinit works for some people and not others would seem to give pause to any idea of using it seriously in any sort of production environment.