Which PIC18?
Do you use interrupts on the bootloader?
All my bootloaders are designed like this:
Bootloader resides at the start of memory, as it is intended*
Let's assume the application code starts at 0x2000.
1)Create the bootloader project. Restrict the ROM range in the linker options by adding 0-1FFF
2a)If the device has the legacy interrupt controller (only capable of high/low priority) the bootloader will not use interrupts, but polling. Which is 99% of the time fine, anyway.
I add an asm file to the project containing this code
;remap the interrupt vectors
psect HiVector,class=CODE,delta=1,abs
org 0x08
goto 0x2008
psect LoVector,class=CODE,delta=1,abs
org 0x18
goto 0x2018
end
The effect is to redirect the interrupt vectors to the application program memory by placing exactly two goto instructions where needed.
Because the bootloader doesn't use interrupts, this doesn't mess anything.
The code above plays well with pic-as, the assembler in current XC8 (2.00-3.00)
2b) If the device has the newer VIC module, just use the interrupt as default in the bootloader.
In the application, however, you will have to explicitely redirect the IVT, for example at 0x2200 so, in the application
- every interrupt routine will have to use the base(0x2200) attribute. this the only absolute address you'll have to enter in the application firmware
- don't forget the default interrupt, also with base(0x2200) attribute
- in the bootloader/application project (whichever will set the configuration bits), don't forget to set IVT1WAY = OFF, because the startup code sets IVT base and locks it
- add to the firmware project, linker options this line: -mivt=0x2200
Application startup code will take care of remapping the IVT.
3) To launch the application, just do this
#define ApplicationEntryPoint 0x2200
void vBlStartApplication(void) {
asm("ljmp " ___mkstr(ApplicationEntryPoint));
Nop();
Nop();
}
ljmp is a pseudoinstruction that will also load PCLH if needed before the goto.
You can also do
void (*startApplication)(void) = (void*)ApplicationEntryPoint;
startApplication();
Nop()
Nop();
but that produces a call instead of a goto, and you lose a call stack level. Though some (all?) PIC18 can manipulate the call stack and reset it in the startup code so it should not be a problem
The application then gets written as if there was no bootloader. Just remember (2b) if the device has vectored interrupts.
After the application is complete
1) Go to the linker options and add
2000 to code offset. All addresses that are not absolute will be shifted by 2000, making the application run in the "application space" outside the bootloader.
2) In the project options, go to loadables and add the bootloader project.
Now, when you compile, the compiler will compiler the bootloader project, the application project, put the respective hexes at the usual place and then combine the two using hexmate. The hex file containing "unified" in its name is the combination of the two.
When you debug, if a breakpoint hits, or if you are single stepping in memory that belongs to the bootloader you will be looking at the debugger source code. Neat!
* The debugger executive code likes to be put at the end of memory. It also plays havok with the SAF when you enable code execution exception from there.