Using Code offset in PIC® Devices with the MPLAB XC8 Compiler
Bootloaders and Downloadable Applications
It is common practice when writing bootloaders and downloadable applications to use the MPLAB® XC8 compiler’s code offset feature to ensure that the code associated with the reset and interrupt vectors is correctly positioned. However, when using PIC18 devices with the Vectored Interrupt Controller (VIC), you must take extra steps to ensure that the interrupt vectors are placed as expected. In this article, we’ll look at how to control your code’s reset and interrupt entry points for any 8-bit PIC® device that uses interrupts.
This shows a simplified representation of a PIC’s program memory for an ordinary PIC18 device, with address 0 at the top. The address to which the device will vector on reset and the addresses to which the device will vector on a low- or high-priority interrupt are represented by the arrows on the left. These addresses are fixed by hardware and can never be changed. Mid-range PIC devices have only one interrupt, however, they operate in a similar manner.
Located at each vector address is an instruction to which execution is transferred when the reset or interrupt occurs. Often these are jump or goto instructions that transfer program execution to code located elsewhere, as indicated by the dashed arrows and larger colored blocks in the diagram. However, in some cases, this instruction might be the first of a block of code. The compiler automatically populates the program memory at these vector addresses from information in your project’s source code.
Figure (1b) shows what happens when you use the code offset option (-mcodeoffset in XC8 v2.x, or --codeoffset in v1.x compilers). Notice that the vector addresses do not move, as these are fixed by the hardware. However, the instructions which were located at these addresses do. It is important to appreciate that at this point, this program would fail to execute because there is no valid code at the vector addresses. It is the job of a bootloader or other code to transfer execution to the offset instructions. Notice also that the bulk of the program’s ordinary functions are not directly affected by this option. However, the compiler ensures they are not linked anywhere from address 0 to the code offset address.
Here we show a PIC18 device with the VIC. Device reset is implemented as per any other PIC18 device, and again, the reset vector address is fixed by hardware. However, these devices use a large table of interrupt vector addresses, instead of the dual low- and high-priority interrupts used by other devices. The compiler automatically populates the table with addresses (not instructions) to which the device should vector for the interrupt corresponding to that location in the table. Unlike the reset vector address, the base address of the vector table that will be used at runtime is specified by the combined content of the IVTBASE registers, which are writable during program execution.
This shows the effect the code offset option has on the program. The option moves the reset instruction by the specified offset, as with other devices, but it has no effect on the vector table.
Shown a project that has moved the vector table, but which has NOT used the code offset option. The interrupt functions in this project were defined using a base(address) argument, and then the IVTBASE register was adjusted to this base value. A project implemented in this way will execute correctly without requiring any further code.
If you want to move both the reset instruction and interrupt table, as shown in Figure (2d), then you must use the code offset option, as well as adjust the IVTBASE register and use a shifted base address for the interrupt functions.
Devices with the VIC can be run in a legacy mode by disabling the MVECEN configuration bit. In this mode, it operates similarly to a PIC18 device without vectored interrupts. You can see in Figure (3a) that there is no vector table. Instructions (rather than addresses) are located at the low- and high-priority interrupt vector addresses, as with ordinary PIC18 devices; however, in legacy mode, the two interrupt vector addresses are still controlled by the IVTBASE register and are not fixed by hardware. This means that when using the code offset option, as shown in Figure (3b), only the instruction associated with the reset vector is shifted. If required, the interrupt vector addresses can be adjusted, as shown in Figure (3c), by using a base() argument when defining the interrupt functions, and adjusting the IVTBASE register to this base value and, once again, these changes to the interrupts and use of the code offset option are both required if you want to move the code associated with all three vectors, as shown in Figure (3d).