How difficult is it to flash in the applicatoin section from the application?

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

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?

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

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...

 

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

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?

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

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"

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

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

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

Thanks guys; will give it a try!

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

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)

 

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

--unused (see another thread I posted earlier today)

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

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.

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

Found it in a thread - added  -Wl,--undefined=bootloader_page_flash_write

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

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();
}

 

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

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.

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

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.

 

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

Sorry, losing my marbles, meant undefined not unused. Doh!

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

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>

 

Last Edited: Sat. Feb 20, 2021 - 03:24 PM