Does cli() postpone or wipe out interrupt requests that have flags?

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

I realize that clearing the global interrupt bit in SREG disables all interrupts. But does it mean interrupt requests indicated by flags will get wiped out or that they will merely be postponed until the global interrupt bit is set? The context is as follows. I need two interrupts on ATtiny85. One interrupt is on timer0 compare match, and the other one handles rising edges on INT0. 

...
    TIMSK = 1<<OCIE0A;                      // interrupt on compare match
    MCUCR |= 1<<ISC01 | 1<<ISC00;           // rising edge interrupt on PB2
    GIMSK |= 1<<INT0;                          // rising edge interrupt on PB2
...

ISR(TIMER0_COMPA_vect) {               // compare match interrupt
    ...
}

ISR(INT0_vect) {                       // PB2 rising edge
    ...
}

I am concerned about the situation when both interrupt requests overlap. Let's say it happens and execution jumps to ISR(INT0_vect). When compare match occurs, the flag bit OCF0A will be set in TIFR, but the global interrupt will be disabled inside the interrupt routine, so nothing will happen. However, when ISR(INT0_vect) is finished and the global interrupt bit is set in SREG, will the micro continue to ISR(TIMER0_COMPA_vect)? Or will it clear OCF0A and simply skip the interrupt for good? The datasheet says the following:

...if one or more interrupt conditions occur while the Global Interrupt Enable bit is cleared, the corresponding Interrupt Flag(s) will be set and remembered until the Global Interrupt Enable bit is set, and will then be executed by order of priority. 

It seems that the compare match interrupt should happen when ISR(INT0_vect) is finished. But I've misread the manual in the past, so I'm not 100% confident I'm reading it right. It would be fantastic if some one could confirm my interpretation.

This topic has a solution.
Last Edited: Sat. Sep 16, 2017 - 03:11 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Postponed. As soon as you re-enable interrupts, they will get serviced in the normal way.
They will follow the normal priority. i.e. INT0 is always serviced before INT1 regardless of which flag occurred first in the disabled period.

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

It's worth noting...

 

  • When using the CLI instruction to disable interrupts, the interrupts will be immediately disabled. No interrupt will be executed after the CLI instruction, even if it occurs simultaneously with the CLI instruction.
  • When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts.
  • 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.

 

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

Perfect! I saw the ISR_NOBLOCK option for interrupt routines to allow nested interrupts, and I started thinking I'd need to use it to have both interrupts processed, but the default behaviour of postponing until the global interrupt enable bit is set sounds safer to me. Thanks for clearing that up.

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

Brian Fairchild wrote:

It's worth noting...

 

  • When using the CLI instruction to disable interrupts, the interrupts will be immediately disabled. No interrupt will be executed after the CLI instruction, even if it occurs simultaneously with the CLI instruction.
  • When using the SEI instruction to enable interrupts, the instruction following SEI will be executed before any pending interrupts.
  • 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.

 

Thanks for the note. I was just wondering what would happen if a function ends with sei() but the program performs cli() right after it jumps back. Since functions truly end with RET, an unwanted interrupt could occur in the meantime.

Last Edited: Sat. Sep 16, 2017 - 05:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

paragliding wrote:
I was just wondering what would happen if a function ends with sei() but the program performs cli() right after it jumps back. Since functions truly end with RET, an unwanted interrupt could occur in the meantime.

I have no idea what you are hammering at.  As these things go, AVR8 has a fairly simple and straightforward interrupt subsystem.  Most of us, for most or nearly all of our apps, will use it in a very vanilla manner: 

-- Set up the AVR, including needed IE flags.

-- If necessary, clear IF bits that might have had a spurious trigger when setting up.  Not uncommon, but usually not a big deal.

-- SEI to enable global interupts.  In many/most apps it won't be touched much after that except for atomic access to data.

-- ISRs fire when the associated event occurs.  Keep them short; a few microseconds usually.  Almost always, no big deal if another event happens.

 

I have no idea why you want to worry about SEI at the end of a function.  I have no idea what you are getting at with CLI after it "jumps back".  Jumps back to where?!?

 

Are you really takling about ISRs?  They end in RETI.

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

 

I have no idea why you want to worry about SEI at the end of a function.  I have no idea what you are getting at with CLI after it "jumps back".  Jumps back to where?!?

 

Are you really takling about ISRs?  They end in RETI.

My apologies. It's only tangentially related to the original question. Here's the context. I had a simple function from before to change the clock divider:
 

void clk_div_1(void) {                  // Set clock divider to 1 (fast operation)
    cli();
    CLKPR = (1<<CLKPCE);
    CLKPR = 0x00;                       // div 1
    sei();
}

There was sei() because I always needed interrupts to be re-enabled. But now I may or may not need interrupts after the function is completed. The solution is easy: delete sei() and control it after the function returns. But for a moment I got curious what would happen if cli() was used immediately after the return (with sei being in the last line of the function). The C code makes it look like sei() is the last instruction before cli(), but it's only an illusion.

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

If cli() were used without the matching sei() in that function, any time you call that function, interrupts would forever and globally be disabled until an sei() is executed sometime in the future.

 

If you have an sei() after the function exits, then that will re-enable interrupts. If every execution path results in an sei() after exit, then you don't need it inside the function. But, then, why have the  cli() at the beginning? Interrupts ARE disabled until the first sei() is executed. If this happens early in the program before an sei() is executed, then you don't need the cli().

 

As  far as the sei() seemingly followed by a cli(), all you have is the  return from the function, with what ever happens on the stack to accomplish that. Functionally, you probably could have an interrupt that hits during that process. If that bothers you, then leave out the sei() at the end of the  function.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Sat. Sep 16, 2017 - 06:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What's considered good practice for functions that perform cli()? Leaving the global interrupt enable bit cleared or restoring SREG prior to returning from the function?

Edit: for code reusability.

 

Last Edited: Sat. Sep 16, 2017 - 06:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Personally, I tend to do those things directly in the main code flow, and not hidden in a function. Then, I have total control of when interrupts are enabled and disabled.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void clk_div_1(void) {                  // Set clock divider to 1 (fast operation)
    uint8_t sreg = SREG;                // save current state
    cli();                              // disable interrupts
    CLKPR = (1<<CLKPCE);
    CLKPR = 0x00;                       // div 1
    SREG = sreg;                        // restore state
}

You can use macros from the "avr/atomic.h" header for access avoiding interrupts.   Or the "avr/power.h" which is specifically for the CLKPR setting.

But quite honestly,  it is not difficult to just write the statements manually.

 

David.

Last Edited: Sat. Sep 16, 2017 - 06:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

david.prentice wrote:

void clk_div_1(void) {                  // Set clock divider to 1 (fast operation)
    uint8_t sreg = SREG;                // save current state
    cli();                              // disable interrupts
    CLKPR = (1<<CLKPCE);
    CLKPR = 0x00;                       // div 1
    SREG = sreg;                        // restore state
}

You can use macros from the "avr/atomic.h" header for access avoiding interrupts.   Or the "avr/power.h" which is specifically for the CLKPR setting.

But quite honestly,  it is not difficult to just write the statements manually.

 

David.

Interesting. Looks like I've been re-inventing the wheel. I suspected there was something for CLKPR, but I had no idea about atomic macros. Live and learn. Thanks for pointing me in the right direction.

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

paragliding wrote:
What's considered good practice for functions that perform cli()?

??? again.  Why are you peppering your code with CLI?

 

If you do need to ensure that interrupts are turned off, such as with your example function, then under what conditions would you decide to >>not<< re-enable global interrupts?

 

In the most common case with "generic" code that doesn't know whether global interrupts are enabled or not is to restore the I bit to the state it was in when the critical path was approached.

 

Note that depending on your toolchain and optimization settings and the phase of the moon, your CLKPR manipulation may not meet the 4-cycle limitation.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.