On an ATEMEGA1284. It will have a bootloader,, but if I wanted the application itself to also update a dedicated unused part of the application section what would be a good approach? Does it need to have a function it can call in the bootloader section? If I define the function in both applications and then manually point it to where it appears in the bootloader (which should not change), can I pass arguments to it for the page to program?
How difficult is it to flash in the applicatoin section from the application?
Does it need to have a function it can call in the bootloader section?
Yes.
If I define the function in both applications and then manually point it to where it appears in the bootloader (which should not change), can I pass arguments to it for the page to program?
Better to have some sort of vector in the bootloader, so that it doesn't change even if you need to change the bootloader.
Relatively recent Optiboot does this (I'm not entirely sure it works with >64k flash devices...) In order to save size, it does little more than the actual SPM instruction in the bootloader, which is enough (slightly different for Mega0/etc.)
https://github.com/Optiboot/opti...
Thanks Bill - that is a good idea. I see the do_spm_t typedef and the do_spm const entry which points it to the end of flash. How do you get the SPM instruction to be at that location?
How do you get the SPM instruction to be at that location?
const do_spm_t do_spm = (do_spm_t)((FLASHEND - 511 + 2) >> 1);
You have to know where the bootloader starts, and there's a vector at the beginning of the bootloader:
startOfBootloaderSection: rjmp bootloader rjmp doSPMfunction
The "const" definition above works out to "(endOfFlash - BootloaderSize) + doSPMvectorLocation"
To absolutely position something in avr-gcc it's a two step process. You assign what must be a fixed address to a section and then later you tell the linker where you want that thing located. So if you want a "fixed entry point" you might do something (in the bootloader) like:
void the_real_SPM_code() {
// stuff
}
__attribute__((section(".spm_entry"))) void do_spm(parms...) {
// stuff
the_real_SPM_code();
}
then later on you do something like:
-Wl,-section-start=.spm_entry=12345
However that "12345" is something you will need to determine. When absolutely placing code it's easiest to do it "beyond" the existing code in the area where you are adding it. So if you have a 32K micro with a 512 word (1K) boot section the bootloader starts at 31K. Now let's assume it doesn't use the entire 1K but only 900 bytes then you would probably want your 12345 beyond this. One "easy" option is to count back form the end of flash. If do_spm() itself is just 59 bytes you could maybe put it at flash end minus 64 say.
Of course a further step beyond this is an actual "dispatch table" which is a table made up of JMPs with fixed sized entries (like an interrupt vector table) to the various sharable functions in the bootloader (you might want to share UART_init(), UART_sendchar(), UART_receivechar() as well as do_spm() for example so the app doesn't need to duplicate those things). To produce such a vector table it's actually easier to do it in Asm than battle with the C compiler. With such a table, if it is five RJMPs (say) then you could locate the table at 10 bytes below FLASHEND
Thanks guys; will give it a try!
gcc is optimizing my function out because the bootloader does not call it. Is there a way to prevent this?
__attribute__((section(".blpfw"))) void bootloader_page_flash_write(uint16_t APage, uint8_t *ABuffer)
--unused (see another thread I posted earlier today)
I've searched through your threads, but couldn't find it.
I tried:
__attribute__((used)) __attribute__((section(".blpfw"))) void bootloader_page_flash_write(uint16_t APage, uint8_t *ABuffer)
and:
__attribute__((unused)) __attribute__((section(".blpfw"))) void bootloader_page_flash_write(uint16_t APage, uint8_t *ABuffer)
... but if I remove my dummy call to the function in main, it disappears from the hex output with either of the above.
Found it in a thread - added -Wl,--undefined=bootloader_page_flash_write
Thanks for the help everyone - I've got it working just as a single function located 256 bytes before the end of flash
I had to use the word address in the application when referencing it.
Bootloader:
__attribute__((section(".blpfw"))) void bootloader_page_flash_write(uint16_t APage, uint8_t *ABuffer) { uint32_t address; uint16_t ui1; address=(uint32_t)APage*256; //erase page boot_page_erase(address); boot_spm_busy_wait(); //enable rww boot_rww_enable(); boot_spm_busy_wait(); //load buffer for (ui1=0;ui1<256;ui1+=2) { boot_page_fill(address+ui1,ABuffer[ui1] | (ABuffer[ui1+1]<<8)); boot_spm_busy_wait(); } //program page boot_page_write(address); boot_spm_busy_wait(); //enable rww boot_rww_enable(); boot_spm_busy_wait(); }
Bootloader linker options:
-Wl,--section-start=.text=0x1F800 -Wl,-section-start=.blpfw=0x1FF00 -Wl,--undefined=bootloader_page_flash_write
Application:
typedef void (*bootloader_page_flash_write)(uint16_t APage, uint8_t *ABuffer); void page_flash_write(uint16_t APage) { uint8_t c1; //wait for blinktimer to increment by one to show that the ISR just executed c1=blinktimer; while (c1==blinktimer) ; //clear display Delay_us(1650); DISP_ROW_PORT=0; //clear interrupts cli(); //call bootloader page flash write ((bootloader_page_flash_write)0xff80)(APage-0x8, buffer); //it should now be 8.67ms later, we should bump time time++; //reenable interrupts sei(); }
Now I'm trying the vector approach, but not getting anywhere.
If I remove the attribute from the main function, it can then be compiled into the regular bootloader area.
Then I tried this:
typedef void (*bootloader_page_flash_write_t)(uint16_t APage, uint8_t *ABuffer); const __flash __attribute__((section(".blpfw"))) bootloader_page_flash_write_t z = bootloader_page_flash_write;
It does not seem to write this z variable to the correct place in flash however.
I've tried adding used/unused to it, I've added z to the linker undefined with no success.
Another approach-
Put a naked function in .init1 that skips the rjmp to the app accessible bootloader function. Your bootloader function can now be accessed from a known location, in this case right after the vector table (0x1F800 + _VECTORS_SIZE + 2). Its not any better or worse than some other known location, as long as it stays put.
//bootloader
#include <avr/io.h>
void bootloader_page_flash_write(uint16_t APage, uint8_t *ABuffer){
//...
}
__attribute((section(".init1"), naked))
void init1(){
asm("rjmp .+2"); //skip to .init2 (startup code)
asm("rjmp bootloader_page_flash_write"); //jump to function
}
int main(){
//...
}
Now in your app you need a function pointer to the known location-
//app
#define BL_START 0x1F800
typedef void (*blpgwr_t)(uint16_t APage, uint8_t *ABuffer);
static const blpgwr_t blpgwr = (blpgwr_t)((BL_START + _VECTORS_SIZE + 2)/2); //byte->word
int main(){
uint8_t buf[SPM_PAGESIZE]; //random data
blpgwr( BL_START/SPM_PAGESIZE-1, buf ); //last page in app (I think)
while(1){}
}
And if you want to have more fun, copy the linker script to the local project folder so you can have easier control-
change the text region to match the bootloader section, eliminating the need to specify the .text section address-
text (rx) : ORIGIN = 0x1F800, LENGTH = 0x800
eliminate the vectors if not needed by commenting out the KEEP vectors line (you still get all the normal c crt*.o things linked in, just not the vectors)-
/* KEEP(*(.vectors)) */
Tell the linker you want to use a script- -Xlinker -script=avr51.xn
Now your init1 function is sitting at the bootloader reset vector, and your app now just eliminates using the _VECTORS_SIZE in the function address calculation.
Sorry, losing my marbles, meant undefined not unused. Doh!
That makes me feel a bit better clawson as I was searching your posts for the day and couldn't find unused! If you are losing your marbles clawson, what programming hope do any of the rest of us have!
curtvm that looks like a good approach to try as well!
I ended up coming up with this last night which works so long as the function you call is large enough not to be integrated into the stub function. I only need one so I named it endvector0, but I tried an endvector1 to see if it could stack and that also worked.
My bootloader function just became a normal function placed in .text wherever gcc likes. Then I added this:
__attribute__((section(".endvector0"))) void endvector0(uint16_t APage, uint8_t *ABuffer) { bootloader_page_flash_write(APage, ABuffer); }
GCC optimizes this into a 2 byte RJMP, so I used these linker options to specifically place it at the end of flash:
-Wl,-section-start=.endvector0=0x1FFFE -Wl,--undefined=endvector0
Then I am calling it from the application with:
typedef void (*bootloader_page_flash_write)(uint16_t APage, uint8_t *ABuffer); #define ENDVECTOR0 0xffff void page_flash_write(uint16_t APage) { uint8_t c1; //wait for blinktimer to increment by one to show that the ISR just executed c1=blinktimer; while (c1==blinktimer) ; //clear display Delay_us(1650); DISP_ROW_PORT=0; //clear interrupts cli(); //call bootloader page flash write ((bootloader_page_flash_write)ENDVECTOR0)(APage-0x8, buffer); //it should now be 8.67ms later, we should bump time time++; //reenable interrupts sei(); }
I'll probably change the #define ENDVECTOR0 to use (0x1FFFE/2) just for consistency.
bootloader lss:
__attribute__((section(".endvector0"))) void endvector0(uint16_t APage, uint8_t *ABuffer) { bootloader_page_flash_write(APage, ABuffer); 1fffe: 4f cc rjmp .-1890 ; 0x1f89e <bootloader_page_flash_write>