avr-gcc Features: Atomic operations

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

F'up of avr-gcc: New Features?

gchapman wrote:
Atomic operations.

Most up to all of it can be done with inline assembler. Draft for an atomic OR:

#include 
#include 

#ifdef __AVR_XMEGA__
#define __AVR_HAVE_ATOMICS__ 1
#else
#define __AVR_HAVE_ATOMICS__ 0
#endif

#define __BPOS(X)       \
   (X == 1 << 1 ? 1 :   \
    X == 1 << 2 ? 2 :   \
    X == 1 << 3 ? 3 :   \
    X == 1 << 4 ? 4 :   \
    X == 1 << 5 ? 5 :   \
    X == 1 << 6 ? 6 :   \
    X == 1 << 7 ? 7 : 0)
    
static __inline__ __attribute__((__always_inline__))
void __atomic_or (const uint16_t __addr, const uint8_t __mask)
{
    if (__builtin_constant_p (__mask)
        && __builtin_constant_p (__addr)
#if __AVR_SFR_OFFSET__
        && __addr >= __AVR_SFR_OFFSET__
#endif
        && __addr < 0x20 + __AVR_SFR_OFFSET__
        && __mask
        && 0 == (__mask & (__mask-1)))
    {
        __asm__ __volatile__ ("sbi %i0, %1"
                              :: "n" (__addr), "M" (__BPOS (__mask))
                              : "memory");
    }
#if __AVR_HAVE_ATOMICS__
    else
    {
        uint8_t __m = __mask;
        __asm__ __volatile__ ("las %a1, %0"
                              : "+r" (__m)
                              : "z" (__addr)
                              : "memory");
    }
#else
    else if (__builtin_constant_p (__addr)
             && (unsigned) __addr >= __AVR_SFR_OFFSET__
             && (unsigned) __addr < 0x40 + __AVR_SFR_OFFSET__)
    {
        uint8_t __v;
        __asm__ __volatile__ ("in __tmp_reg__,__SREG__ $ cli $ in %0, %i1"
                              : "=r" (__v)
                              : "n" (__addr)
                              : "memory");
        __v |= __mask;
        __asm__ __volatile__ ("out __SREG__,__tmp_reg__ $ out %i0, %1"
                              :: "n" (__addr), "r" (__v)
                              : "memory");
    }
    else
    {
        uint8_t volatile *const __a = (uint8_t volatile*) __addr;
        cli();
        *__a |= __mask;
        sei();
    }
#endif /* __AVR_HAVE_ATOMICS__ */
}

static __inline__ __attribute__((__always_inline__))
void atomic_or (uint8_t volatile *const __addr, const uint8_t __mask)
{
    __atomic_or ((uint16_t) __addr, __mask);
}

void f (void)
{
    atomic_or (&PORTB, 0x10);
}

It's possible to do it by means of a new avr-gcc builtin, of course. It could even detect whether or not a return value is used or not. The above version assumes that the return value (former SFR content) is unused.

I actually started to implement respective builtins but immediately stopped when I noticed that atomics are not a general Xmega feature and information like __AVR_HAVE_ATOMIC__ is not available (the above definition is wrong).

It was not possible to find out what devices actually can atomic and what don't, cf. What silicon supports LAT, LAC, LAS, XCH. The Atmel support doen't know either, they cannot provide a list of what device supports what...

Since then the patch is rotting somewhere in the file systems...

As Binutils support these instructions, it's easy enough to write inline assembler to make them usable from C/C++. Even without Binutils support, the GNU assembler's .marco is mighty enouth to hack it, cf. this.

Or simply us the old-school IRQ on / off scheme. 1 or 2 ticks won't hurt "” and will be inevitable if no atomics are available and everything must be open coded...

The same trouble as with avr-gcc is with binutils: It's agnostic w.r.t atomics and DES, no clue whether the instruction is fine or worth an error, cf. PR15043.

avrfreaks does not support Opera. Profile inactive.