Treballant amb punt flotant

Sempre s’ha dit que cal evitar l’ús de variables en punt flotant (float o double) en sistemes encastats. Això prové dels temps en que els microcontroladors disponibles no tenien cap unitat hardware de punt flotant i aquestes operacions s’havien de fer per software i això penalitzava moltíssim el rendiment.

Això encara és aplicable per la majoria de casos, tot i que els nous microcontroladors basats en Cortex-M4 o Cortex-M7 poden portar unitats de punt flotant. Anem a veure amb detall una mica com treballar amb les eines i el codi perquè tot funcioni correctament.

Treballarem amb dos exemples molt senzills, que son les funcions mulf i muld, que multipliquen dos valors de tipus float i double respectivament. Cal recordar que el tipus float correspon a un tipus de punt flotant de 32 bits conegut com single precision seguint l’standard IEEE 754. Double correspon a un tipus de 64 bits conegut com double precision del mateix standard.

/* mulf.c */
float mulf(float a, float b) {
  return a*b;
}
/* muld.c */
double muld(double a, double b) {
  return a*b;
}

Les biblioteques estàndard de C incorporen funcions per operar amb aquests tipus, i son les que es fan servir per defecte pel compilador si no li donem ordres especials.

Així doncs, si compilem el fitxer mulf.c amb la següent comanda

arm-none-eabi-gcc mulf.c -o- -S -mthumb -mcpu=cortex-m4

ens mostrarà per pantalla el codi en assemblador que genera el compilador. Cal fixar-se que els flags que li passem al compilador son només que el microcontrolador és un Cortex-M4.

        push    {r7, lr}
        sub     sp, sp, #8
        add     r7, sp, #0
        str     r0, [r7, #4]    @ float
        str     r1, [r7]        @ float
        ldr     r1, [r7]        @ float
        ldr     r0, [r7, #4]    @ float
        bl      __aeabi_fmul
        mov     r3, r0
        mov     r0, r3
        adds    r7, r7, #8
        mov     sp, r7
        @ sp needed
        pop     {r7, pc}

Aquí el que es veu és que es preparen uns registres i es crida una funció anomenada __eabi_fmul que és la funció de la biblioteca encarregada de fer les multiplicacions per software.

Si ara especifiquem que el cortex-M4 te el mòdul d’operacions en punt flotant amb la segúent comanda

arm-none-eabi-gcc muld.c -o- -S -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16

El resultat serà el següent, on ja es veu que es fan servir instruccions de punt flotant (vstr, vldr, vmul, vmov, etc.)

        push    {r7}
        sub     sp, sp, #12
        add     r7, sp, #0
        vstr.32 s0, [r7, #4]
        vstr.32 s1, [r7]
        vldr.32 s14, [r7, #4]
        vldr.32 s15, [r7]
        vmul.f32        s15, s14, s15
        vmov.f32        s0, s15
        adds    r7, r7, #12
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr

En aquest cas els flags del compilador indiquen quin mòdul FPU te el nostre microcontrolador (fpv4-sp-d16): fp versió 4, single precision i 16 registres).

Això seria pel cas de la funció que treballa amb precisió simple, si ara fem el mateix per la funció que treballa amb dobles, fent servir els mateixos flags

arm-none-eabi-gcc muld.c -o- -S -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
        push    {r7, lr}
        sub     sp, sp, #16
        add     r7, sp, #0
        vstr.64 d0, [r7, #8]
        vstr.64 d1, [r7]
        ldrd    r2, [r7]
        ldrd    r0, [r7, #8]
        bl      __aeabi_dmul
        mov     r2, r0
        mov     r3, r1
        vmov    d7, r2, r3
        vmov.f32        s0, s14
        vmov.f32        s1, s15
        adds    r7, r7, #16
        mov     sp, r7
        @ sp needed
        pop     {r7, pc}

Veiem que altre cop es fa l’operació per software enlloc de fer-la via les instruccions de punt flotant. Per què passa això? Doncs perquè l’arquitectura Cortex-M4 només permet FPUs de precisió simple i no pot treballar amb precisió doble i així li hem especificat al compilador. Per tant el compilador fa les crides a la biblioteca software pertinent (__eabi_dmul).

Per tant, compte amb treballar amb doubles i arquitectures Cortex-M4 o inferiors! Cal tenir en compte que algunes operacions en C que fan servir constants poden acabar en un tipus double si no vigilem.

Si ara fem la prova amb la mateixa funció però usant els flags per un Cortex-M7

arm-none-eabi-gcc muld.c -o- -S -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16

En aquest cas, els flags indiquen que el processador és un Cortex-M7 i la unitat de punt flotant és la versió 5 (Tal com indica el ARM Cortex-M7 Processor Technical Reference Manual (pàg 8-2)).

Amb aquests flags, el codi assemblador que es genera és el que es veu al llistat següent

        push    {r7}
        sub     sp, sp, #20
        add     r7, sp, #0
        vstr.64 d0, [r7, #8]
        vstr.64 d1, [r7]
        vldr.64 d6, [r7, #8]
        vldr.64 d7, [r7]
        vmul.f64        d7, d6, d7
        vmov.f64        d0, d7
        adds    r7, r7, #20
        mov     sp, r7
        @ sp needed
        ldr     r7, [sp], #4
        bx      lr

Aquí es veu que es torna a fer servir registres i instruccions pròpies del punt flotant (vstr, vldr, vmul, vmov) amb el suffix .f64 que es correspon a la mida del tipus double (64 bits) i que, per tant, les operacions no es fan per una rutina software si no que les executa el mòdul de punt flotant del microcontrolador.

Per tant, podem veure clar que l’ús del tipus double només és recomanat per arquitectures Cortex-M7 i posteriors si no volem tenir una pèrdua de rendiment considerable.

Per últim, no cal espantar ningú, ja que aquest flags els maneguen les eines de cada fabricant segons les característiques dels seus microcontroladors i normalment no ens n’hem de preocupar.

Deixa un comentari

Fill in your details below or click an icon to log in:

WordPress.com Logo

Esteu comentant fent servir el compte WordPress.com. Log Out /  Canvia )

Google photo

Esteu comentant fent servir el compte Google. Log Out /  Canvia )

Twitter picture

Esteu comentant fent servir el compte Twitter. Log Out /  Canvia )

Facebook photo

Esteu comentant fent servir el compte Facebook. Log Out /  Canvia )

S'està connectant a %s

Aquest lloc utilitza Akismet per reduir els comentaris brossa. Apreneu com es processen les dades dels comentaris.