I wrote a C++ version to the the C headers avr/interrupt.h and util/atomic.h from AVR Libc. Nothing especial here, I'm using RAII to implement an atomic scope to execute a block of code instead the clever macro ATOMIC_BLOCK that uses a for loop and the GCC attribute cleanup. The macro functions cli/sei() were replaced by always_inline functions defined inside a namespace:
using namespace avr::interrupt;
on(); //enable global interrupts
off(); //disable global interrupts
{ //scope with code executed without being disturbed by interrupts
atomic sa;
//code
} //global interrupts are always enabled at the end
{
atomic sa{restore};
//code
} //global interrupts are enabled at the end if they were enabled before
There is also a support to help the programmer to avoid reordering code when timing is a critical factor. Let's consider the code below:
using namespace avr::interrupt;
unsigned int ivar;
void f(unsigned int val) {
val = 65535U / val;
atomic sa;
ivar = val;
}
Using avr-gcc 10.2 with -Os we have the following:
movw r22, r24
cli
ldi r24, 0xFF ; 255
ldi r25, 0xFF ; 255
rcall .+18 ; 0x4e <__udivmodhi4>
sts 0x0061, r23 ; 0x800061 <ivar+0x1>
sts 0x0060, r22 ; 0x800060 <ivar>
sei
ret
The division call is inside the critical region. We can achieve a better result helping the compiler:
using namespace avr::interrupt;
unsigned int ivar;
void f(unsigned int val) {
val = 65535U / val;
atomic sa(on_at_the_end, val);
ivar = val;
}
This generates:
movw r22, r24
ldi r24, 0xFF ; 255
ldi r25, 0xFF ; 255
rcall .+20 ; 0x4e <__udivmodhi4>
cli
sts 0x0061, r23 ; 0x800061 <ivar+0x1>
sts 0x0060, r22 ; 0x800060 <ivar>
sei
ret
Note that the call to __udivmodhi4 is now outside the region delimited by the instructions cli and sei.