Interrupts

Here are a few observations and tips I have from my experience working with interrupts in Swordfish.

Defining Interrupt Service Routines (ISRs) using Interrupt()

Only one high priority and one low priority interrupt service routine (ISR) definition can be compiled in at a time. It's not possible to have, for instance, two high-priority interrupt routines with different names, then enable only one or the other. The way the enable() command works is that it sets the high priority interrupt flag only, not the the interrupt location. The interrupt routine location is only referred to in the interrupt vector at the base of the MPU code, with location 0x08 for high priority and 0x18 for low priority, as defined in the Microchip 18F manuals.

Where ISRs can be defined

You can have both the ISRs in the main program, one of the ISRs in the main program and one in a module, or both ISRs in one or two modules. You have to define the priority level in the interrupt() routine, including in the respective modules or by the use of an #option statement at the top of the main program that is read by the respective module(s).

Enabling and disabling ISRs

Resist the temptation to set the INTCON bits manually. Use the Enable() and Disable() directives instead.

Priority level is always high when one ISR is defined

Even if only one ISR is defined in the code and is designated as a low priority interrupt, it will be implemented as a high priority interrupt by the compiler.

Context saving on execution of an interrupt routine

Only one level of interrupt can be accomodated by the Fast Register Stack built into 18F hardware, where the STATUS, WREG (W register) and BSR (memory Bank Select Register) are stored until the ISR is complete and returns to normal program execution. When there are two levels of interrupt defined, if a high priority interrupt is called while a low priority interrupt is executing, the STATUS, WREG and BSR are saved again to a software-defined location that is automatically allocated by SF when compiled.

Ensure all the registers that will be changed by the ISR are saved

Users need to heed whether other critical registers are affected in any ISR; for example, if your routine uses any commands that utilize the hardware multiplier, then you should save the PROD registers (PRODL/PRODH) when you start the ISR and restore it when you leave the ISR.

Disabling of high and low priority interrupts

Users need to know that if both a high-priority interrupt and a low-priority interrupt is defined, if the the high-priority interrupt is disabled, the low priority interrupt is also disabled. This is because in the 18F chip, when priority levels are turned on by setting the IPEN bit (bit 7 in the RCON register), disabling the GIE (Global Interrupt Enable) bit (bit 7 of INTCON) all interrupts are disabled. This is an important departure from what you see in other multi-interrupt priority level chips, where each level is enabled/disabled independently.

So, it is important to realize that low-priority interrupts only need to be used when you know there is a more important function that has to be addressed immediately, even if it means pre-empting any other operation currently in use. Otherwise, if all functions can wait for the completion of other functions, you might as well put them all into one priority level. Trying to put them into high/low in order to logically organize them doesn't gain you much.

Execution of conditions in interrupt service routines

It's also important to note that even though an interrupt condition was met that fired an interrupt trigger, the 18F will still execute the entire interrupt ISR. So, ensure you define conditional statements in the interrupt that you can tolerate occuring whenever the interrupt is executed. For instance, even though timers, port conditions, etc. may have their interrupt enable flags off (for example, in the PIEx registers), they may still have the "tripped" flags on (for example, in the PIRx registers).

What does this mean? This means conditional statements defined in the ISR testing whether a flag has been being tripped will execute if INT0IF is tripped (a 1 or "true" if defines as a boolean) even if the INT0IE enable bit is "off". For example, an "If INT0IF = 1 then...Endif" conditional statement. Therefore, if you only want some of the conditional statements in the ISR to be executed, you would also have to test for whether the "enable" flags are set (in the PIE registers and some bits in the INTCON registers) in addition to the "tripped" flags (in the PIR registers and some bits in the INTCON registers).

Using interrupt "Events" allows for generic ISR modules

The use of an "Event" allows the user the flexibility of defining any other action that needs to be done during an interrupt without having to modify an ISR definition that is in a module. This means the module code with the ISR is left untouched so it can be a true generic library to be re-used for different programs. It also provides the benefit that any variables defined in the user's main program can be used in an ISR without having to have them referenced in the ISR's software module. Of course, since events are executed within the context of an ISR, they have to be written to ensure none of the operations would change any register values that would not be restored at the end of the ISR. In other words, treat then the same as you would when writing the interrupt service routine. This is a real brilliant feature of Swordfish that simplifies ISR module coding.