24) BOUUUMMMMMmmm … Collision de PILE.

Particulièrement NON indispensable, ce chapitre ne s’adresse qu’aux programmeurs qui ont été voir dans les démonstrateurs comment sont traitées certaines séquences. Sans que ce soit forcément des exemples à afficher sur les murs, l’intégralité des démonstrateurs a été rédigée avec un souci permanent de rigueur et d’optimisation. S’il est vrai que le problème de collision de PILE ne se pose généralement qu’avec des programmes très volumineux qui consomment toutes les ressources internes de l’ATmega328, il peut toutefois survenir avec des logiciels moins développés mais qui utilisent des procédures récursives qui s’invoquent un grand nombre de fois sans sortie.

La PILE : Tout programmeur doit s’en préoccuper.

Visitons un court instant l’intimité du fonctionnement d’un programme. Chaque fois que le logiciel fait appel à une procédure, le microcontrôleur sauvegarde sur la PILE l’adresse de retour pour pouvoir continuer quand cette dernière est terminée. Si une procédure en appelle une deuxième, qui en invoque une troisième, toutes ces adresses de retour s’empilent les unes sur les autres. Puis, en retour de « subroutine » elles sont dépilées et l’emplacement sur la PILE libéré. Chaque fois qu’une procédure utilise une variable locale, on réserve de la place sur le TAS. Plus il y a de variables locales dans les procédures qui s’invoquent les une et les autres, plus on va entasser des octets de données. Naturellement, en retour de subroutine on libère la place sur le TAS. La Fig.109 schématise le fonctionnement interne du microcontrôleur pour la gestion des variables dynamiques. Le haut de la zone en vert est réservé à la PILE qui progresse « vers le bas ». Par exemple en 1 on a empilé deux adresses de retour. Quand la deuxième subroutine se termine, son adresse de retour esr récupérée et l’emplacement libéré en 3. En 2 ont été entassées deux variables locales. En retour d’une subroutine, en 4 la place de la variable qui n’est plus utilisée est libérée. Au cours du programme, la zone non utilisée jaune se rétréci à chaque appel de procédure ou de fonction, et se réduit pour chaque variable locale. Si l’on a trop de procédures qui appellent des procédures et qui entassent des donnée locales trop volumineuses, arrive un moment ou il y a, comme en 5, COLLISION de PILE. Le TAS et la PILE se rencontrent, l’un écrasant les données de l’autre. Le programme devient totalement incohérent et se met à faire n’importe quoi. C’est un problème particulièrement sournois, car strictement rien ne prévient le programmeur. Par exemple dans une procédure vous remplacez une variable locale qui était un byte par un int. Imaginons que le contexte à empilé et entassé tellement de données qu’il ne restait plus de place. Maintenant la variable locale fait un octet de plus. Sur un RUN se produit alors la collision tant destructrice. Votre programme se met à faire n’importe quoi. Vous avez beau analyser, analyser et analyser encore, le croquis est propre et en aucun cas ne peut expliquer le comportement du logiciel.
CONCLUSION : Il faut impérativement s’assurer que ça n’arrivera pas.

Prendre une assurance vie pour la PILE.

Parer le risque de COLLISION de PILE est tellement important, que le C++ d’Arduino met à notre disposition un outil spécial pour évaluer les risque encourus. Aussi, chaque fois que je termine un gros logiciel qui invoque des subroutines à la pelle, avec des variables locales nombreuses, avant de considérer qu’il est fiable j’en teste la PILE, ou plus exactement l’amplitude de la zone jaune qui reste quand le programme a fait son RESET. Le petit programme qui effectue ce travail consomme environ 140 octets. Aussi, sur un logiciel avec aucun gaspillage il n’y a pas la place. Je me contente de passer en remarque certains affichages, et valide la procédure. Puis elle repasse en remarques et je restitue les affichages. Dans notre application, vu que je gaspille à outrance, avec l’espoir de « rentabiliser » ma carte Arduino NANO, l’outil d’évaluation de la PILE est resté actif et de plus encombre le MENU des OPTIONS.

Configuration qui produit la collision de PILE avec le TAS.

Concrètement on oublie royalement qu’un nombre important d’interruptions se produisent en tâche de fond. Quelquefois un codeur rotatif génère des interruptions, sans compter les procédures delay(), les fonctions telles que millis(), la PWM … une foule de ressources internes déclenche des interruptions. C’est transparent car c’est le compilateur C++ qui sur ces instructions fait sa cuisine interne. Arrive un moment, ou trop de données sont empilée sur le TAS et viennent écraser les adresses empilées. Puis le délimiteur ‘}‘ de fin d’une procédure ou d’une fonction demande au processeur de dépiler une adresse de retour. Comme cette dernière contient les résidus de la variable qui a « écrasé » les octets, le programme se « branche » strictement n’importe où. Étant alors sur du code objet incohérent, le comportement du logiciel devient totalement aléatoire.
PRÉVENTIF : Aucun programmeur n’est à l’abri d’une telle « catastrophe ». Aussi, pour minimiser les risques il faut placer le minimum de chaînes de caractères dans le programme car en réalité elles sont placées sur le TAS. Il faut également (Et surtout.) minimiser les tableaux.

CURATIF : Quand se produit le Scratchhh prouitchhh bom bring protchhhh ! c’est qu’il est trop tard. Nous avons placé plein plein plein de bavardages, alors que nous savons que ces « bla bla bla » sont entassés dans la mémoire dynamique. Notre démonstrateur comporte une foule de procédures et de fonctions qui passent des paramètres, sans compter les for (byte I=1, …) qui ne sont pas gratuits. Les variables locales des boucles for doivent aussi être logées en RAM. Enfin les tableaux sont gourmands en octets.
REMÈDE : Dégager impérativement de la place sur le TAS en minimisant le nombre et la taille des vaiables locales. Éviter les procédures récursives. Placer les textes en EEPROM etc.
MANIPULATION :
01) Frapper la commande ‘p‘. (C‘est tout, du coup ici MANIPULATION est écrit au singulier !)
Cette option du Menu de base affiche la place qui reste entre la PILE et le TAS au moment où elle est invoquée. Dans notre cas l’espace disponible avoisine 1018 octets. C’est amplement suffisant pour assurer la stabilité de notre logiciel. Une longue expérience dans la pratique des microprocesseurs m’a convaincu qu’à partir d’environ cent octets de disponibles, la collision de PILE n’est plus du tout à craindre. Donc avec P13 on peut rester confiants, il n’y aura pas d’incident de circulation ou ce sera la conséquence d’une vermine dans le programme qui aura échappé aux innombrables vérifications qui ont été effectuées au cours de cette saga.

Un dernier petit bilant avec le programme d’exploitation

C’est la fin de ce didacticiel, et le programme ultime d’exploitation est arrivé à maturité. Sachant que l’on allait disposer d’autant de place que désiré, plusieurs options « de luxe » ont été ajoutées dans le but d’améliorer la convivialité d’utilisation de ce programme. (Et également pour rentabiliser l’exploitation des ressources internes de l’ATmega328.) Dans cette optique, en dernière minute le Menu de base a été complété par l’encadré du bas qui précise la fonction des options de la commande ‘e‘. À court d’idée pour l’exploitation de notre codeuse virtuelle, il est donc temps de songer à passer à une autre activité. On constate au final, que même avec l’ajout des options de convivialité on n’arrive pas à utiliser la moitié de l’espace réservé au programme dans le microcontrôleur. Aussi, toutes les routines de traitement du signal électrique virtuelles sont écrites et vérifiées et pourront servir éventuellement à créer une réplique matérielle avec des roues qui tournent, un clavier sur lequel faire clic clic clic, un tableau sur lequel brancher des lignes croisées et un tableau de LEDs pour simuler les ampoules électrique. Cette perspective est très alléchante …

La suite est ici.