AS7 - __attribute__ ((section (".noinit"))); not valid anymore?

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

Hello All,

 

I've recently migrated to AS7 and discovered that "__attribute__ ((section (".noinit")));" for vars doesn't work anymore... 

 

Any solution? :/

Thanks!

My electronic projects blog >> www.limpkin.fr

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

nobody? :(

My electronic projects blog >> www.limpkin.fr

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

The change (if there is one) is not because of AS6->As7 but because of avr-gcc changing from 4.8.1 to 4.9.2

 

However I wonder on what basis you have proven this anyway? The fact is that it is just a section attribute. Nothing more than that. It just dictates to what section in the linker script the object is assigned. If there's been any kind of operational change then it would be in the linker script itself.

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

Nevermind, found the solution: adding a volatile to the declaration:

 

volatile uint16_t bootloader_start_var __attribute__ ((section (".noinit")));

My electronic projects blog >> www.limpkin.fr

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

Just tried this example:

$ cat avr.c
#include <avr/io.h>

int l __attribute__((section(".noinit")));
int m;
int n = 12345;


int main(void)
{
    while (1) 
    {
    }
}

$ ./avr-gcc -v -mmcu=atmega16 -Os -Wl,-Map,avr.map avr.c -o avr.elf
         [snip]
gcc version 4.9.2 (AVR_8_bit_GNU_Toolchain_3.5.0_1660) 
         [snip]
GNU C (AVR_8_bit_GNU_Toolchain_3.5.0_1660) version 4.9.2 (avr)
    compiled by GNU C version 4.6.4, GMP version 5.0.2, MPFR version 3.0.0, MPC version 0.9

$ cat avr.map
         [snip]

.data           0x0000000000800060        0x2 load address 0x0000000000000098
                0x0000000000800060                PROVIDE (__data_start, .)
 *(.data)
 .data          0x0000000000800060        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/../../../../avr/lib/avr5/crtatmega16.o
 .data          0x0000000000800060        0x2 /tmp/cc5dcwGL.o
                0x0000000000800060                n
 .data          0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/avr5/libgcc.a(_exit.o)
 .data          0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/avr5/libgcc.a(_copy_data.o)
 .data          0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/avr5/libgcc.a(_clear_bss.o)
 *(.data*)
 *(.rodata)
 *(.rodata*)
 *(.gnu.linkonce.d*)
                0x0000000000800062                . = ALIGN (0x2)
                0x0000000000800062                _edata = .
                0x0000000000800062                PROVIDE (__data_end, .)

.bss            0x0000000000800062        0x2
                0x0000000000800062                PROVIDE (__bss_start, .)
 *(.bss)
 .bss           0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/../../../../avr/lib/avr5/crtatmega16.o
 .bss           0x0000000000800062        0x0 /tmp/cc5dcwGL.o
 .bss           0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/avr5/libgcc.a(_exit.o)
 .bss           0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/avr5/libgcc.a(_copy_data.o)
 .bss           0x0000000000800062        0x0 /home/uid23021/windows/avr8-gnu-toolchain-linux_x86_64/bin/../lib/gcc/avr/4.9.2/avr5/libgcc.a(_clear_bss.o)
 *(.bss*)
 *(COMMON)
 COMMON         0x0000000000800062        0x2 /tmp/cc5dcwGL.o
                0x0000000000800062                m
                0x0000000000800064                PROVIDE (__bss_end, .)
                0x0000000000000098                __data_load_start = LOADADDR (.data)
                0x000000000000009a                __data_load_end = (__data_load_start + SIZEOF (.data))

.noinit         0x0000000000800064        0x2
                0x0000000000800064                PROVIDE (__noinit_start, .)
 *(.noinit*)
 .noinit        0x0000000000800064        0x2 /tmp/cc5dcwGL.o
                0x0000000000800064                l
                0x0000000000800066                PROVIDE (__noinit_end, .)
                0x0000000000800066                _end = .
                0x0000000000800066                PROVIDE (__heap_start, .)

So that has placed "n" into .data at 0x0060/61, "m" into .bss at 0x0062/63 and "l" into .noinit at 0x0064/65. Looking at the startup code:

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:	e8 e9       	ldi	r30, 0x98	; 152
  68:	f0 e0       	ldi	r31, 0x00	; 0
  6a:	02 c0       	rjmp	.+4      	; 0x70 <__do_copy_data+0x10>
  6c:	05 90       	lpm	r0, Z+
  6e:	0d 92       	st	X+, r0
  70:	a2 36       	cpi	r26, 0x62	; 98
  72:	b1 07       	cpc	r27, r17
  74:	d9 f7       	brne	.-10     	; 0x6c <__do_copy_data+0xc>

00000076 <__do_clear_bss>:
  76:	20 e0       	ldi	r18, 0x00	; 0
  78:	a2 e6       	ldi	r26, 0x62	; 98
  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 36       	cpi	r26, 0x64	; 100
  82:	b2 07       	cpc	r27, r18
  84:	e1 f7       	brne	.-8      	; 0x7e <.do_clear_bss_loop>
  86:	0e 94 49 00 	call	0x92	; 0x92 <main>

The do_copy_data() is copying the bytes at 0x98/99 in the flash to RAM locations 0x0060/61 (.data) and stops when it reaches 0x0062. The do_clear_bss() is writing 0x00 to locations 0x0062/63. It stops when it reaches 0x0064.

 

NOTHING in any of this code is touching the "l" variable at 0x0064/65. So it is "no init".

 

Exactly what do you think is the problem here then?

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

That's a not meaning-loaded question...

 

current code:

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

/* Check if the firmware asked to start the bootloader */
    if (bootloader_start_var == 0xBEEF)
    {
        wdt_disable();
        start_bootloader();
    }

 

will work while

 

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

    /* Check if the firmware asked to start the bootloader */
    if (bootloader_start_var == 0xBEEF)
    {
        bootloader_start_var = 0x0000;
        wdt_disable();
        start_bootloader();
    }

 

won't, hence the volatile. I'm therefore guessing that it's an unwanted optimization.

My electronic projects blog >> www.limpkin.fr

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

So this is NOTHING to do with it being .noinit?! Ho hum.

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

I'm not sure if it is sarcasm or not. 

 

This is because avr-gcc optimizes the var put in the .noinit section when it should not.

My electronic projects blog >> www.limpkin.fr

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

limpkin wrote:
This is because avr-gcc optimizes the var put in the .noinit section when it should not.

Are you sure placing something in .noinit means it should not be optimized away or are you just assuming that?

 

No, I don't positively know - it is an honest question. (Still, reading the avrlibc documentation on .noinit says nothing about preventing optimization.)

 

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Hang on.

 

You're saying that when you only read the .noinit variable it 'works', but when you both read and write to it then it 'won't work'?

 

The only meaningful question anyone can ask here is "What does 'won't work' mean?"

 

You're going to have to show a >>minimal<< but complete test program which demonstrates the problem, along with the .lss and the build commands that generated them.  Do this both for the 'works' and 'does not work' scenarios.  And explain what that means.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Are you sure placing something in .noinit means it should not be optimized away or are you just assuming that?

Well it depends on what you "assume" I guess. The fact is that a write to a global should always generate code because at the time of compiling the compilation unit in which the write occurs the compiler cannot possible know if this will later link with code that accesses it.

 

As the variable must be global to have the __attribute__ applied (you cannot assign automatics to sections - they are on the stack) then I tend to agree that the compiler has no right to optimize the write. Reads, however, might be ignored if the compiler thinks it already has the data loaded into a register and it sees no opportunity for it to have changed since the last read was made. "volatile" is the way to say "this might have changed without you realising it"

 

Like Joey I'd like to see some real code and if it's not complete and can be built then a view of the LSS where the reads/writes are expected to have been made.

Last Edited: Thu. Nov 19, 2015 - 02:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello All,

 

I'll clarify.

This code is executed at boot:

 

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

    /* Check if the firmware asked to start the bootloader */
    if (bootloader_start_var == 0xBEEF)
    {
        bootloader_start_var = ANY_VALUE_HERE;
        wdt_disable();
        start_bootloader();
    }

 

The goal of that is to know if a reboot into bootloader was requested. 

The technique: set the bootloader_start_var variable to 0xBEEF, launch the WDT and wait for the platform to reboot. This way, you're sure that all the register values are reset and that the bootloader can normally start.

 

When migrating from Atmel Studio 6 to 7 (hence migrating to avr-gcc 4.9.2) this code stopped working.

However, removing this particular line of code:

 

        bootloader_start_var = ANY_VALUE_HERE;

 

solved the problem.

 

I therefore think that the assumption when setting a variable into the .noinit section was removed during this migration.

This is therefore why adding the volatile parameter was also the solution, so it doesn't get optimized away.

My electronic projects blog >> www.limpkin.fr

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

This code is executed at boot:

But that code tels us nothing about the scope of the variable.

Last Edited: Thu. Nov 19, 2015 - 04:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry... I should have made it explicit that this is a global variable.

My electronic projects blog >> www.limpkin.fr

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
in a c file:

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

void main()
{
    /* Check if the firmware asked to start the bootloader */
    if (bootloader_start_var == 0xBEEF)
    {
        bootloader_start_var = ANY_VALUE_HERE;
        wdt_disable();
        start_bootloader();
    }
    
    // reset of the code
    
    if(something happens)
    {
        arm_wdt_here...
        bootloader_start_var = 0xBEEF;
        while(1);
    }
}

 

My electronic projects blog >> www.limpkin.fr

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

Did I pass through the scrutiny of the elders? Is my bug and solution officially recognized as they are? ;)

Just pulling your leg... I'm just glad it works now and apologize if I wasn't clear enough.

My electronic projects blog >> www.limpkin.fr

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

limpkin wrote:
Did I pass through the scrutiny of the elders? Is my bug and solution officially recognized as they are? ;)
Did you provide the requested information?

 

You're going to have to show a >>minimal<< but complete test program which demonstrates the problem, along with the .lss and the build commands that generated them.  Do this both for the 'works' and 'does not work' scenarios.  And explain what that means.

 

Your 'bug solution' may not be a solution of any kind, because a bug has not been confirmed, let alone seen.  Any bug report which does not include at least the above informal supporting evidence is likely to get ignored here, and guaranteed to be ignored by the GCC developers.  You've been around here since 2008, you know how this works.

 

I'd say it is more likely that the new behaviour you have observed is the result of a legitimate optimisation done by the new toolchain which was not done by the previous version.  If you're satisfied with a volatile kludge, so am I ;-)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Hmm, does seem to work for me

$ cat noinit.c 
#include <stdint.h>
#include <stdlib.h>

uint16_t bootloader_start_var __attribute__ ((section (".noinit")));
volatile int something_happens;

void main()
{
    /* Check if the firmware asked to start the bootloader */
    if (bootloader_start_var == 0xBEEF)
    {
        bootloader_start_var = 20;
    }
    
    // reset of the code
    
    if(something_happens)
    {
        bootloader_start_var = 0xBEEF;
        while(1);
    }
}

Building with

$ ~/3.5.0.1662/avr8-gnu-toolchain-linux_x86_64/bin/avr-gcc -mmcu=atmega1280 -Wl,--gc-sections -Os noinit.c -Wl,--Map=test.map

And then looking at the map file, I do see

<snip>
.noinit         0x0000000000800202        0x2
                0x0000000000800202                PROVIDE (__noinit_start, .)
 *(.noinit*)
 .noinit        0x0000000000800202        0x2 /tmp/ccV8I6dE.o
                0x0000000000800202                bootloader_start_var
                0x0000000000800204                PROVIDE (__noinit_end, .)
                0x0000000000800204                _end = .
                0x0000000000800204                PROVIDE (__heap_start, .)

.eeprom         0x0000000000810000        0x0
</snip>

 

Regards

Senthil

 

blog | website

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

Senthil,

 

It's not the placement of the variable in .noinit he is claiming the problem in (his thread title and OP are a bit misleading). He's saying the the later access to the variable is not working as before as some part of that access appears to be optimised by 4.9.2 when it wasn't by 4.8.1

 

What he seems incapable of is simply putting together a complete test case that demonstrates this.

 

If I use the code and command you just used (but with added -g) then the code I get for the very simple main() is:

0000010c <main>:
volatile int something_happens;

void main()
{
    /* Check if the firmware asked to start the bootloader */
    if (bootloader_start_var == 0xBEEF)
 10c:	80 91 02 02 	lds	r24, 0x0202
 110:	90 91 03 02 	lds	r25, 0x0203
 114:	8f 3e       	cpi	r24, 0xEF	; 239
 116:	9e 4b       	sbci	r25, 0xBE	; 190
 118:	31 f4       	brne	.+12     	; 0x126 <main+0x1a>
    {
        bootloader_start_var = 20;
 11a:	84 e1       	ldi	r24, 0x14	; 20
 11c:	90 e0       	ldi	r25, 0x00	; 0
 11e:	90 93 03 02 	sts	0x0203, r25
 122:	80 93 02 02 	sts	0x0202, r24
    }
    
    // reset of the code
    
    if(something_happens)
 126:	80 91 00 02 	lds	r24, 0x0200
 12a:	90 91 01 02 	lds	r25, 0x0201
 12e:	89 2b       	or	r24, r25
 130:	09 f0       	breq	.+2      	; 0x134 <main+0x28>
    {
        bootloader_start_var = 0xBEEF;
        while(1);
 132:	ff cf       	rjmp	.-2      	; 0x132 <main+0x26>
 134:	08 95       	ret

In that code the test of something_happens is there but the consequent write to the variable is not.

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

Ah ok. That makes sense.

 

FWIW, I see the same code with 4.8.1 as well - the write inside the infinite loop gets optimized away.

Regards

Senthil

 

blog | website

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

FWIW, I see the same code with 4.8.1 as well - the write inside the infinite loop gets optimized away.

Yup I tried it all the way back in 4.5.3 (the one that comes in Ubuntu 12.04LTS) and got the same. So that's not the "change" the OP here is troubled by. I guess we'll know what is troubling him when we see the test program that builds differently between 4.8.1 and 4.9.2 :-)