Calling a function by address

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

Hi all,

I would like to know if it is possible to call a function by address as well as pass data to it and return data from it?

For example, if I write a function that takes two float values, adds them and returns the result as one float and I explicitly place the binary at 0x7F00 (and I don't use "extern", but ONLY have the address), can I do something like this:

float result = 0x7F00 (123.4, 567.8);

If so, how to do it?

Help will be appreciated!

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

You can use pointers to access functions. Any C reference should be able to tell you how. Why  is  it  necessary for the function to be at a location you choose?  That can be very inefficient.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

https://godbolt.org/z/reKEez

 

I should add that addresses for an avr need to be word addresses as the resulting icall uses a word address. So byte address 0x7F00 needs to be 0x7F00>>1. Wrap it all up in a macro or something to make usage easier.

Last Edited: Sat. Mar 7, 2020 - 08:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Extending curtvm's solution for repeated use and also confining the awfulness to just one line.

 

    float(*fixedfunc)(floatfloat) = (float(*)(floatfloat))0x7f00;

 

    float result1 = fixedfunc(123.4567.8);

    float result2 = fixedfunc(1.2345.678);

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

ka7ehk wrote:

You can use pointers to access functions. Any C reference should be able to tell you how. Why  is  it  necessary for the function to be at a location you choose?  That can be very inefficient.

 

Jim

 

What I want to do is setup an AVR (a 328p) without a bootloader, but with a small piece of code in the bootloader section to write data to flash during program execution. The intent is to record data over a period of time, and larger than EEPROM.

 

Speed is no worry. I know I'll need to read a flash page into ram, modify the one current byte (not word) and re-flash the entire page to record my 1 byte of data, but I'm doing so every 10 minutes, so exec time is irrelevant to me.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Why not start with Optiboot and remove as much code as possible, leaving just the write-to-flash functionality. Are you concerned about the size of the 'bootloader' ? i.e. is your application code + data storage very large ?

 

In fact, why not just use Optiboot as-is, and ignore its bootloading capability ?

 

External serial EEPROM is very cheap, easy to use and available in a range of physical packages. External Flash is less so, but denser.

Last Edited: Sat. Mar 7, 2020 - 02:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I showed an example of this very thing using a function in a bootloader in this post/thread just the other day..

 

https://www.avrfreaks.net/commen...

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

>I know I'll need to read a flash page into ram, modify the one current byte (not word) and re-flash the entire page to record my 1 byte of data

 

You do not need to save the flash page unless you are overwriting something that is not already erased-

 

some-page = 0x01,0x02,0xff,0xff,0xff,..........,0xff
                        |-want to write to this byte

 

Load the temp page buffer with a byte of your choice at the page offset you want, all the other untouched bytes(words) in the temp page buffer will remain in erased state (0xff). A page write can only clear bits, so all those 0xff will do nothing to existing bytes-

current  desired  tbuffer  result     current       tbuffer  result 

 0xff     0x01     0x01     0x01       0xff  bitand  0x01  =  0x01

 0x01     nc       0xff     0x01       0x01  bitand  0xff  =  0x01

 0x01     0x11     0x11     0x01       0x01  bitand  0x11  =  0x01 <- cannot set bits 

 

(You can program a bit at a time if you want to. I wrote an avr bootloader which had an 'encryption' scheme that used this fact as one more layer in the scheme. The hex records were encrypted- address and data only, in addition the hex generator would write records knowing which bits were already programmed, and would created records in some random order that overlapped in some random fashion so no information was given away about what a record may be. The first record may or may not start at the vector table, so no info is leaked about what would typically sit in the vectors, and so on. Too bad I never had a real need to use it)

 

 

 

Its been a long time since I used an 'old' avr (anything other than avr0/1), but somewhere along the line the spm instruction seems to have been changed to allow spm from anywhere in flash and there appears to be no RWW so I assume you just write and hope you wake up from your temporary  4ms coma.

 

This is from the 328p datasheet-

 

25.1
In Atmel® ATmega328P, there is no read-while-write support, and no separate boot loader section. The SPM instruction can
be executed from the entire flash.

25.2.5
Note that the RWWSB bit will always be read as zero in Atmel® ATmega328P. Nevertheless, it is recommended to check
this bit as shown in the code example, to ensure compatibility with devices supporting read-while-write.

 

25.3.1
• Bit 6 – RWWSB: Read-While-Write Section Busy
This bit is for compatibility with devices supporting read-while-write. It will always read as zero in ATmega328P.

 

BUT, they also have this later, which I assume is a copy/paste error as it seems unlikely they went through all the above documentation and then suddenly decided they didn't really mean it-

 

26.3.1

The application section can never store any boot loader code since the SPM instruction is disabled when executed from the
application section.

 

So, it may be you have no need to to do your blind function call's at all.

 

 

You may also be able to find the first available unused flash page with something like-

 

    #define SPM_MASK (0xFFFF^(SPM_PAGESIZE-1))
    extern void* __data_load_end;
    const uint16_t flash_data_start = ((uint16_t)&__data_load_end+1+SPM_PAGESIZE) & SPM_MASK;

 

Now you have a start page where no code is located, and can erase pages if needed without having to touch any code already in flash.

Last Edited: Sat. Mar 7, 2020 - 05:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

I showed an example of this very thing using a function in a bootloader in this post/thread just the other day..

And if that thread had a more descriptive title than "bootloader"; this thread, as a very close, or even exact duplicate, may not have been necessary.

 

I wonder if when answering a question we freaks should be encouraging the OP to edit the Thread Title to better summarise the question.

 

The StackOverflow guys get pretty anal about this sort of thing, but there of course one can edit the original question & title.

 

So then for this thread (and the one referenced in #9) a better title may be: "How to call Function in Bootloader from Application",  or "How to call Function in Bootloader code from Application code".

 

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

>So then for this thread (and the one referenced in #9) a better title may be: "How to call Function in Bootloader from Application",  or "How to call Function in Bootloader code from Application code".

 

Except now it may have nothing at all to do with a bootloader and the original title is just as valid as before. He wanted to know how to call a function when only its address is known. That is not a bootloader question, and the title is focused exactly on what was wanted whatever the need for it may have been. Title is good.

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

curtvm wrote:
Except now it may have nothing at all to do with a bootloader

Perhaps ... Your #10 confused me somewhat because I don't remember reading that. Which revision of the datasheet did you copy that from ?

 

I'm looking at DS40002061A 2018 "ATmega48A/PA/88A/PA/168A/PA/328/P"

 

27.2 Overview

In ATmega88A/88PA/168A/168PA/328/328P the Boot Loader Support provides a real Read-While-Write Self-
Programming mechanism for downloading and uploading program code by the MCU itself.

 

I wonder if they've done a silicon change but then that's one hell of a silicon change.

 

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

I can't find it, but I may have clicked on the automotive one, as this looks like what I was seeing-

http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

 

Now, I don't know if automotive 328P is a different 328P other than temperature/voltage things. Wouldn't an A be in the name? The datasheet shows no DIP parts, so I guess its the same, but different. I don't know. Its a little confusing.

 

I guess one would simply have to test if spm works from app section.

 

 

 

 

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

I think the automotive data sheet has a copy/paste error from the 48/88/168/328 datasheet.  The latter is specific that it is only the ATmega48x that can use SPM anywhere.  (ATmega48 has "Self Programming Flash", the other chips have "Boot loader Support."

 

OTOH, the automotive data sheet goes on to describe the "self program enable" fuse, rather than the bootloader fuses, so...

 

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

As noted, C syntax allows OP to do what he asks,

but what he asks does not seem all that useful.

In the GNU toolchain, the mechanism for putting a

function in a particular place do not remove its name.

Why not just use its name?

Do other toolchains remove the name?

Iluvatar is the better part of Valar.

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

I think he was talking about calling across the divide between an application and a bootloader where all the app knows is that do_spm() or whatever is located at 0x7C80 or whatever.

 

The very fact that #1 included 0x7F00 could be a strong clue ;-)

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

clawson wrote:
I think he was talking about calling across the divide between an application and a bootloader where all the app knows is that do_spm() or whatever is located at 0x7C80 or whatever.
In #7, Krupski wrote "without a bootloader."
clawson wrote:
The very fact that #1 included 0x7F00 could be a strong clue ;-)

Iluvatar is the better part of Valar.

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

obdevel wrote:

Why not start with Optiboot and remove as much code as possible, leaving just the write-to-flash functionality. Are you concerned about the size of the 'bootloader' ? i.e. is your application code + data storage very large ?

 

In fact, why not just use Optiboot as-is, and ignore its bootloading capability ?

 

External serial EEPROM is very cheap, easy to use and available in a range of physical packages. External Flash is less so, but denser.

 

Actually, the serial EEPROM is a good idea and I may do it that way instead. I found a 4 megabit (512 megabyte) part by ST Micro... a M95M04-DR in a tiny 8 pin package: LINK

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

clawson wrote:

I showed an example of this very thing using a function in a bootloader in this post/thread just the other day..

 

https://www.avrfreaks.net/commen...

 

Although I intend(ed) to place my code in a 328p bootloader section, my primary question was how to call a function by address (since I plan to use this for other things than just this project).

 

"Bootloader" initially wasn't even on my mind.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

N.Winterbottom wrote:
And if that thread had a more descriptive title than "bootloader"; this thread, as a very close, or even exact duplicate, may not have been necessary.

 

I wonder if when answering a question we freaks should be encouraging the OP to edit the Thread Title to better summarise the question.

 

The StackOverflow guys get pretty anal about this sort of thing, but there of course one can edit the original question & title.

 

So then for this thread (and the one referenced in #9) a better title may be: "How to call Function in Bootloader from Application",  or "How to call Function in Bootloader code from Application code".

 

 

Please see my post #20

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

curtvm wrote:

This is from the 328p datasheet-

 

25.1
In Atmel® ATmega328P, there is no read-while-write support, and no separate boot loader section. The SPM instruction can
be executed from the entire flash.

25.2.5
Note that the RWWSB bit will always be read as zero in Atmel® ATmega328P. Nevertheless, it is recommended to check
this bit as shown in the code example, to ensure compatibility with devices supporting read-while-write.

 

 

If this were true, then a lot of Arduino UNO owners would be in trouble......  :)

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

clawson wrote:

I think he was talking about calling across the divide between an application and a bootloader where all the app knows is that do_spm() or whatever is located at 0x7C80 or whatever.

 

The very fact that #1 included 0x7F00 could be a strong clue ;-)

 

Exactly correct.

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

>In the GNU toolchain, the mechanism for putting a function in a particular place do not remove its name

 

I think we found the original use for 'BOOTLOADER_SECTION' created in boot.h, which of course really has nothing to do with a bootloader, but rather locating code in the 'bootloader' section of flash so the spm instruction can be used. In this scenario, as pointed out you will still have the function name so no need to use function addresses as you would do calling a function located in a 'true' bootloader. You would just need to tag your spm function with a section attribute to locate it in the 'spm' section (last page(s) would be ideal, so all free flash is continuous up to the page with spm code), and instruct the linker where that section is located.

 

 

>Actually, the serial EEPROM is a good idea and I may do it that way instead

 

It just depends on how much data you need to store. If it easily fits in flash, and you know that will not change later, its nice to stay inside one device. If the size can exceed available free flash, or you could see the need later (more frequent, more data per save, etc) then you would just be delaying what you will have to do later. If you are using a 32k avr, with half of flash in use, that still leaves 16k free which is still quite a bit of storage (relatively speaking). Of course, if you use floats everywhere, then it may start to look a little lacking quite a bit sooner.

 

>but I'm doing so every 10 minutes

 

1 float every 10 minutes =576bytes/day, if 16KB free flash, that is 28 days of storage. Calculate out what you need/want/may want, which will tell you which way to go.

 

 

>If this were true, then a lot of Arduino UNO owners would be in trouble......  :)

 

The only trouble they would be in, is if they realized what kind of power they had. I think the 328P datasheet I was looking at just has major copy/paste errors, and I guess it would be kind of odd that no one in arduino world discovered the available spm so its most likely not available in the app section. I should have quit posting after post #3.

 

I also see how I ended up with the 'automotive' datasheet- I saw 'Complete Datasheet', which I typically click instead of the usual 'Summary', and nothing in the datasheet led me to believe I was not looking at a 328P datasheet, so I plead innocent. Mostly.

Last Edited: Sun. Mar 8, 2020 - 06:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you simply want to put a function in the BLS but it's still part of the single application then, just like Skeeve, I have no idea why you need to call it by address rather than just call it by name?

 

You don't do it with variables. You don't absolutely place uint16_t foo at 0x124 then use *(uint16_t *)0x124= or whatever so why would you do the same kind of thing for calling functions? 

 

I do see the point when there are TWO programs and one doesn't know the symbolic names of the other but otherwise it seems pointless? 

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

clawson wrote:

If you simply want to put a function in the BLS but it's still part of the single application then, just like Skeeve, I have no idea why you need to call it by address rather than just call it by name?

 

You don't do it with variables. You don't absolutely place uint16_t foo at 0x124 then use *(uint16_t *)0x124= or whatever so why would you do the same kind of thing for calling functions? 

 

I do see the point when there are TWO programs and one doesn't know the symbolic names of the other but otherwise it seems pointless? 

 

Yes, I intended to place the flash writer code in the BLS separately and place the data logger code in the application section separately.

 

Reason being, I don't know how to write it all in one C source file and explicitly locate part of the code up in the BLS.

 

In assembler, a simple "ORG" statement would allow me to locate what I want anywhere I want it, but I don't know how to do it in C.

 

I had thought about writing it all at once then editing the HEX file to set the load address of the flash write code to the BLS, but the binary is probably not PIC and it wouldn't work (nor would calling it by name as the main binary would expect it to be where it originally was).

 

If there's a simple way to locate code in the BLS _AND_ have it's name so I can call it as a function, that's awesome. Else, I'll probably just wimp out and get some SPI EEPROM.

 

BTW, based on the rate I'm acquiring the data and for how long, I need about 28K of space (which is why I wanted to use the flash for it), but a 32K or even larger serial EEPROM would probably be easier.

 

I guess at this point suggestions on WHICH way to do it would be helpful....

 

 

Gentlemen may prefer Blondes, but Real Men prefer Redheads!

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

Krupski wrote:
I don't know how to write it all in one C source file and explicitly locate part of the code up in the BLS

In that case surely that should have been your question.

 

Answer found with a 5s search using your favourite search engine: "avr locate part of code in Bootloader section" leading to:

 

https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_reloc_code.html

 

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

Krupski wrote:
Reason being, I don't know how to write it all in one C source file and explicitly locate part of the code up in the BLS.
See the link I gave. But basically:

__attribute__((section(".high"))) int add(int a, int b) {
    return a + b;
}

int main(void) {
    PORTB = add(17, 23);
}

then build with:

-Wl,-section-start=.high=0x7F00

Now the add() function is located at 0x7F00. In fact in GCC this is such a common requirement that https://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html has BOOTLOADER_SECTION:

 

#define  BOOTLOADER_SECTION   __attribute__ ((section (".bootloader")))

 

So my code example could more easily be:

BOOTLOADER_SECTION int add(int a, int b) {
    return a + b;
}

int main(void) {
    PORTB = add(17, 23);
}

then, because it always uses ".bootloader" this would then be built with:

-Wl,-section-start=.bootloader=0x7F00

If you have:

BOOTLOADER_SECTION int add(int a, int b) {
    return a + b;
}

BOOTLOADER_SECTION int sub(int a, int b) {
    return a - b;
}

then they'll be placed from 7F00 on wards. Of course you don't actually need to know their absolute addresses as you can just use:

int main(void) {
    PORTB = add(17, 23);
    PORTB = sub(137, 59);
}

Now it could be that add() is at 7F00 and sub() is at 7F1C  but you aren't aware of this because you just call them by name add() and sub() like any other function even though they are actually CALL 7F00 and CALL 7F1C