AVR Interrupt Priorities

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I finally read the datasheet details about ATmega32 interrupt priorities and will share here details learned. (Other devices may differ; not sure.)

If you enable interrupts in any ISR then that ISR can be interrupted by ANY interrupt no matter the priorities.

The priority is determined by the order of the vector addresses and determines which interrupt is serviced when two or more are waiting. (The lowest number is serviced first.)

Enabled interrupts that are scheduled while SREG is set to disable all interrupts will be serviced when global interrupts are enabled provided the individual interrupt wasn't cleared already by software.

One instruction of main code is executed between interrupts. I am not sure if this is correct: if the interrupted code is itself an ISR then the "one instruction" rule may apply there. Anyone know?

No doubt this is covered in the FAQs and I probably got something wrong. But perhaps someone will find this useful.

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Once you enable interrupts in an ISR I wouldn't call that an ISR anymore, it is plain interruptible code. Which also answers the second question, it doesn't matter why code is currently executed (main,ISR), the only thing that matters is: Is it interruptible?

The rest gets handled by the stack.

Have fun,
Markus

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree with MegaTorr, the second ISR doesn't know where it came from, only that it is returning. When the RETI instruction is run, it sets the global interrupt flag which doesn't take effect for one clock cycle. Which brings up the question that if you set global interrupts with SEI, does it also not take effect for one clock? I think that this has been discussed here before, but I don't recall what the conclusion was.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, SEI will set the global interrupt enable bit, allowing any interrupt to occur *after* the following instruction as there's a delay for it to take effect.
CLI will however take immediate effect and block interrupts in its own cycle.

Source: 8-bit AVR Instruction Set
(Edited with confirmed information on CLI behaviour.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sounds logical, except for:

MegaTorr wrote:
Once you enable interrupts in an ISR I wouldn't call that an ISR anymore, it is plain interruptible code.
Forgive my nitpick but an interruptable ISR is still an ISR, if it in fact services the interrupt.

So after RETI, one instruction must execute to completion before a pending interrupt is serviced, that instruction being the one pointed to by the address popped off the stack.

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

cpluscon wrote:
One instruction of main code is executed between interrupts.

I was under the impression that the current instruction (be it 1, 2 or 3 clock cycles) was completed before the interrupt process actually began.

cpluscon wrote:
I am not sure if this is correct: if the interrupted code is itself an ISR then the "one instruction" rule may apply there.

The key to this thought is that, for a given interrupt handler, you'd better get in, get the job done, and get out (properly exit) of that interrupt handler, before the next entry into that interrupt handler begins. If you don't, you'll over run the stack and the rest of the SRAM.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

microcarl wrote:
I was under the impression that the current instruction (be it 1, 2 or 3 clock cycles) was completed before the interrupt process actually began.
That is my understanding as well.

I certainly agree with your other point. However, I am not ready to state that in ALL cases you must not enable interrupts in an ISR. Under certain circumstances it may be desirable. (But dangerous unless implemented with care?)

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

cpluscon wrote:
I certainly agree with your other point. However, I am not ready to state that in ALL cases you must not enable interrupts in an ISR. Under certain circumstances it may be desirable. (But dangerous unless implemented with care?)

Well, that depends...

I run multiple interrupts all of the time. But the interrupt handlers are unique and specific to hardware modules.

I frequently have at least two interrupt driven timers and IRQ0, all active at the same time. The priority interrupt handler takes care of those things and I hope that the priorities fall to my advantage. It would be nice if it always worked out that, the handlers with the highest interrupt rate had the higher priority but, that is wishful thinking. There are always compromises.

But in the case of say, IRQ0, where the interrupt is repetitively triggered by two different device signals that are "OR-Tied " at the IRQ0 pin. That interrupt (IRQ0) handler must execute its code and get out faster then the repetition period, or arrival of the next scheduled IRQ0 interrupt.

You can't have USART characters coming faster then it takes for the USART interrupt handler to process each incoming character.

You can't have a timer interrupt occurring faster then the time it takes to process the code within that timer interrupt handler.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The key thing about enabling interrupts in ISRs is that you need to be certain that the handling of the current interrupt will complete before another one of the same type may occur. It doesn't matter if an interrupt on another vector occurs (as long as its handler doesn't delay things so long that the handling of this current interrupt isn't finished before the next one. Otherwise you already have a RETI address on the stack and then you stack another one (and another one and another one and another one.... until the stack hits the variables)

But a bit like chaos theory where a few simply expressed rules interact to make a system so complex it cannot be predicted, so it is with ISRs with interrupts being enabled - so I'd avoid it at all costs unless there really is NO other alternative.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some of the older machines had a means to set the interrupt priorities. If a certain hardware function had to be a higher interrupt priority then some other hardware function, simple... move that hardware functions priority where you wanted it.

I've also use an "8 in to 3 out " multiplexer with the common strobe connected to the external IRQ pin. When a particular event triggered one of the 8 multiplexer inputs, the IRQ interrupt pin was triggered. Upon entry to the IRQ interrupt handler, the 3 multiplexer outputs were read and the proper section of the IRQ interrupt handler code was then executed.

But even then, you ran the risk of a stack crash or missing other IRQ generated interrupts, if everything wasn't handled properly.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We'd probably agree that more than 99% of the time ISRs simply shouldn't be interruptable, to keep things simple and safe.

Getting beyond that there are two different scenarios. The ISR that can be interrupted by OTHER ISRs, and the reentrant ISR. The former is probably more likely to come up than the latter.

However, one embedded system I've worked with (not uC!) drove a scheduler within the context of a timer interrupt and it was reentrant. It would reenable interrupts quickly just after performing some bookkeeping that required mutual exclusion.

Higher proirity tasks would be serviced correctly despite the overrun while lower priority offenders would get throttled. Of course this technique was probably susceptible to the stack underflow problem, but there may have been detection/handling of that condition. (Didn't study it that hard.)

Luckily such sophisticated techniques rarely are needed in a small MCU.

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's okay to have multiple hardware block interrupts enable at the same time. You just have to make sure that the interrupt with the higher frequency can take control over the lower priority interrupts. And that thought trickles down hill to the lower priority interrupts, as well.

When I have a system with multiple interrupts, when the highest interrupt is entered, the first thing I usually do is re-enable the global interrupt system, Then the lesser priority interrupts can be serviced simultaneously (seemingly). The catch here is that, you have to be absolutely certain that you don't get stuck in a quagmire where all you are doing is interrupting and not doing any useful work in the main loop. And, you still have to be absolutely sure that interrupts aren't coming so fast that the stack is over-run.

A major key concept of interrupts it to get in, and get out! Use the minimum code possible, and no more! If something can be done in the main loop, do it there. Use flags as indicators to the main loop that a particular event has transpired and do the actual work that can be done, in the main loop.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

Last Edited: Thu. Oct 18, 2007 - 11:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
... an interruptable ISR is still an ISR, if it in fact services the interrupt.
That's probably what I tried to say with the (provocative) statement. Since all bets are off in regards to actually servicing the interrupt.

The Linux/Unix kernel has this concept of bottom-halfs. In the interrupt context all you are supposed to do is get your IO lines correct and start off the bottom-half of your task. The bottom-half, once started runs in regular process context. So what qualifies as your ISR? Just what runs in the interrupt context itself? Or does the bottom-half belong to it?

I tend to prefer the first view where only what runs in interrupt-context is the actual ISR, and starting off the bottom-half is part of that. The bottom-half itself is more or less plain application code because it runs in the same context and also is subject to the same constraints (in the AVR world: it can be interrupted for indefinite times).

This view makes it easier for me because ISRs have to follow certain (programming style) rules. So I only call an ISR what has to play within ISR constraints.

But you are of course right, all this is just nitpicking and terminology.

Have fun,
Markus

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(I'm not really wanting to enable irq's inside irq's, but just interested in how things work- and sorry if someone has already covered this above)

mega168 datasheet-
'When the AVR exits from an interrupt, it will always return to the main program and execute one more instruction before any pending interrupt is served.'

does 'exits from an interrupt' mean a RETI instruction?

I conclude no. It means (to me)-
'When the AVR exits from an interrupt with a RETI, it will always return to the address popped from the stack and execute one more instruction before any pending interrupt is served. If global interrupts are enabled before the RETI instruction, one more instruction will execute after the SEI instruction, then any pending interrupts will be served.'

So the key is the enabling of interrupts either by RETI or SEI. One instruction always executes after either of those instructions. In the case of RETI, you just end up wherever the PC is 'popped' back to, and execute (at least) 1 instruction.

test case 1 - (run in simulator only)
will not overflow stack
the reti always executes
(RXC is manually set to get it going)

#include 
#include 

int main(void){
UCSR0B = (1<<RXEN0) | (1<<RXCIE0);
sei();
while(1);
return 0;
}

void USART_RX_vect(void) __attribute__ ((naked)); 
void USART_RX_vect(void){
sei();
__asm__ __volatile__ ("reti" ::);
}

test case 2 - (run in simulator only)
will overflow stack (because of pushes in isr)

#include 
#include 

int main(void){
UCSR0B = (1<<RXEN0) | (1<<RXCIE0);
sei();
while(1);
return 0;
}

ISR(USART_RX_vect){
sei();
}

but I could be wrong.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
does 'exits from an interrupt' mean a RETI instruction?
In the case of Atmel's datasheets it means exactly that. They don't consider enabling interrupts in an ISR.

What they say is that one more instruction will be executed after interrupts are enabled again. It doesn't matter how interrupts are enabled.

So if you manually enable interrupts with sei() within an ISR then one more instruction of your ISR is executed before any pending interrupts are serviced.

But if you run your ISR to completion without enabling interrupts then the reti instruction will that for you, in which case the next instruction from whatever was interrupted will be executed.

Quote:
test case 2 - (run in simulator only)
will overflow stack (because of pushes in isr)

There should be no pushes in the ISR, no stack overflow neither (unless USART_RX_vect triggers at least every 5 clock cycles).

Hope this made it a bit clearer.
Markus

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

test case 2 compiles with a few pushes/pops for me. Which cause a stack overflow. That sei() comes after the pushes, so the irq pushes, then sei(), then one instruction, then irq again, repeat. test case 1 (in the simulator) will never show the stack being used as the reti (pop of stack to pc) and irq (push pc on stack) are back to back.

I was just trying to do 'isr's within isr's' as easy as possible, so just thought of rx without ever getting udr to leave the irq always 'pending'.

Isr's withinin isr's can end up in a big mess, in a big hurry, in a big way. Good luck to those who attempt it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:
Isr's withinin isr's can end up in a big mess, in a big hurry, in a big way. Good luck to those who attempt it.

I obviously have no problem doing concurrent ISRs, but I think a nesting ISRs might be setting yourself up for some hellashus bugs.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

microcarl wrote:
I obviously have no problem doing concurrent ISRs...
Well, that's why you get the big bucks :). Guys like me should not even attempt that. I would forget to get udr before the sei, and end up scratching my head for days.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MegaTorr wrote:
The Linux/Unix kernel has this concept of bottom-halfs. In the interrupt context all you are supposed to do is get your IO lines correct and start off the bottom-half of your task. The bottom-half, once started runs in regular process context.

Most RTOS have a concept of HISR which are like "mini tasks" and are scheduled in a similar way to the normal threads (except the HISR list is scanned for anything needing service BEFORE the normal thread list). You have ISRs for direct interrupt handling of the hardware but on the whole these should do the minimal work necessary (probably just clearing the interrupt flag/source) and then they trigger the associated HISR to run.

I imagine AVR RTOS' have a similar concept. As always the key thing with ISRs if "keep 'em lean and mean" and do the main "work" outside the actual interrupt context.

PS if one really wanted to SEI in an ISR but not worry about re-entrancy then I guess the ISR could just clear the IE flag for its own source until just before the final RETI