Bootloader Function Optimized Out

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

I want to be able to call a function that is located in the bootloader section of my ATmega328p from inside of my main application.

The two projects (bootloader and application) have been created as two separate projects, and thus the application does not know the bootloader, or the bootloader's functions exist.

It is pretty straight forward process to call a function using a pointer to the location of the function in the bootloader.

My issue is, I do not just want to place the function in the bootloader in a specific location, in-case the function changes in the future.

So I have created a "pass-along" function in my bootloader.

For Example:

/*- Prototypes ----------*/

__attribute__((section(".writer"))) void call_foo(void);
void foo(void);


/*- Implementations ----------*/

void call_foo(void)
{
   foo();
}

void foo(void)
{
  //do something amazing
}

My problem with the above example is that when I build the boot-loader "call_foo" gets optimized out. Is there a way (without creating a linker script) to still optimize for size, but not lose the function "call_foo" function?

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

check the 'used' attribute: https://gcc.gnu.org/onlinedocs/g...

:: Morten

 

(yes, I work for Atmel, yes, I do this in my spare time, now stop sending PMs)

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

Morten, Thank you for your input. So I tried this...

/*- Prototypes ----------*/ 

__attribute__((section(".writer"),used)) void call_foo(void); 
void foo(void); 


/*- Implementations ----------*/ 

void call_foo(void) 
{ 
   foo(); 
} 

void foo(void) 
{ 
  //do something amazing 
} 

But I still get the same thing. When I set Optimize (-01) I no longer see the function when I read from my flash. If I turn off optimization I do see it in my flash.

Last Edited: Tue. Sep 2, 2014 - 05:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

If I turn on optimization I do see it in my flash.

Did you mean "off"?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Yes, You are correct... I meant "Off".

When the optimization is ON I do NOT see the function in my flash, when optimization is OFF I DO see the function in flash.

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

I plugged your code into a "vanilla" Studio 6.1 test app, and have apparently reproduced your results.

Discarded input sections

 .bss           0x00000000        0x0 c:/program files (x86)/atmel/atmel toolchain/avr8 gcc/native/3.4.2.1002/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.7.2/../../../../avr/lib/avr4/crtm88pa.o
 .text          0x00000000        0x0 GccApplication1.o
 .bss           0x00000000        0x0 GccApplication1.o
 .writer        0x00000000        0xa GccApplication1.o
 .text          0x00000000        0x0 c:/program files (x86)/atmel/atmel toolchain/avr8 gcc/native/3.4.2.1002/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.7.2/avr4\libgcc.a(_exit.o)
 .bss           0x00000000        0x0 c:/program files (x86)/atmel/atmel toolchain/avr8 gcc/native/3.4.2.1002/avr8-gnu-toolchain/bin/../lib/gcc/avr/4.7.2/avr4\libgcc.a(_exit.o)
 .text.libgcc.mul
...

I tried a couple different things, like doing a volatile operation in "amazing", with the same results.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Ok, thank you for your help. Do you have any suggestions on a better way of doing this. Calling a function that is in the boot-loader section from the main application?

Thank you,

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

Quote:

My issue is, I do not just want to place the function in the bootloader in a specific location, in-case the function changes in the future.

Why not just use a dispatch table - obviously it means a .S file but it's just a sequence of JMPs and you can locate that at a fixed address that the app knows. The bootloader linker will fill in the destination addresses for you.

Alternatively I don't believe your function is "optimised away" anyway. It may be garbage collected if you are using -ffunction-sections and -gc-sections. If so then turn off one or both.

As an alternative what do you get if call_foo() and foo() are in different compilation units?

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It may be garbage collected if you are using -ffunction-sections and -gc-sections. If so then turn off one or both.

Interestingly, there is no mention of such dependencies in the document that OP was directed to.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Clawson,

Thank you. All great options. Since I only need to expose one function in the boot-loader I went ahead and applied the second option you suggested.

Quote:
Alternatively I don't believe your function is "optimised away" anyway. It may be garbage collected if you are using -ffunction-sections and -gc-sections. If so then turn off one or both.

You are correct I was able to turn off the garbage collection using '-gc-sections' and now the function still resides in the address I specify in any optimization mode.

Thank you,

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

I thought "used" was supposed to defeat garbage collection?

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

westfw wrote:
I thought "used" was supposed to defeat garbage collection?
Apparently it only defeats garbage collection by the compiler, not by the linker.
I think that passing --undefined=foo to the linker will defeat garbage collection by the linker.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Yep - Skeeve's --undefined flag is a magic sauce that retains symbols even through a linker garbage collection. I use this in my LUFA bootloaders for the user application API functions:

https://github.com/abcminiuser/l...

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Ok, I can create the 'pass-along' function in my bootloader and make sure that it is not picked up by the garbage collector.

I thought I would be able to easily create a function pointer in my application to the function in the bootloader, but alas... it is not working.

Does anyone have any examples of doing this?

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

The simplest is probably to skip the pointer.
Use call_foo in your application code.
Use -Xlinker -defsym=call_foo=... on its link line.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Quote:

I thought I would be able to easily create a function pointer in my application to the function in the bootloader, but alas... it is not working.

Does anyone have any examples of doing this?


I'd probably follow Michael's advice but if you want to use a function pointer then something like:

typedef int (*fp_t)(int, char, long);

int main(void) {
  fp_t foo = (fp_t) 0x1234;
  TCNT = foo(12345, 'A', 0xBABEFACE);
}

Obviously my example defines a function that returns an int and takes, int, char and long parameters but the simple case is just:

typedef void (*fp_t)(void);

int main(void) {
  fp_t foo = (fp_t) 0x1234;
  foo();
}

I hope it's obvious but 0x1234 is the fixed address of the foo() function, adjust for wherever it really happens to be.

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

If you combine Clawson's advice with mine,
0x1234 becomes call_foo , which needs to be declared.
The combination obviates the need to decide whether 0x1234 should be a byte address or a word address.
Addresses to and from the gnu linker are byte addresses.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Thank you Clawson and Michael. Both of you gave great advice. I think I am going to play around with both of them. I like the idea of just placing it in the linker file. Makes sense.

Clawson, for your example with the function pointer, would the address "0x1234" be the byte or the word location?

Thank you,

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

Quote:

Clawson, for your example with the function pointer, would the address "0x1234" be the byte or the word location?

This kind of thing is very easy to check:

//==>   TCNT1 = foo(12345, 'A', 0xBABEFACE);
	ldi r18,lo8(-50)
	ldi r19,lo8(-6)
	ldi r20,lo8(-66)
	ldi r21,lo8(-70)
	ldi r22,lo8(65)
	ldi r24,lo8(57)
	ldi r25,lo8(48)
	ldi r30,lo8(52)
	ldi r31,lo8(18)
	icall

That's putting 0x12 in R31 and 0x34 into R30. As it's an ICALL that's a word address. This is why Michael suggested defining the target address externally to GCC tools as everything specified there is a byte address. OTOH if you wanted to land at byte address 0x1234 I guess you'd just:

  fp_t foo = (fp_t) (0x1234 >> 1); // >>1 => word to byte

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

I think I see a missed optimization.
ICALL was not really necessary.
Defining foo with the linker would have eliminated the ICALL.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Ok, hopefully my last question on this and then I will put this topic to bed smiley.

 

So testing this I can do the following

typedef void (*fp_t)(void);

int main(void) {
  fp_t foo = (fp_t) 0x7FFC;
  foo();
}

Or

typedef void (*fp_t)(void);

int main(void) {
  fp_t foo = (fp_t) 0x3FFE;
  foo();
}

 

With the exact same results? 

 

In my bootloader I have my functions address specified at 0x3FFE (word address) in the linkers "Memory Settings"

 

So why can I pass the function the word or byte address with no change?

 

Thank you,

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

So why can I pass the function the word or byte address with no change?

because the  space from 0x3FFE to 0x7FFF is full of 0xFF's which act (kind of) as NOPs.