embedded software boot camp

Unused interrupt vectors

Sunday, April 19th, 2009 by Nigel Jones

With the exception of low end PIC microcontrollers, most microcontrollers have anywhere from quite a few to an enormous number of interrupt vectors. It’s a rare application that uses every single interrupt vector, and so the question arises as to what, if anything, should one do with unused interrupt vectors? I have seen two approaches used – neither of which is particularly good.

Do nothing

I would say this is the most common approach. My guess is that when this approach is used, it’s not via conscious choice, but rather the result of inaction. So what’s the implication of this approach? Well if an interrupt occurs for which you have not installed an interrupt handler, then the microcontroller will vector to the appropriate address and start executing whatever code happens to be there. It’s fair to say that this will ultimately cause a system crash – the only question is how much damage will be done in the process? Having said that, I don’t necessarily consider that this approach is always awful. For example a reasonable argument might go something like this.

I know via design, code inspection, static analysis and testing that the probability of a coding error enabling the wrong interrupt is remote. Thus if it does happen it’s probably either via severe RF interference, or because the code has crashed. In either case the system has bigger problems than vectoring to an unsupported interrupt.

Of course anybody that’s put this much thought into it, will probably be conscientious enough to do something different.

Another valid argument on very memory constrained processors is that you need the unused interrupt vector space for the application. Indeed I have coded 8051 applications where this has been the case. Such is the price we sometimes have to pay on very small systems.

Install ‘RETI’ instructions at all unused vectors

In this approach, you arrange for there to be a ‘Return From Interrupt’ instruction at every unused interrupt vector. Indeed this approach is common enough that some compiler manufacturers offer it as a linker option. The concept with this approach is that if an unexpected interrupt occurs, then by executing a RETI instruction, the application will simply continue with very little harm done. All in all this isn’t a bad approach. However it has several weaknesses.

  • The biggest problem with this approach is that it doesn’t solve the problem of an interrupt source that keeps on interrupting. The most egregious example of this is a level triggered interrupt on a port pin. In this case, depending upon the CPU architecture, it is quite possible for the system to go into a mode whereby it essentially spends all its time vectoring to the interrupt and then returning. However this is by no means the only example. Others that spring to mind are ‘Transmit buffer empty’ interrupts, and timer overflow type interrupts. In the latter case, the system probably wouldn’t spend all of its time interrupting; however a certain fraction of the CPU bandwidth would be wasted, which in a battery powered application for instance, would be a big deal.
  • If you do this at the start of a project, you lose the opportunity to discover errors in which an interrupt source has been erroneously enabled. In short this approach can mask problems, while what is really needed is an approach that can reveal problems.

Recommended Approach

What I do is the following.

  1. At the start of a project I create a file called vector.c In vector.c I create an interrupt handler for every possible interrupt vector. Not only is this an essential first step in solving the problem, I also find it very illuminating as it forces me to read about and understand all the CPU’s interrupt sources. This is always a useful step, as in many ways the interrupt sources for a CPU tell you a lot about its capabilities and the designers intent.
  2. Within each interrupt handler, I explicitly mask the interrupt source. This will prevent the interrupt from reoccurring in all but the most extreme of cases.
  3. If necessary, I also clear the interrupt flag. (In some CPU architectures this occurs automatically by vectoring to the interrupt. In others you have to do it manually).
  4. After masking the interrupt source, I then make a call to my trap function. What this means is that while I’m debugging the code, if any unexpected interrupt occurs, then I’ll know about it in a hurry. Conversely, of course, with a release build, the trap function compiles down to nothing, essentially removing it from the code.

Here’s a code fragment that shows what I mean. In this case it’s for an AVR processor and the IAR compiler. However it should be trivial to port this to other architectures / compilers. Note that for the AVR it is in general not necessary to clear the interrupt flag as it is cleared automatically upon vectoring to the ISR.

#pragma vector=INT1_vect /* External Interrupt Request 1 */
__interrupt void int1_isr(void)
{
 EIMSK_INT1 = 0;  /* Disable the interrupt */
 /* Interrupt flag is cleared automatically */
 trap();
}
#pragma vector=PCINT0_vect /* Pin Change Interrupt Request 0 */
__interrupt void pcint0_isr(void)
{
 PCICR_PCIE0 = 0; /* Disable the interrupt */
 /* Interrupt flag is cleared automatically */
 trap();
}
...
#ifndef NDEBUG
/** Flag to allow us to exit the trap and see who caused the interrupt */
static volatile bool Exit_Trap = false;
#endif
static inline void trap(void)
{
#ifndef NDEBUG
 while (!Exit_Trap)
 {
 }
#endif
}

Home

3 Responses to “Unused interrupt vectors”

  1. GregK says:

    1. The best way to track why this unwanted interrupt happened (assumed software crashed, it is only reason I can imagine, we can not avoid RF problems ) is simply put a breakpoint on write memory location where interrupt enable flag exist (in our example is address of EIMSK_INT ). If flag is not shared among other flags what you modify during program execution then you are almost at home 2. ‘Proper’ compilers (actually linkers) support “default interrupt”, for example pic24H has 117 interrupts’ sources … so if we do not assign some of them but we define default interrupt we can trap this in only one place.

  2. Nigel Jones says:

    Hi Greg. I have a couple of problems with what you suggested in point 1.1.It doesn’t solve the problem of an errant pointer being dereferenced and causing the interrupt to be enabled.2.A lot of debuggers have limited break points, so dedicating breakpoints to things that you don’t think are going to happen is rarely done.Regarding the proper compiler. The feature you describe sounds helpful (and it sure beats defining 117 interrupt handlers). Being a default handler though, how difficult is it to determine what tripped it – and thus what the problem is?

  3. GregK says:

    Hi NigelAd.1 In this case I do not have ambition to catch problem what I do not expect and cannot reproduce, it is actually point how to catch something when we already determine which unwanted interrupt occurred by your trap method.Ad.2 Do You mean which source caused call this handler? It is not easy I suppose , probably you have to open SFR window in IDE and go through all flags to see which one is set. It is not big effort once every 3years (probably we do not have to do this to often). However IDE always support some kind of signalling which registers changed its value from last refresh so it should not be difficult.Back to Ad.1 There is other way to catch overwritten enable flag register without breakpoints, just set on start up all pending flags for not used interrupts, and before of course clear enable registers for that sources if not by default… Vice versa should works as well (clear pending flags, enable interrupts).

Leave a Reply