I have a 64 bit integer variable on a 32 bit Cortex-M3 ARM controller (STM32L1), which can be modified asynchronously by an interrupt handler.
volatile uint64_t v;
void some_interrupt_handler() {
v = v + something;
}
Obviously, I need a way to access it in a way that prevents getting inconsistent, halfway updated values.
Here is the first attempt
static inline uint64_t read_volatile_uint64(volatile uint64_t *x) {
uint64_t y;
__disable_irq();
y = *x;
__enable_irq();
return y;
}
The CMSIS inline functions __disable_irq()
and __enable_irq()
have an unfortunate side effect, forcing a memory barrier on the compiler, so I've tried to come up with something more fine-grained
static inline uint64_t read_volatile_uint64(volatile uint64_t *x) {
uint64_t y;
asm ( "cpsid i\n"
"ldrd %[value], %[addr]\n"
"cpsie i\n"
: [value]"=r"(y) : [addr]"m"(*x));
return y;
}
It still disables interrupts, which is not desirable, so I'm wondering if there's a way doing it without resorting to cpsid
. The Definitive Guide to
ARM Cortex-M3 and Cortex-M4 Processors, Third Edition by Joseph Yiu says
If an interrupt request arrives when the processor is executing a multiple cycle instruction, such as an integer divide, the instruction could be abandoned and restarted after the interrupt handler completes. This behavior also applies to load double-word (LDRD) and store double-word (STRD) instructions.
Does it mean that I'll be fine by simply writing this?
static inline uint64_t read_volatile_uint64(volatile uint64_t *x) {
uint64_t y;
asm ( "ldrd %[value], %[addr]\n"
: [value]"=&r"(y) : [addr]"m"(*x));
return y;
}
(Using "=&r"
to work around ARM errata 602117)
Is there some library or builtin function that does the same portably? I've tried atomic_load()
in stdatomic.h
, but it fails with undefined reference to '__atomic_load_8'
.