Hi, I am developing a boot-loader for lpc1769 based custom board using KEIL uv5. My approach is to have a separate boot-loader project starting at flash 0x00000000. The main application is another project which exists at a higher address on flash. My boot-loader uses USB for communication with PC software as a simple VCOM and relies on reading some settings from I2C EEPROM. In short, it uses some RAM(all static data). Both my projects have default KEIL startup file and settings(taken from lpcopen 2.10 distribution) with only flash start address of application changed to a higher value. To jump after a successful Bootloader flash, I am trying to use this piece of code from www.nxp.com/.../AN10866.pdf
__asm void boot_jump( uint32_t address ){ LDR SP, [R0] ;Load new stack pointer address LDR PC, [R0, #4] ;Load new program counter address } void execute_user_code(void) { /* Change the Vector Table to the USER_FLASH_START in case the user application uses interrupts */ SCB->VTOR = USER_FLASH_START & 0x1FFFFF80; boot_jump(USER_FLASH_START); }
I am not very convinced by this. First, I think before loading PC, the instruction pipeline should be flushed. Second, interrupts should be disabled as USB, systick all are active. Now in my example, main application has it's own startup file, so it will do a copy of RW and ZI data to RAM. Because my application has same RAM start address as BL, I assume this is as good as reclaiming Bootloader RAM. But this means I will have to reinit USB and other peripherals. I am confused if this is the best way to do it as on every startup there is a overhead of init-deint peripherals and again init'ing them in main application. Also I guess, I have to manually clear Interrupt pending register before jumping to application. This bring me to my rather convoluted query. - What would be the best way to have a 'fake' reset so that application starts cleanly at its address? Is there any way to convince cortex m3 to start at a different address than 0x0000000 on reset? Is it possible in KEIL debugger to set up two projects and see step through this jump from BL to application ?
Thanks.
If it disturbs you, branch via a register
__asm void boot_jump( uint32_t address ){ LDR SP, [R0] ;Load new stack pointer address LDR R0, [R0, #4] ;Load new program counter address BX R0 }
You could load it into LR too, and 'return' to that new address.
To do an actual reset, perhaps use NVIC_SystemReset(), after you've set a fixed RAM variable, and recognize that in your ResetHandler code on the boot loader side, and immediately set up and go to your new VTOR address for your application.
The Cortex-Mx parts always boot from address zero, or whatever is mapped/shadowed there by the particular implementation, often a FLASH, ROM or RAM base address. The choices generally are very limited, so addresses picked randomly by the user, aren't going to work.
If control is handed over in the manner I described above, the application code will enter with extremely close to reset conditions.
Hi Westonsupermare pier, Thank you for the valuable opinion. I don't see a lot of difference with 'You could load it into LR too, and 'return' to that new address.' I was actually talking about lack of interrupt disabling in the above code.
The reset is a good idea as the most common case of invoking bootloader would be from application program(in my project).
The reset is the solution to disabling the interrupts - it will restore the hardware to the state the application will accept. So the boot loader checks some magic flag after the reset and then decides to not activate any USB or other hardware and instead directly jumps to the application code with the CPU in a stable and well-defined state.
Not sure disabling interrupts really helps, because you end up with similar problems when you reenable them.
Ideally the boot loader shouldn't handover control with randomly enabled/firing interrupts. You either clean that stuff up, or you have a much more defined handover where the application can take over those responsibilities immediately. Just jumping to some standalone app (ie code just compiled/linked for a different base address) isn't the way to go, as there are lots of other C runtime and static initialization, and RAM is allocated with no regard to previous structures/content.
If "manually" trying to restore the processor to a pristine state, then it most definitely isn't enough to disable interrupts. Every register of every device must be returned to the factory default, since the boot loader can't know which registers the application will assign and which registers the application intends to keep with the factory default value. And the order of initialization matters.
A program that first enables the interrupt for a device and then sets the ISR and then configures interrupt sources would make interrupt calls out in the air if one or more interrupt sources has already been enabled. So in the end, it's quite important to consider internal hw reset or wd reset to quickly normalize the chip. A boot loader normaly lives for many years, and it isn't possible to predict what expectations the application may have several years into the future.
Hello Westonsupermare, Per Westermark,
I was maybe not clear, but I agree with the procedure of doing a system reset(NVIC) rather than trying to put processor manually into a 'reset state'. This ensures a clean slate with no C lib init, no scatter loading, no stack-heap setup, no RAM/ZI copying already done.
In my project, new firmware request will be done by PC SW talking to application code(not BL)
Just evaluating pros/cons of having magic number in flash(helpful in power-up's) or in RAM (enough for communication between application and BL but cannot handle power-up's). In the latter case each power up will go into BL at-least once before jumping to application code(by another reset but this time with magic number)