If you enable interrupts by storing to SREG is the next instruction executed?

On another thread an interesting question was raised (the thread was about dynamic array sizes).

The compiler generated some rather interesting code, I'm not sure if it is correct.

The problem:

The Atmega328 datasheet, page 15 says:

When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts, as shown in this example.

The example they give is:

__enable_interrupt(); /* set Global Interrupt Enable */
 __sleep(); /* enter sleep, waiting for interrupt */
/* note: will enter sleep before any pending interrupt(s) */

Or, in assembler:

sei        ; set Global Interrupt Enable
sleep      ; enter sleep, waiting for interrupt

The important thing is that after executing sei() the processor guarantees that the next instruction will be executed (before any interrupts are processed).


First question

Does this also apply, if interrupts are already enabled? (Not that I care that much). For example:

sei        ; set Global Interrupt Enable
sei        ; set Global Interrupt Enable
sleep      ; enter sleep, waiting for interrupt

Since interrupts are already enabled by the first sei instruction, does the processor always execute the next instruction (the sleep) in this case? Or, does this "next instruction will always be executed" only apply if the sei() instruction actually turned interrupts on?


Second question

What if you turn interrupts on in a different way? For example:

in r16, SREG     ; store SREG value
cli              ; disable interrupts 
                 ;  // do stuff here 
out SREG, r16    ; restore SREG value (I-bit)
sleep            ; go to sleep

In this case we are not literally using sei() - we are turning interrupts on with an "out" instruction. But does that count as "the next instruction will be executed"?

If the answer is "no, that only applies to the sei() instruction", then this code generated by the compiler (for dynamic array allocation inside a function) is wrong:

  f8:  0f b6        in  r0, 0x3f    ; 63   // save SREG
  fa:  f8 94        cli    // disable interrupts
  fc:  3e bf        out 0x3e, r19   ; 62   // change stack register high byte
  fe:  0f be        out 0x3f, r0    ; 63   // restore SREG, possibly turning on interrupts
 100:   2d bf        out 0x3d, r18   ; 61   // change stack register low byte

One guesses that the compiler outputs the code this way to have interrupts off for the shortest possible time. But where is the supporting documentation that says this is OK?

If the answer is "yes, anything that turns interrupts on causes the next instruction to be always executed" then some code can be made more efficient. For example, digitalWrite currently does this:

        uint8_t oldSREG = SREG;
 19e:   9f b7        in  r25, 0x3f   ; 63
                cli();
 1a0:   f8 94        cli
        *out &= ~bit;
 1a2:   8c 91        ld  r24, X
 1a4:   30 95        com r19
 1a6:   83 23        and r24, r19
 1a8:   8c 93        st  X, r24
        SREG = oldSREG;
 1aa:   9f bf        out 0x3f, r25   ; 63
 1ac:   08 95        ret

By using assembler you could rejig it to turn interrupts on an instruction earlier, eg. something like:

 19e:   9f b7        in  r25, 0x3f   ; 63  // save SREG
 1a0:   f8 94        cli   // turn interrupts off
 1a2:   8c 91        ld  r24, X
 1a4:   30 95        com r19
 1a6:   83 23        and r24, r19
 1aa:   9f bf        out 0x3f, r25   ; 63  // restore SREG
 1a8:   8c 93        st  X, r24  // do one final thing which won't be interrupted
 1ac:   08 95        ret

Excellent questions! This answers the second question (built for an Uno; may need an LED on pin 5)...

static volatile uint8_t flag;

ISR( PCINT2_vect )
{
  flag = 1;
}

void setup( void )
{
  uint8_t t;
  uint8_t s;

  Serial.begin( 250000 );

  // pin 5, PD5, PCINT21
  DDRD |= _BV( DDD5 );
  
  // Enable pin change interrupt on pin 5
  PCICR |= _BV( PCIE2 );
  PCMSK2 |= _BV( PCINT21 );

  // Force the compiler to assign the value here
  asm volatile
  (
    "ldi  %0, 0x02"      "\n\t" 
    :
      "=r" ( t )
    :
  );

  // Trigger pin change interrupt on pin 5
  s = SREG;
  cli();
  PORTD |= _BV( PORTD5 );
  asm volatile("nop\n\t" ::); 
  asm volatile("nop\n\t" ::); 
  PORTD &= ~ _BV( PORTD5 );
  SREG = s;

  asm volatile("nop\n\t" ::);  // Remove me to see a one!

  // Single machine instruction
  flag = t;

  Serial.println( flag, DEC );
}

void loop( void )
{
}

OK, good. So it looks like any attempt to actual turn(ing) interrupts on is followed by the processor executing the next instruction regardless.

And in this case:

  SREG = s;
  sei();  
//  asm volatile("nop\n\t" ::);  // Remove me to see a one!

I see “2” printed which means that the sei() did not prevent the assignment, because interrupts were already on.

The answer to the second question is … It makes no difference how interrupts are enabled. The next instruction executes. The answer to the first question is … Only the single next instruction executes. Nice.

Presumably, the net result of this is to waste two machine cycles…

  SREG = s;
  cli();

And this would allow any pending interrupts to fire…

  SREG = s;
  asm volatile("nop\n\t" ::);  // Remove me to see a one!
  cli();

[quote author=Coding Badly link=topic=90836.msg682339#msg682339 date=1328669590] The answer to the first question is ... Only the single next instruction executes. Nice. [/quote]

More specifically, SEI will cause the next instruction to be executed unconditionally, if and only if that caused interrupts to be enabled (from a disabled state).

I can't see any documentation on this, and a Google search already puts this thread on page 1 (of Google).

Reading between the lines a bit, the SEI instruction is documented to move 1 to the "I" bit in the status register.

I presume now that the behaviour we have observed is triggered internally in the chip by the toggling of the "I" bit from 0 to 1, and not any other condition (like, exactly how that bit got toggled). Which makes sense.

That actually fits in nicely with the statement on page 14 of the 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.

Since the RETI instruction sets the "I" flag (thus re-enabling interrupts) then it follows that, as in the other cases we discussed, the next instruction will be executed.

So the general rule would appear to be:

If the "interrupt enable" flag in the SREG register is turned from a 0 to a 1, thus enabling interrupts which were previously disabled (however that happens), then the processor will always execute the next instruction after the one that caused that to happen.

I presume now that the behaviour we have observed is triggered internally in the chip by the toggling of the "I" bit from 0 to 1, and not any other condition (like, exactly how that bit got toggled). Which makes sense.

Yeah, I expect that this is more of a consequence of the hardware implementation more than a "feature." The AVR has a 1-instruction pipeline, of sorts, and I bet that it checks for interrupts as instructions are loaded INTO the pipeline. So when you change the bit, the instruction that's already in the pipeline always finishes executing.

That makes sense. By structuring the tests in the state machine internally in a certain way, they get this “feature”. Not that you would want it any other way.

So it would be something like:

while <current instruction is executing>
if <interrupt flag is set>
  {
  check for interrupts
  }
if <interrupt was found>
  {
  load interrupt vector into pipeline
  }
else
  {
  load next instruction into pipeline
  }

[quote author=Nick Gammon link=topic=90836.msg682345#msg682345 date=1328670367] So the general rule would appear to be:

If the "interrupt enable" flag in the SREG register is turned from a 0 to a 1, thus enabling interrupts which were previously disabled (however that happens), then the processor will always execute the next instruction after the one that caused that to happen.

[/quote]

If I have followed this correctly, the instruction that caused this to happen could be in an interrupt. In that case referring to the 'next instruction after the one that caused it to happen' is misleading. The 'next' instruction in this case is the one indicated by the program counter after interrupts have been enabled. That might not be one immediately after the instruction that caused them to be enabled - if you see what I mean.

I meant, the next instruction "that would be executed". So in the case you mention, I presume the RETI instruction, the "next instruction" is not physically located after the RETI instruction, but the one (in the main code) that is executed once the RETI pops the program counter off the stack.