• [^] # Re: Idée pour les autres MCU

    Posté par . En réponse au journal Un utilitaire pour formater la sortie de avr-objdump. Évalué à 3.

    C'est fou ce qu'on peut apprendre des macros de GCC!

    Bon, ceci sort du cadre de mon journal mais je voulais aussi afficher des statistiques/renseignements sur le projet, par exemple le nom du fichier d'entête, l'architecture, les fonctionnalités additionnelles, l'ordre des octets, etc. Après avoir "chipoté" quelques heures de plus, voici ce que j'ai fini par utiliser:

    echo -e "#include <avr/io.h>\nvoid main() {}" | sort | \
    avr-gcc -E -dM -Os -DF_CPU=16000000 -mmcu=atmega2560 - | \
    sed -rn \
    -e '/AVR_IOXXX/s/.*\s+"([^"]+)"$/MCU header: 1円/p' \
    -e '/__AVR_LIBC_VERSION_STRING__/s/.*\s+"([0-9\-\.]+)"$/AVR libc: version 1円/p' \
    -e '/__AVR_DEVICE_NAME__/s/.*\s+(\w+)$/device name: 1円/p' \
    -e '/__AVR_ARCH__/s/.*\s+(\w+)$/architecture: avr1円/p' \
    -e '/SIGNATURE/{N;N;s/#define\s+SIG\w+//g;s/\n/,/g;s/^/signature:/gp}' \
    -e '/RAMSIZE/s/.*\s+([0-9]+)$/RAM size: 1円 bytes/gp' \
    -e '/E2SIZE/s/.*\s+([0-9]+)$/EEPROM size: 1円 bytes/gp' \
    -e '/__SIZEOF_(SHORT|INT|LONG|FLOAT|POINTER|SIZE_T)__/s/.*__SIZEOF_(\w+)__\s+([0-9]+)$/\L1円 size: 2円 bytes/gp' \
    -e '/__BYTE_ORDER__/s/.*__ORDER_(\w+)_ENDIAN__/byte ordering: \L1円 endian/gp' \
    -e '/_AVR_HAVE_(MUL|MOVW|E?LPMX?|E?JMP_E?CALL|RAMP[DXYZ])/{s/.*HAVE_(\w+)__.*$/1円/g;H}' \
    -e '${x;s/\n|_/ /g;s/^/features:/gp}'

    C'est imbitable, hein? C'est normal. Comment ça fonctionne? avr-gcc -E invoque le préprocesseur sans compiler quoi que ce soit. La source est une simple fonction main() avec le fichier d'en-tête minimal, io.h. Dans tout ce bazar, sort est essentiel! Ensuite, vient l'analyse:

    • la macro __AVR_IOXXX__ contient le nom du fichier d'en-tête spécifique au processeur,
    • décortiquer __AVR_LIBC_VERSION_STRING__ et ne prendre que la chaîne correspondant à la version de avr-libc,
    • faire pareil avec __AVR_DEVICE_NAME__ et __AVR_ARCH__,
    • la signature est répartie sur 3 lignes, du style #define SIGNATURE_[012] 0x[0-9]+; on jumelle ces trois lignes en ne retenant que les valeurs hexadécimales,
    • les macros RAMSIZE et E2SIZE sont décortiquées comme __AVR_DEVICE_NAME__,
    • extraire les tailles pour des types intéressants (short, int, float, etc.) en rappelant le nom du type, gentiment converti en minuscules (\L)
    • faire de même pour l'agencement avec __BYTE_ORDER__, dont on ne retient que le terme du milieu (peut être "LITTLE", "BIG" ou "PDP")
    • extraire les fonctionnalités __AVR_HAVE_XXX__ et les joindre sur une seule ligne.

    Toutes ces transformations sont aussi préfixées par une étiquette. Voici ce que ça donne, par exemple pour un ATmega2560:

    architecture: avr6
    device name: atmega2560
    MCU header: iomxx0_1.h
    AVR libc: version 2.1.0
    byte ordering: little endian
    signature: 0x1E, 0x98, 0x01
    float size: 4 bytes
    int size: 2 bytes
    long size: 4 bytes
    pointer size: 2 bytes
    short size: 2 bytes
    size_t size: 2 bytes
    features: ELPM ELPMX JMP CALL1 LPMX MOVW MUL RAMPZ

    Dans un autre fil, j'ai avoué que je sed souvent à la difficulté. Ben voilà.

    La documentation se trouve ici: https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html

    1 Noter que les valeurs JMP et CALL sont liées par un underscore avec la macro _AVR_HAVE_JMP_CALL__. J'ai simplement remplacé dans la foulée ce dernier par un espace, comme le marqueur de fin de ligne.