es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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?

Tags:  , , , ,

Answer

  1. Avatar for 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.

    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;
        register union foo tmp;
        tmp.x = 1;
        tmp.y = 7;
        tmp.z = 10;
        *f_ptr = tmp;
        return 0;
    }
    

    Con este enfoque, el compilador puede optimizar el código para utilizar solo una instrucción STR para actualizar el registro de hardware.

    main:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
        movw    r3, #:lower16:.LANCHOR0
        movs    r0, #0
        movt    r3, #:upper16:.LANCHOR0
        movs    r2, #10
        orr r2, r2, #112
        str r2, [r3]
        bx  lr
        .size   main, .-main
        .global f
        .bss
        .align  2
    

Comments are closed.