Electronic – PIC ISR – check every interrupt source or just one

assemblyinterruptspic

I'm curious if there are any particular wins for using a call instead of a goto when checking each interrupt source in an ISR. My current code would do something like this:

.intr   CODE        4

    pagesel     $

    btfsc       INTCON, TMR0IF          ; Timer0
      goto      Timer0_Entry

    banksel     PIR1                    ; Bank #0
    btfsc       PIR1, SSP1IF
      goto      I2C_Entry               ; I2C Peripheral
...

but that is on a enhanced midrange pic16 where the context save is done for you. If I was on a PIC16F88 say, there would be a (minor) win for avoiding the whole tedious context save in a situation where two simulataneous interrupt sources were pending.

Doing it with gotos implicitly creates a priority list which can stall other sources – if I had multiple interrupts from one source, that source would stall the other irq sources until it stopped. I hit that once by forgetting that the serial port irq flag is asserted by default.

But other than that, is it just a question of taste? The hit you take (in a multiple irq situation) by doing a retfie and then heading back into the ISR immediately doesn't particularly severe.

Best Answer

TL;DR: Use as few interrupts as you can, and keep their ISR's short. Offloading non-critical work to the main loop is a big help.


On a PIC, with its single interrupt vector (double for PIC18), I often do this:

void ISR()
{
    if(FLAG1_EN && FLAG1)
    {
        //ISR1
    }
    if(FLAG2_EN && FLAG2)
    {
        //ISR2
    }
    //etc.
}

This avoids function calls from the ISR, which causes my compiler to generate a bunch of extra context-saving code, even on a chip that does (some of) that automatically.

If necessary, I can order them by priority and return from each without checking the rest of the flags, so that the higher priorities get polled more often. Or to save some latency, you might jump back and repeat the ISR, only returning if there are none active.

Or, if some ISR's are periodic tasks that only have to average to the correct frequency, you could have their ISR's simply set a flag and leave. The main loop then watches for the flags and does the activities when it gets around to them. This makes the actual ISR super short, and allows a higher-priority task to interrupt its activity. In fact, you might even use the interrupt flag itself in the main loop and not enable the actual interrupt for it.

On this platform, you will never be guaranteed that a super-high priority interrupt will be serviced with constant latency, unless you can make it the only real interrupt source. There is always a chance that it will be postponed until a different one is finished. There are ways to reduce that chance and the consequence of it happening, but never eliminate it.

That being said, Spehro does have a good point about interrupts typically happening one at a time and usually not being a problem. Unless you spend all your time in the ISR; then you almost guarantee a collision.