[C AVR] the global interupt enable function - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: September 09, 2018, 11:49:15 am
content: we are normally told to use CLI() and SEI() do control global interrupts on an AVR, I think because of timing issues? would the following be acceptable?Code:static inline void interrupts_global_enable(){SREG = SREG | 0b10000000;}or should i just do:Code:static inline void interrupts_global_enable(){SEI();}

author: hans
author_rank: Super Contributor
timestamp: September 09, 2018, 11:56:55 am
content: Doesn't matter. SEI is an acronym for the same bitmask you wrote.In terms of clarity, every (AVR) programmer can lookup what the "SEI" macro/instruction is, while for SREG I would need to download a 300-400 page PDF datasheet, find the page that lists SREG register content (not just referencing the name), and figure out what the bitmask is doing.You sometimes need to disable interrupts to ensure operations on e.g. shared variables are atomic.

author: Simon
author_rank: Unknown
timestamp: September 09, 2018, 12:05:46 pm
content: Yes I know what I am saying is will my code work or are there time constraints that I'd break. If i just reference SEI() / CLI() then it is fairly quick to find out what is happening.

author: hans
author_rank: Super Contributor
timestamp: September 09, 2018, 02:59:32 pm
content: SEI/CLI are an acronym for a BSET/BCLR SREG instruction. SEI/CLR are absorbed inside the instruction set, but they are not unique CPU instructions as such. If you look up the opcode patterns you'll find that they just do the exact same operation as you've described.So in terms of output code you could get the same results. However I would always use the SEI/CLR macro's, as they are most predictable to use. Your bitmask operation is subject to the optimizations effort of the compiler, which in debugging may be set very low.Whether this will fix your program timing problems (i.e. missing deadlines) with the inclusion of these programs is impossible to say for sure without lots of info.. There is a whole branch of theoretical computer system that performs analysis for real time systems such that it can be proven that a program will always meet it's deadline given X processors, Y tasks, Z execution times, A dependencies, B buffer sizes, etc. It still remains mostly an academic exercise, though.I just mean to say this is not a problem that a few (disabling/enabling) interrupts will fix, nor can be explained in 1 sentence. People often do "best effort" engineering and lots of testing/verification instead.In practice, you might see disabled interrupts temporarily if there is a critical section. This is perhaps when you:- Atomic operations; you want exclusive access to a variable in your program, especially needed if your interrupt also uses this variable.- You may want exclusive access to a peripheral, and without a RTOS/semaphores you could disable interrupts instead..- There is a piece of code that is timing sensitive. For example: if you want to send a bitstream to a WS2812 led strip. Most AVR drivers I've seen use assembly for that, with carefully added NOP instructions to get the timing right. If an interrupt needs to be served during that assembly routine, the bit transmission is screwed up.You may notice that the first 2 are actually countermeasures to avoid that timing behaviour will influence the functional behaviour of a program. By "functional behaviour" I mean reordering of operations. Something to always look out for.On the other hand note that disabling interrupts does in fact mean that you're screwing up the timing of interrupt routines. Those may also have deadlines before they need to be served, otherwise a hardware overflow/underflow may occur. For example: if you have a serial UART receive interrupt, it only has a limited FIFO (often only 1 byte). Obviously that byte needs to be read before the next is received, otherwise the program won't be able to keep up with the serial communications. Given a certain baudrate you can calculate how long that time is.Since UART is an asynchronous protocol, data can be received at any time. In that case you must also make sure that the longest critical section is sufficiently short that you won't be too late to receive the byte from the hardware.In practice people thereby only write the shortest interrupt and critical sections possible.

author: richardman
author_rank: Frequent Contributor
timestamp: September 17, 2018, 07:22:34 pm
content: The short answer is SEI/CLI are the actual instructions needed, and any additional wrappers you add on top, may or may not be optimized away by a compiler.So first, the inline function calls may not be inlined. If it doesn't, then a function call is one CALL and one RET overhead.Second, the compiler may do "stupid things" with respect to the SREG operations, and generate longer code. A compiler SHOULD do the right thing, but it doesn't have to. So now you may end up with 2-3 instructions instead of one.So generally speaking, just use SEI/CLI. You can further #define them to give them meaningful names without introducing potential overhead.

author: Simon
author_rank: Unknown
timestamp: September 17, 2018, 07:25:55 pm
content: Quote from: richardman on September 17, 2018, 07:22:34 pmYes that was what I had done before i was sold on inline functions, sounds I should make an exception there.

author: maginnovision
author_rank: Super Contributor
timestamp: September 17, 2018, 09:25:28 pm
content: Quote from: Simon on September 17, 2018, 07:25:55 pmI'm not sure it matters. I think everything goes back to sei and cli instructions anyway, at least every compiler I've seen. It is usually easier to read though which is a big plus.

author: TassiloH
author_rank: Regular Contributor
timestamp: September 18, 2018, 08:52:06 am
content: I think there is one important difference between accessing SREG in C code and usind the sei/cli macros:The definition of sei/cli in the macro using inline assembly also sets a memory barrier, which forces the compiler not to re-order any memory accesses across the instruction. This is important because for optimization the compiler may re-order instructions freely as long as the result remains the same - and the compiler has no concept of interrupts etc.This does not make any difference when trying to ensure atomic access to volatile variables (i.e. things that are accessed during interrupt and in regular code), because SREG is also volatile and so no re-ordering may take place. But what can happen is that you do this:Code:// be x something volatile also used in an interrupt routinetmp = (lengthy, time consuming computation);SREG = SREG &= ~0b10000000;x = tmp;SREG = SREG | 0b10000000;and the lengthy calculation is swapped with the SREG access, so suddenly the interrupts get disabled for much longer than intended.