Bootloader as API?

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

Hi,

I'm working on a new project that will have a simple bootloader that will look for a connected SPI flash chip and load the program stored into the internal flash.

The device also has a display that needs to be initialised, so I thought the bootloader could handle this task as well at power-on. It then occurred to me that this would mean the bootloader would have code to deal with writing to the display.

Is it possible for the 'main' program to jump into subroutines located in the bootloader section and return back to the main program again?

Thanks
-Mike

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

Is it possible for the 'main' program to jump into subroutines located in the bootloader section and return back to the main program again?

Sure, it's possible.  Late versions of optiboot have an API to do SPM instructions on behalf of the application (since they aren't permitted from the Application Section.)

Some TI microcontrollers have a substantial "ROM" section that the compiler knows how to call.

 

It's usually done by putting a branch instruction at a known location (end of memory, beginning of bootloader section, wherever) and then arranging for the application to "call" that address.

 

I generally prefer the application to have as little dependence on the presence of a bootloader as possible, though.

 

 

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

Thanks westfw,

 

  I've been having a play and I've written a 'bootloader' which is just a program that flashes an LED slowly 10 times then jumps to address 0.

 

int main (void) {

    DDRC = 1<<PC5;

    for(uint8_t j=0 ; j<20 ; j++) {
        PORTC ^= 1<<PC5;

        for(uint32_t i=0 ; i<600000 ; i++) {
            asm("NOP");
        }
    }

    PORTC = 0;

    for(uint32_t i=0 ; i<6000000 ; i++) {
        asm("NOP");
    }

    asm("jmp 0");
}

 

I then have another program that flashes an LED fast, which should live at address 0.

 

I've set the fuses on my ATMega328 (which is on a breadboard) with D8 in the hfuse, which should enable the bootloader section?

 

I compiled the bootloader with -Ttext=0x7000 which should put it in the bootloader section?

 

When I flash it, the slow flashing program runs, but appears to jump back and run again at the end.

 

If I flash the fast flashing program, the LED flashes fast, but the bootloader never appears to run.

 

I've done something stupid, but I don't know what it is?

Last Edited: Sun. Dec 8, 2019 - 11:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MalphasWats wrote:

I've done something stupid, but I don't know what it is?

 

I think I know what the stupid thing was now. I was programming the 2 programs in 2 separate steps, instead of combining to a single .hex (because obviously my 'bootloader' doesn't actually program anything yet).

 

The Bootloader FAQ had me covered:

 

srec_cat app.hex -I boot.hex -I -o combined.hex -I

and then AVRDUDEed the combined.hex and I get the 2 flashy programs working as expected.

 

I feel a bit stupid for thinking that AVRDUDE only overwrote the sections of flash it needed for the given program blush

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

So I guess my next question is - if I have a function as part of my bootloader, how do I find the address of it so I can jump to it from my main program?

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

Define a memory segment for the linker and give it a fixed start address. In the bootloader code, define a function pointer that points to your function and add the segment name to it. The linker will put the function pointer where you told it to.

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

Kartman wrote:
Define a memory segment for the linker and give it a fixed start address. In the bootloader code, define a function pointer that points to your function and add the segment name to it. The linker will put the function pointer where you told it to.

 

Thank you Kartman. I shall add memory segments and linkers to the list of things I need to learn about for this project ;)

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

A very simple example:

uint16_ common_increment(uint16_t n) {
    return n + 1;
}

int32_t common_decrement(int32_ n) {
    return n - 1;
}

int common_add(int n, int p) {
    return n + p;
}

typedef void (*fptr_t) (void); // simple interface - forget parameters

__attribute__((section(".fntable"))) fptr_t func_table[] = {
    (fptr_t)common_increment,
    (fptr_t)common_decrement,
    (fptr_t)common_add
};

as well as the usual:

-Wl,-Ttext=0x3800

which moves this whole bootloader program to the bootloader boundary address (I'm assuming 16K micro with bootloader at 14K) you would actually use:

-Wl,-Ttext=0x3800 -Wl,-section-start=.fntable=0x3FC0

Now the function table is placed 64 bytes from the end of flash. Move it up if it's only going to be small (at present it's only really 6 bytes for three 16bit pointers), move it down if there are loads of shared functions.

 

I'm assuming that a shared header elsewhere has: 

uint16_t common_increment(uint16_t n);
int32_t common_decrement(int32_t n);
int common_add(int n, int p);

so all users know the real input/output formats which is why the table can just be made up of "void(*)(void)" entries as all that is important to get them from one place to the other is to know their address, not their actual interface.

 

In fact to call them you could later use something like:

uint16_t (*common_increment)(uint16_t n) = (uint16_t(*)(uint16_t)) 0x3FC0;
int32_t (*common_decrement_(int32_t n) = (int32_t(*)(int32_t)) 0x3FC2;;
int (*common_add)(int n, int p) = (int(*)(int, int)) 0x3FC4;

(in which case you may not even need the .h)

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

clawson wrote:

A very simple example:

 

 

That is really great! Thank you.