arm - leyendo una variable volátil de 64 bits en cortex-m3
interrupt atomic (1)
La atomicidad no está garantizada en LDRD según el manual de referencia ARMv7m. (A3.5.1)
The only ARMv7-M explicit accesses made by the ARM processor which exhibit single-copy atomicity are:
• All byte transactions
• All halfword transactions to 16-bit aligned locations
• All word transactions to 32-bit aligned locations
LDM, LDC, LDRD, STM, STC, STRD, PUSH and POP operations are seen to be a sequence of 32-bit
transactions aligned to 32 bits. Each of these 32-bit transactions are guaranteed to exhibit single-copy
atomicity. Sub-sequences of two or more 32-bit transactions from the sequence also do not exhibit
single-copy atomicity
Lo que puedes hacer es usar un byte para indicar al ISR que lo estás leyendo.
non_isr(){
do{
flag = 1
foo = doubleword
while(flag > 1)
flag = 0
}
isr(){
if(flag == 1)
flag++;
doubleword = foo
}
Fuente (se requiere iniciar sesión): http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0403e.b/index.html
No es necesario iniciar sesión: http://www.telecom.uff.br/~marcos/uP/ARMv7_Ref.pdf
Tengo una variable entera de 64 bits en un controlador ARM Cortex-M3 de 32 bits (STM32L1), que puede ser modificado asincrónicamente por un controlador de interrupción.
volatile uint64_t v;
void some_interrupt_handler() {
v = v + something;
}
Obviamente, necesito una forma de acceder a ella de una manera que evite obtener valores inconsistentes y actualizados a medias.
Aquí está el primer intento
static inline uint64_t read_volatile_uint64(volatile uint64_t *x) {
uint64_t y;
__disable_irq();
y = *x;
__enable_irq();
return y;
}
Las funciones en línea de __disable_irq()
y __enable_irq()
tienen un desafortunado efecto secundario, forzando una barrera de memoria en el compilador, así que he tratado de encontrar algo más refinado
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;
}
Todavía desactiva las interrupciones, lo cual no es deseable, así que me pregunto si hay alguna manera de hacerlo sin recurrir a cpsid
. La guía definitiva de los procesadores ARM Cortex-M3 y Cortex-M4, tercera edición de Joseph Yiu, dice:
Si llega una solicitud de interrupción cuando el procesador está ejecutando una instrucción de ciclos múltiples, como una división de enteros, la instrucción podría abandonarse y reiniciarse después de que el manejador de interrupciones finalice. Este comportamiento también se aplica para cargar instrucciones de doble palabra (LDRD) y almacenar palabras dobles (STRD).
¿Significa que estaré bien simplemente escribiendo esto?
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;
}
(Usando "=&r"
para trabajar con ARM errata 602117)
¿Hay alguna biblioteca o función incorporada que haga lo mismo de forma portátil? He intentado atomic_load()
en stdatomic.h
, pero falla con undefined reference to ''__atomic_load_8''
.