Evita la lectura o escritura de memoria varias veces al asignar expresiones de campos de bits volátiles.
Quiero usar una estructura de campo de bits volátil para configurar un registro de hardware como en el siguiente código:
union foo {
uint32_t value;
struct {
uint32_t x : 1;
uint32_t y : 3;
uint32_t z : 28;
};
};
union foo f = {0};
int main()
{
volatile union foo *f_ptr = &f;
*f_ptr = (union foo) {
.x = 1,
.y = 7,
.z = 10,
};
return 0;
}
Sin embargo, el compilador generará instrucciones STR
y LDR
para acceder al registro hardware varias veces. Esto es un problema porque hará que el hardware funcione de inmediato cuando se escriba en el registro.
main:
movw r3, #:lower16:.LANCHOR0
movs r0, #0
movt r3, #:upper16:.LANCHOR0
ldr r2, [r3]
orr r2, r2, #1
str r2, [r3]
ldr r2, [r3]
orr r2, r2, #14
str r2, [r3]
ldr r2, [r3]
and r2, r2, #15
orr r2, r2, #160
str r2, [r3]
bx lr
.size main, .-main
.global f
.bss
.align 2
Mi versión de gcc es: arm-linux-gnueabi-gcc (Linaro GCC 4.9-2017.01) 4.9.4
y se ha construido con la optimización -O2
.
He intentado resolver este problema usando una variable local:
union foo {
uint32_t value;
struct {
uint32_t x : 1;
uint32_t y : 3;
uint32_t z : 28;
};
};
union foo f = {0};
int main()
{
volatile union foo *f_ptr = &f;
union foo tmp;
tmp = (union foo) {
.x = 1,
.y = 7,
.z = 10,
};
*f_ptr = tmp;
return 0;
}
Bien, ahora no se realizan múltiples instrucciones STR
al registro de hardware:
main:
movs r1, #10
movs r2, #15
movw r3, #:lower16:.LANCHOR0
bfi r2, r1, #4, #28
movt r3, #:upper16:.LANCHOR0
movs r0, #0
str r2, [r3]
bx lr
.size main, .-main
.global f
.bss
.align 2
Sin embargo, aún pienso que no es una buena idea utilizar una variable local debido a la limitación del tamaño binario en sistemas embebidos.
¿Hay alguna otra forma de solucionar este problema sin utilizar una variable local?
davy.ai
Una posible solución es utilizar una variable temporal del mismo tipo que la estructura de bits volátiles y luego asignarla a la variable volátil. La variable temporal se puede declarar como
register
para sugerir al compilador que la mantenga en un registro en lugar de en la memoria, y también para indicar que su valor puede cambiar inesperadamente.Con este enfoque, el compilador puede optimizar el código para utilizar solo una instrucción
STR
para actualizar el registro de hardware.