Nested Interrupts

nest
Normally, the AVR hardware clears the global interrupt flag (in SREG) before entering an interrupt. This means other interrupts are disabled inside the running handler until the handler finishes and exits.

The RETI instruction is the normal function epilogue for an interrupt handler, and thus re-enables interrupts (just like executing an SEI instruction).

For this reason, normally, interrupt handlers can not be nested.

For most interrupt handlers, this is the desired behavior. For some its even required in order to prevent infinitely recursive interrupts. Recursion could easily exceed the available stack space and crash the running program. In rare circumstances it might be desirable to re-enable the global interrupt flag as early as possible inside the interrupt handler, allowing recursive interrupt handling. Carefully.

This could be accomplished by inserting an sei() instruction right at the beginning of the interrupt handler, but this still leaves a few instructions inside the (compiler-generated) interrupt prologue to run with global interrupts disabled. A better method is to instruct the compiler to insert an SEI instruction at the very beginning of the interrupt handler by declaring the handler in the following way (XXX_vect is the name of a valid interrupt vector):

ISR(XXX_vect, ISR_NOBLOCK) {
  ...
}

Compare the following three disassembly examples which demonstrate how this works:

Non-nested, normal method:

ISR(TIM0_OVF_vect) {
  //do nothing
}

     PUSH      R1             
     PUSH      R0             
     IN        R0, 0x3F        
     PUSH      R0             
     CLR       R1             
     POP       R0             
     OUT       0x3F, R0        
     POP       R0             
     POP       R1             
     RETI                     ; <<-- Interrupts re-enabled here!

Nested, SEI inserted after prologue:

ISR(TIM0_OVF_vect) {
  sei();
  //do nothing
}

     PUSH      R1             
     PUSH      R0             
     IN        R0, 0x3F        
     PUSH      R0             
     CLR       R1             
     SEI                      ; <<-- Global Interrupts re-enable here!
     POP       R0             
     OUT       0x3F, R0        
     POP       R0             
     POP       R1             
     RETI                     

Nested, best method:

ISR(TIM0_OVF_vect, ISR_NOBLOCK) {
  //do nothing
}

     SEI                      ; <<-- Global Interrupts Enabled here!
     PUSH      R1             
     PUSH      R0             
     IN        R0, 0x3F        
     PUSH      R0             
     CLR       R1             
     POP       R0             
     OUT       0x3F, R0        
     POP       R0             
     POP       R1             
     RETI                     

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a comment