Proper Way to Start User Application From Bootloader

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

I'm working on a custom bootloader, to sit ontop of the DFU bootloader, and I'm trying to figure out the "proper" way to start my program in two scenarios.

1) My bootloader is triggered by the presence of a specific file. If the file exists, the bootloader will run, otherwise it should jump to the user application at some higher point in flash.
2) If the file exists, after programming and verifying the flash, I'd like my bootloader to jump immediately to the user application.

I know how to reset to the DFU bootloader, that's easy to use the WDT to just reset to the beginning of flash. What I'm struggling with is jumping to an address other than the beginning of the flash. Looking at the DFU bootloader (1.0.3) it looks like there's a boot.S assembly file that handles this, I was hoping to stick to C (easier for me to understand what's happening), but if that's not possible I'll just reuse the boot.S/h files from the DFU bootloader.

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

Of course, you need to know the address you want to jump, and you need to have all low level initializations there (stacks etc). Here is how to jump to a specific address:

	typedef void(*fn_prt_t)(void);
	fn_prt_t bootloader;

	bootloader= (fn_prt_t)0x80000000;
	bootloader();

Replace 0x80000000 with correct address.

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

Great!

I don't know the address I'll be jumping to yet, as I haven't yet completed the bootloader, so I don't know how much space it will actually take up. Of course, once the bootloader is done it's quite simple to pick the address of my user application.

When you say "low level initializations" what exactly are you referring to? For instance, my code starts the main loop with something like this:

irq_initialize_vectors();
	cpu_irq_enable();
	wdt_clear();
	wdt_disable();
	sleepmgr_init();
	sysclk_init();
	board_init();
	delay_init(sysclk_get_cpu_hz());

Is this the kind of thing you're referring to, or do you mean something completely different?

Thanks!

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

The low level initialisations must be done before your main loop is started. In my old-ish ASF 1.7 this is done by assembly code in crt0.x. It contains stuff like this:

// Set initial stack pointer.
lda.w   sp, _estack
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think this is all being handled by ASF, but I'm not certain.

Another relevant question though, if I initialize a peripheral in a bootloader, and then jump to my user application, is the peripheral still initialized? Or another way of putting it, if I turn something on in the bootloader, do I need to make sure to turn it off before leaving the bootloader?

I'm assuming yes, but I'm not sure if ASF does anything behind the scenes.

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

Jumping directly to an address, possibly as shown by jkuusama, leaves everything unchanged except for the PC register. Not only do register and SRAM contents stay the same, all periphery stays the same. For example, the ISP must disable the WDT after the user enabled it for a reset. Leaving interrupts on and other things that would poke your application unasked is a Bad Thing.

That said, changing all peripherals back to the defaults when leaving the ISP would be good practice, but assuming that they’re in a certain state when you want to use them in your application is quite bad practice. ;-)

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

So this might be a really dumb question then...but I don't think ASF provides code to "turn off" some of the peripherals. For example, I'm using the SD card via MCI. There's an sd_mmc_mci_init function, but nothing to halt it. I'm sure I could write something else to take care of that, but I haven't looked too hard yet.

Alternatively, I might be able to do some funny business with setting fuses, but it would need to be coordinated between my main program and the bootloader.

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

Trying to use the following code and it's failing to do anything.

	typedef void(*fn_prt_t)(void);
	fn_prt_t user_application;

	user_application = (fn_prt_t)USER_APPLICTION_START_ADDRESS;
	user_application();

To be completely fair, I'm not certain that my bootloader is copying everything over correctly, and I haven't had a chance to test that yet (tomorrow morning's goal). All I can tell is that it executes the start command, and then stops, which I'm assuming means I either did it wrong, or there's no code where it's jumped to.

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

Well, check for write errors then. ;-)

Some more ideas:
* Check the Flash write errata in the datasheet. Many UC3s don’t like writing to the Flash in 1 waitstate mode.
* Is your program compiled to be run starting from USER_APPLICTION_START_ADDRESS? If it’s compiled to run from 0x80000000 and you copy it elsewhere, it won’t run correctly.
* Is your program using a trampoline? If yes, the resulting binary is for running from 0x80000000, even if your application code starts at a higher address.

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

So my code is using a trampoline, but how does that work with the DFU bootloader occupying 0x80000000? I thought the trampoline just puts a jump command at 0x80000000, so if the DFU bootloader isn't there it jumps to the appropriate spot.

I changed PROGRAM_START_OFFSET in the user application's trampoline.h to 0x8000 (from 0x2000), and cleaned/compiled. I checked the resulting hex file, and it looks correct, no user code until 0x8000. Is there something else that needs to be done instead?

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

Well, if your trampoline has a size of 0x2000, then you must strip the first 8 KiB off the resulting binary and write the rest to the Flash starting from address 0x80002000. If you don’t strip the first 8 KiB, you would have to overwrite your bootloader for the addresses to be correct.

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

Turns out the trampoline works great, so long as you're doing flash math right.

For instance, AVR32_FLASHC_ADDRESS and AVR32_FLASH_ADDRESS are two very different things. Very related, and very different. Things don't work well when you try to write a user application to AVR32_FLASHC_ADDRESS. On the plus side, I don't think I did any lasting harm...

Hooray for working things. And hooray for being a complete idiot...

*sigh*

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

iwoloschin wrote:
Hooray for working things. And hooray for being a complete idiot...

As my boss once said "must have been working on that while logged in as moron." :lol:

Letting the smoke out since 1978

 

 

 

 

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

Refreshing the thread instead creating new one because I have exactly the same problem.
I created my custom bootloader, and application is sitting at 0x80010000.
I tried to call it the way described here, but it just hangs.
I checked, the application is in the right place, I am making a call and nothing.
Could you tell me how did you handle it ?

Regards
Pees