Journal HWA : accéder au matériel autrement

14
27
juin
2012

L'objectif de HWA est d'aider à l'écriture de code C de bas niveau compréhensible sans commentaire, le plus portable possible et sans pénalisation en occupation mémoire ou en vitesse d'exécution.

En gros, HWA fournit trois niveaux d'accès au matériel :

  • le plus bas, classique, au niveau des registres ou bits de registres des contrôleurs (io, timer-counter, uart, adc…) ;
  • un intermédiaire qui permet de spécifier des contraintes sur un contrôleur matériel pour en obtenir le fonctionnement souhaité, HWA se chargeant de reconstituer les valeurs à inscrire dans les registres ;
  • un niveau d'abstraction supérieur qui permet de spécifier la configuration d'un contrôleur « virtuel » (sortie PWM par exemple), HWA se chargeant de programmer le contrôleur réel sous-jacent (timer-counter).

HWA propose des fonctions synchrones (à effet immédiat) et asynchrones (à effet différé, permettant l'optimisation des accès aux registres).

Le premier jet est sur GitHub: http://github.com/duparq/hwa

Pas de documentation mais 4 fichiers de démonstration commentés (en anglais) illustrant graduellement les possibilités de HWA à partir d'un projet simple tiré du site d'AVR-Libc. Le code a été compilé avec avr-gcc 4.7.0 et testé sur un Atmel ATtiny44.

  • # code code fait la poule

    Posté par (page perso) . Évalué à 10. Dernière modification le 27/06/12 à 23:54.

    Pour moi le commentaire c'est important dans du code, voir presque aussi important que le code lui même…

    et franchement, ce genre de commentaire :

    #1 "hw_func(DEV, reg1, 42);"
    hw_func(DEV, reg1, 42);
    one(dev1,0x1100, reg1,8,0x00,0xFF,8,0, 42);
    
    

    C'est juste inutile, ok dac, c'est cette fonction, mais ça si on fait du C, on le sait… par contre rien sur ce qu'elle fait, quand elle sert, c'est quoi les paramètres d'entrée, de sortie…

    Bref, c'est illisible le mois suivant et intransmissible.

    La réalité, c'est ce qui continue d'exister quand on cesse d'y croire - Philip K. Dick

    • [^] # Re: code code fait la poule

      Posté par (page perso) . Évalué à 1.

      Mais vous faites les poubelles !

      Le fichier que vous avez regardé, foo.c, ne sert qu'à vérifier le comportement des macros de HWA avec le script test-foo.sh. Si test-foo renvoie 0, c'est que les macros sont OK. Sinon, c'est que quelque chose ne fonctionne pas comme prévu et les couacs sont visibles dans foo.cp.c. C'est utile pour s'assurer rapidement qu'une modification des macros n'entraîne pas d'effet indésirable.

      En attendant qu'il y ait une doc, seuls les fichiers demo-01.c ou plutôt demo-02.c à demo-04.c sont représentatifs de ce que vise HWA. Tous ces fichiers aboutissent au même code machine mais il me semble que demo-04.c est clairement plus lisible et portable que le demo.c du projet qui sert de base.

      « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

      • [^] # Re: code code fait la poule

        Posté par . Évalué à 4.

        Il a juste pris le premier fichier .c qui apparaît dans ton lien. Mais au delà de ce détail, ton journal fera évidemment bondir n'importe quel développeur un peu consciencieux: aider à programmer sans écrire de commentaire est une aberration.

        Un exemple tout simple ? J'ai regardé, dans l'ordre demo-04.c puis demo-03.c. J'ai globalement compris ce que faisait le second, grace…. aux commentaires :)

        Ne pas se méprendre: proposer une abstraction pour l'accès au hardware en programmation est tout à fait appréciable. C'est surtout ces petits mots "compréhensible sans commentaire", qui rappellent inévitablement de mauvais souvenirs.

        • [^] # Re: code code fait la poule

          Posté par (page perso) . Évalué à 2.

          Pourquoi Ducros y se décarcasse ?

          Il y a un README et 4 fichiers de démo à consulter dans l'ordre : demo-01.c, demo-02.c, demo-03.c, demo-04.c.

          demo-01.c n'est clairement pas le plus intéressant : il montre juste qu'il est possible de compiler un source écrit pour AVR-Libc avec HWA moyennant la traduction des symboles utilisés. Il sert aussi à mesurer la taille du code produit et à le comparer au code original. Ici, les codes machines produits sont identiques donc les déclarations de registres de HWA sont valables.

          demo-02.c (65 lignes de commentaires en tête) montre comment on accède aux registres et aux bits de registres à la sauce HWA. Ce n'est pas encore transcendant mais il me semble que cela apporte déjà quelque chose. Par exemple, préférez-vous écrire :

          /* Enable timer 1 overflow interrupt. */
          TIMSK = _BV (TOIE1);
          
          

          qui nécessite de savoir que le bit TOIE1 appartient au registre TIMSK (et si ce bit appartient en fait à un autre registre de même taille, vous n'aurez pas de message d'erreur), ou bien :

          hw_write( TOIE1, 1 );
          
          

          qui garantit que le bit TOIE1 sera mis à 1, et ne requiert pas que vous sachiez à quel registre il appartient (donc vous ne pourrez pas faire d'erreur) ?
          Au passage :

          1. le code machine produit a rétréci car l'utilisation des fonctions asynchrones (hwa_*) a permis à HWA d'optimiser les accès aux registres ;
          2. le commentaire /* Enable timer 1 overflow interrupt. */ est faux car ce qui est écrit ne se contente pas de mettre le bit TOIE1 à 1 mais aussi tous les autres à 0… (là, je me marre !)

          Autre exemple :

          hwa_write( WGM1, 0b0011 );
          
          

          Rien n'empêche de créer un symbole pour expliquer 0b0011 (par exemple HW_WGM1_PWM_10BITS_PHASE_CORRECT) mais vu ce que propose encore HWA (voir ci-dessous), il n'est peut-être pas nécessaire de s'attarder là-dessus.
          Aussi, ce qu'on ne voit pas avec cette dernière instruction, c'est que les bits 3 et 2 de WGM1 sont aux positions 4 et 3 d'un registre, et les bits 1 et 0 aux positions 1 et 0 d'un autre registre…

          demo-03.c pousse le concept un cran au dessus : on ne s'occupe plus des valeurs des registres mais on décrit comment on veut faire fonctionner les contrôleurs et HWA se débrouille pour écrire les bonnes valeurs dans les bons registres (en optimisant). Par exemple, je veux que le timer 1 compte et décompte en boucle :

          hwa_timer_set_countmode( hw_timer1, loop_updown );
          
          

          Franchement, vous ajouteriez un commentaire ?

          demo-04.c va au bout du concept : finalement, pourquoi ne pas juste décrire ce qu'on veut faire avec le circuit ? pourquoi exprimer tous les paramètres de fonctionnement du timer alors qu'il serait plus simple (et plus lisible, et plus portable) de dire ce que je veux en faire ? Je veux utiliser le PWM virtuel pwm0a (qui correspond au canal compare-match 'a' du timer-counter 0), sans respect de la phase (compte juqu'à TOP puis repart de 0) et cadencé par l'horloge système divisée par PWM_PSC :

          hwa_config( hw_pwm0a, fast, TOP, PWM_PSC );
          
          

          Si plus tard je veux que mon PWM respecte la phase, je n'ai qu'à changer 'fast' en 'phase_correct' :

          hwa_config( hw_pwm0a, phase_correct, TOP, PWM_PSC );
          
          

          Perso, je n'en vois pas l'intérêt d'un commentaire ici. Mais avec ce type de code, je peux décider de changer de sortie PWM ou même de microcontrôleur sans avoir à rechercher des valeurs pour des bits de registres. Il me semble que c'est intéressant. Et si HWA ne trouve pas le moyen d'obtenir le fonctionnement désiré, il émettra un message d'erreur.

          Alors ? Il n'a pas quelques qualités mon HWA ?

          « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

          • [^] # Re: code code fait la poule

            Posté par . Évalué à 1.

            mais vu ce que propose encore HWA (voir ci-dessous), il n'est peut-être pas nécessaire de s'attarder là-dessus.

            Ca sert a quoi de faire des fichiers de demo si tu ne prends meme pas le temps d'expliquer ce que fait le code?

            Pour ce genre de fichiers d'exemple, on a souvent 3 lignes de commentaires pour une ligne de code, qui expliquent en detail ce qui se passe (on fera jamais ca dans du code en prod evidemment), meme si ca revient a repeter le code en commentaire.

            Franchement, vous ajouteriez un commentaire ?

            Oui, certainement. A la fois dans le fichier d'exemple (voir au dessus) et probablement en prod aussi (au niveau de la fonction, pour expliquer pourquoi).

            Perso, je n'en vois pas l'intérêt d'un commentaire ici.

            Si tu fais un projet juste pour toi, ca va. Mais a partir du moment ou tu veux partager, va falloir aussi penser un peu a ce dont les utilisateurs peuvent avoir besoin…

            Alors ? Il n'a pas quelques qualités mon HWA ?

            Surement, mais comme tu te comportes comme un douchebag avec ce genre de phrases (Pourquoi Ducros y se décarcasse ? et Mais vous faites les poubelles !), tu vas pas attirer grand monde.

            • [^] # Re: code code fait la poule

              Posté par (page perso) . Évalué à 0.

              Ca sert a quoi de faire des fichiers de demo si tu ne prends meme pas le temps d'expliquer ce que fait le code?

              La question était : est-il bien nécessaire de passer du temps à créer des symboles pour des valeurs numériques de configuration si HWA fournit des fonctions permettant de ne pas avoir à manipuler des valeurs de bits. L'important ici n'était pas la signification de cette valeur particulière mais comment on affecte une valeur à un ensemble de bits.

              Franchement, vous ajouteriez un commentaire ?

              Oui, certainement. A la fois dans le fichier d'exemple (voir au dessus) et probablement en prod aussi (au niveau de la fonction, pour expliquer pourquoi).

              Sur les commentaires dans le code, puisque cela semble être une question fondamentale, je considère qu'il est primordial pour la lisibilité d'un programme que les libellés soient correctement choisis pour les fonctions autant que pour les variables. HWA permet de remplacer des constructions à base d'affectations et d'opérateurs logiques qui nécessitent d'être commentées pour ne pas rester opaques par des instructions dont l'effet doit sembler évident à celui qui lit le code. Pour moi, les commentaires sont utiles pour expliquer pourquoi ou comment on fait les choses, pas pour expliquer le fonctionnement d'une fonction. S'il est nécessaire d'expliquer ce que fait une fonction, c'est que son libellé est mal choisi.

              Alors ? Il n'a pas quelques qualités mon HWA ?

              Surement, mais comme tu te comportes comme un douchebag avec ce genre de phrases (Pourquoi Ducros y se décarcasse ? et Mais vous faites les poubelles !), tu vas pas attirer grand monde.

              Je pourrais reprendre : « Ça sert a quoi de faire des fichiers de démo si les gens ne prennent même pas le temps de les lire ».

              Il me semble avoir bien indiqué qu'il n'y a pas de doc mais seulement des fichiers de démo qui essaient de montrer par étapes ce que fournit HWA.

              Le premier retour reçu pointe un fichier de test qui n'a rien à voir avec le côté utilisateur de HWA pour dénoncer son illisibilité et l'absence de commentaire. On croirait une scène dans laquelle on remet un appareil à quelqu'un pour qu'il l'essaie mais qui au lieu de cela le démonte puis fait remarquer que l'appareil est salissant.

              Second retour, on me dit qu'en regardant dans l'ordre demo-04.c puis demo-03.c, on comprend ce que fait le second grâce aux commentaires. La logique de l'approche qui consiste à commencer par la fin m'échappe.

              Bien. J'ai clairement loupé ma présentation de HWA. Je n'ai rien à vendre, c'est du libre. Il me semble que ça peut rendre service et comme je n'ai pas l'intention de faire tous les portages possibles de HWA, je vous ai soumis cette présentation pour recueillir des avis et des critiques constructives sur le premier jet. J'aurais peut-être dû m'abstenir avant qu'une documentation exhaustive et en français ne soit disponible.

              Je reste disponible pour répondre aux questions s'il y en a malgré mon comportement de douchebag .

              « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

    • [^] # Re: code code fait la poule

      Posté par . Évalué à 2.

      Il a bien dit que c'était pour écrire du code sans commentaires :)

      Et sinon, si j'ai envie d'en écrire, des commentaires, je fais comment avec HWA, hein ?

  • # lapin compris

    Posté par . Évalué à 5.

    Le but c'est de faire un paquet de macro C pour aider à écrire du code d'écriture de registre proprement ?

    Si le but est de faire vraiment compact, tout doit se retrouver dans un header pour que le compilateur vire les fonctions non utilisés(fonction static). Le principe d'une lib à coté fait que l'on a toujours qq dizaine de ko de mémoire de pris même pour faire clignoter une led.

    En plus, si tout est dans un header, le compilo peut faire de l'inlining et si les macros sont pas trop bête, le code peut vraiment devenir tout petit.

    "La première sécurité est la liberté"

    • [^] # Re: lapin compris

      Posté par (page perso) . Évalué à 1.

      C'est exactement ce qui est fait ! C'est une combinaison de « function-like » macros et de fonctions inline pour les accès synchrones, plus une structure de données pour mémoriser puis résoudre les contraintes exprimées par les accès asynchrones.

      Le compilateur n'ayant à manipuler que des constantes numériques connues au moment de la compilation, il peut optimiser au maximum le code pour ne laisser finalement que les accès réels aux registres matériels.

      « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

      • [^] # Re: lapin compris

        Posté par . Évalué à 2.

        Difficile de lire ton code alors :)

        A toi de voir si tu peux recoder l'avr-lib en bien plus compact, tu aura du succès dans ce cas. Je n'ai pas vu de "static" dans tes exemples.

        "La première sécurité est la liberté"

        • [^] # Re: lapin compris

          Posté par (page perso) . Évalué à 0.

          A toi de voir si tu peux recoder l'avr-lib en bien plus compact, tu aura du succès dans ce cas.

          Je crois que la comparaison des codes machines produits par demo-01.c et demo-04.c est concluante.

          Je n'ai pas vu de "static" dans tes exemples.

          Il n'y en a pas (en tout cas pour la version Atmel de HWA), pourquoi ?

          « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

          • [^] # Re: lapin compris

            Posté par . Évalué à 2.

            A moins que tu es un linker intelligent qui fait le ménage après coup, toutes tes fonctions vont se retrouver dans objet, car sans "static", cela veut dire que la fonction est appelable de l’extérieur du .o, elle est donc généré même si elle n'est pas utilisé.

            "La première sécurité est la liberté"

            • [^] # Re: lapin compris

              Posté par (page perso) . Évalué à 0.

              Ah que non ! Il n'y a pas de static mais des inline qui sont automatiquement éliminées.

              Il est vrai aussi que l'éditeur de liens de gcc a beaucoup progressé ces derniers temps sur l'optimisation du code compilé.

              Chargez le code, compilez les démos, comparez le code produit à la version d'origine…

              « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

              • [^] # Re: lapin compris

                Posté par . Évalué à 2.

                C'est grâce à gold alors. "Inline" ne veut pas dire ce que tu semble croire. gcc utilisait "static inline".

                "inline" demande de mettre en ligne le code, et encore souvent les compilateurs ne font plus attention à ce mot clef.

                Seul "static" veut dire que tu ne veux jamais accéder au code de l'extérieur. Si tu utilises 2 fichiers .c, tu dois avoir une erreur de compilation.

                "La première sécurité est la liberté"

                • [^] # Re: lapin compris

                  Posté par (page perso) . Évalué à -2.

                  http://gcc.gnu.org/onlinedocs/gcc/Inline.html:

                  By declaring a function inline, you can direct GCC to make calls to that function faster. One way GCC can achieve this is to integrate that function's code into the code for its callers. This makes execution faster by eliminating the function-call overhead; in addition, if any of the actual argument values are constant, their known values may permit simplifications at compile time so that not all of the inline function's code needs to be included.

                  Les fonctions inline de HWA sont éliminées à la compilation. C'est une nécessité pour que HWA ait un intérêt. S'il faut ajouter static pour cela, cela sera fait mais jusqu'à présent le problème ne s'est pas posé.

                  « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

                  • [^] # Re: lapin compris

                    Posté par . Évalué à 1.

                    Tu n'as toujours pas compris l'usage de "inline".

                    Inline demande l'inclusion dans le code objet quand c'est possible la fonction est toujours "extern" par défaut. Donc, en plus de son inclusion dans le code du fichier .c en cours, il y a une version de la fonction qui est appelable de l'extérieur depuis un autre .o. Le symbole de la fonction est présent et trouvable par le linker.

                    Aujourd'hui, les linker sont capable d'enlever le code non utilisé dans les .o. Mais c'est assez rare.

                    De plus, si tu utilises 2 fichiers .c que tu compiles avec un header contenant la même fonction inline foo (), les 2 symboles existent, et le linker va sortir en erreur. Pour masquer au linker, les 2 symboles, la fonction doit être marqué comme static.

                    Il suffit que tu face une démo utilisant 2 fichiers .c utilisant ton ".h", tu le verra par toi même.

                    "La première sécurité est la liberté"

                    • [^] # Re: lapin compris

                      Posté par (page perso) . Évalué à 1.

                      Je viens de le faire : j'ai copié la fonction ioinit() de demo-03.c en ioinit2() dans un autre fichier qui inclue lui aussi . Les fonctions inlines de hwa.h sont donc définies dans les deux unités de compilation et demo-03.c appelle les deux fonctions ioinit() et ioinit2().

                      Pas de message d'erreur. J'en déduis que les inlines n'ont pas été transmises aux fichiers objets ; elles n'apparaissent d'ailleurs pas dans leur table des symboles (objdump -t).

                      « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

                      • [^] # Re: lapin compris

                        Posté par . Évalué à 2.

                        Tu as été jusqu'au link ? Genre :"$ avr-gcc demo-03.c demo-03_2.c" ?

                        "La première sécurité est la liberté"

                        • [^] # Re: lapin compris

                          Posté par (page perso) . Évalué à 1.

                          J'ai produit un exécutable.

                          LANG=C make -k clean all
                          Rebuilding deps...
                          rm -rf build
                          find . '('          \
                                  -name '*~'      \
                                  -o -name '*.cp.*'   \
                                  -o -name '*.pyc'    \
                                  ')' -exec rm {} ';'
                          avr-gcc -mmcu=attiny44 -std=c99 -Wall -Os -DFUSE_LB=0xE2 -DFUSE_HB=0xDF -DFUSE_EB=0xFE -DHW_DEVICE=attiny44 -I/home/indy/dev/hwa -c demo-03.c -o build/demo-03.attiny44.o
                          avr-gcc -mmcu=attiny44 -std=c99 -Wall -Os -DFUSE_LB=0xE2 -DFUSE_HB=0xDF -DFUSE_EB=0xFE -DHW_DEVICE=attiny44 -I/home/indy/dev/hwa -c demo-03b.c -o build/demo-03b.attiny44.o
                          avr-gcc -mmcu=attiny44 -Wl,-Map,build/out.map,--cref -Wl,--gc-sections -o build/out.elf build/demo-03.attiny44.o build/demo-03b.attiny44.o 
                          avr-objcopy --gap-fill=0xFF -R .eeprom -O ihex build/out.elf build/out.hex
                          avr-objcopy -R .eeprom -O binary --gap-fill=0xFF --pad-to 0x1000 build/out.elf build/out.bin
                          avr-objdump -h -S build/out.elf >build/out.lst
                          avr-size build/out.elf
                             text    data     bss     dec     hex filename
                              220       0       2     222      de build/out.elf
                          
                          Compilation finished at Fri Jun 29 12:44:21
                          
                          
                          build/demo-03.attiny44.o:     file format elf32-avr
                          
                          SYMBOL TABLE:
                          00000000 l    df *ABS*  00000000 demo-03.c
                          00000000 l    d  .text  00000000 .text
                          00000000 l    d  .data  00000000 .data
                          00000000 l    d  .bss   00000000 .bss
                          0000003e l       *ABS*  00000000 __SP_H__
                          0000003d l       *ABS*  00000000 __SP_L__
                          0000003f l       *ABS*  00000000 __SREG__
                          00000000 l       *ABS*  00000000 __tmp_reg__
                          00000001 l       *ABS*  00000000 __zero_reg__
                          00000000 l     O .bss   00000001 direction.1643
                          00000001 l     O .bss   00000001 pwm.1642
                          00000000 l    d  .text.startup  00000000 .text.startup
                          00000000 l    d  .comment   00000000 .comment
                          00000000 g     F .text  00000054 __vector_11
                          00000054 g     F .text  00000016 ioinit
                          00000000 g     F .text.startup  00000014 main
                          00000000         *UND*  00000000 ioinit2
                          00000000         *UND*  00000000 __do_clear_bss
                          
                          
                          build/demo-03b.attiny44.o:     file format elf32-avr
                          
                          SYMBOL TABLE:
                          00000000 l    df *ABS*  00000000 demo-03b.c
                          00000000 l    d  .text  00000000 .text
                          00000000 l    d  .data  00000000 .data
                          00000000 l    d  .bss   00000000 .bss
                          0000003e l       *ABS*  00000000 __SP_H__
                          0000003d l       *ABS*  00000000 __SP_L__
                          0000003f l       *ABS*  00000000 __SREG__
                          00000000 l       *ABS*  00000000 __tmp_reg__
                          00000001 l       *ABS*  00000000 __zero_reg__
                          00000000 l    d  .comment   00000000 .comment
                          00000000 g     F .text  00000016 ioinit2
                          
                          

                          « J'ai pas Word, j'ai pas Windows, et j'ai pas la télé ! »

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.