Notions de bases en C

Processus de développement d'un programme en C

Du code source à la mémoire programme du microcontrôleur...

En tant que futur programmeur, capable de « déboguer » ses propres programmes, comprendre le processus de compilation qui rentre en jeu lorsqu’on développe une application, est très important.

Que se passe-t-il à partir de l'instant où on clique sur l’onglet ‘Build’, ou ‘Compile’ jusqu’au moment où le code est prêt à être programmé dans le microcontrôleur?

Les étapes de traitements que va subir le fichier source jusqu'à la production du code prêt à être programmé sont représentées par la figure ci-dessous, pour l'environnement MPLAB X de Microchip, pris en exemple:

 

Les fichiers sources et les fichiers en-têtes:

Au sommet de cette chaîne d’exécution se trouvent les fichiers sources et les fichiers en-têtes dans lesquels on dira au compilateur ce qu’on veut que le microcontrôleur fasse. Ces deux fichiers sont les interfaces entre l’homme et la machine, ils sont écrits dans un langage compréhensible par les êtres humains. Ils contiennent les commentaires (ou du moins devrait en contenir), et sont écrits dans une forme lisible par les êtres humains (indentation, les espaces, …). Les fichiers sources ont une extension ".c", et les fichiers en-tête une extension ".h".

L'utilitaire Make:

Ce programme contrôle l’ensemble du processus de construction du code exécutable. C’est lui qui appelle le compilateur, lui passe les fichiers qui doivent être construits, et tout ce qui suit jusqu’à l’appel de l’éditeur de liens (Linker). Quand on clique sur le bouton ‘Build’, ou ‘Compile’ (tout dépend de l’environnement de développement),  on exécute le programme ‘Make’, qui se charge de traiter entièrement le processus de création du code exécutable en arrière plan.

En association avec l’utilitaire ‘Make’, il y a le ‘Makefiles’.  Ce fichier (Makefiles), définit quel fichier est associé au projet, les liens avec les dépendances de ces fichiers.

Certains environnements de développement comme MPLAB X génèrent automatiquement le fichier Makefiles sur la base de la configuration faite lors de la création du projet. Et le contenu du Makefiles n’a plus besoin d’être modifié, les développeurs experts peuvent le modifier s’ils le désirent pour avoir le contrôle sur le processus de construction de fichiers exécutables.

Le compilateur:

Il sert à traduire le code source (.c), en assembleur (.asm); il comprend trois sous-ensembles: le Préprocesseur (Preprocessor), l’analyseur de code (Parser), le générateur de code (Code generator).

Le Préprocesseur :

Ce dernier exécute les directives qui le concernent dans le programme, enlève tout ce qui rend le code lisible par les êtres humains, puis présente ce code sous une forme pure au Parser.

Tous les commentaires sont supprimés, ainsi que les espaces blancs inutiles ;

Tous les textes et les macros sont remplacées par leur valeur ;

Le contenu des fichiers en-têtes est fusionné au code source.

Le Parser :

Avec son rôle d'analyseur de code, il effectue une grande partie de travail du compilateur:

L’analyse lexicale : création des « jetons » à partir du code fournit par le préprocesseur (les jetons sont des caractères ou des chaînes de caractères qui ensemble forment quelque chose de significatif).

L’analyse syntaxique : elle s’assure que les jetons des expressions sont  conformes aux règles du langage C ;

L’analyse sémantique : détermine quelles actions doivent être entreprises par chaque expression, puis soumet une liste de ces actions au générateur de code.

Le générateur de code : (Code generator)

Le générateur de code est unique pour chaque architecture de microcontrôleur. Il prend la liste transmise par le Parser, des actions que doit réaliser le programme;  puis traduit celle-ci en instructions en assembleur spécifiques au composant.

Le code assembleur généré est «déplaçable» ; ce qui signifie que rien dans ce code n’est assigné à une adresse physique fixe du composant.  La désignation des zones où le code et les variables seront situés est réalisée par le Linker (l'éditeur de liens) que nous verrons plus tard.

L’assembleur (Assembler):

Il récupère cette forme de code machine encore lisible par les êtres humains pour le traduire directement en code machine sous forme binaire.

Même si l’assembleur utilise un Préprocesseur, un Parser et un générateur de code, son utilisation du Parser est simplifiée car elle n’a pas besoin d’une étape d’analyse sémantique.

Le code généré par l'assembleur est une liste d’actions qui seront prises, sous forme d'un fichier objet contenant du code binaire au format presque exécutable, prêt pour le traitement par le Linker.

L'éditeur de liens:  (Linker) :

Le rôle du Linker est de combiner des fichiers objets et des librairies (qui elles même sont sous forme de fichiers objets), en un seul fichier exécutable. Dans le cas des microcontrôleurs ce sera un fichier au format hexadécimal (.hex).

L’emplacement des fichiers n’est pas figé, ils sont déplaçables ; cela veut dire qu’ils peuvent être placés n’importe où dans la mémoire du composant ; leur adresse n’est pas définie dans les plages d’adresses du microcontrôleur.  Cela pose problème, car, par exemple si on appelle une fonction nommée "my_function" dans un programme, celle-ci ne sera pas exécutée car "my_function" n’a pour l’instant pas d’adresse. L’instruction d’appel n’a fait que réserver un espace au Linker (en lui disant : « Quand tu pourras trouver où "my_function" sera situé dans la mémoire programme, donne lui une adresse ainsi je pourrai l’appeler à partir de cet endroit »).

L'une des tâches du Linker consiste à déterminer l’emplacement (ou la plage d'adresses) mémoire du microcontrôleur où va résider tout le code et les données du programme.

L'assistant du Linker dans cette tâche est le fichier script du Linker (fichier lkr), qui définit la structure de la mémoire du microcontrôleur ainsi que les emplacements qui peuvent ou non être attribués par le Linker. Par exemple, une partie de la mémoire est réservée spécifiquement à l'utilisation des interruptions. Avec ces informations, le Linker tente de placer chaque bloc de code et chaque variable à une adresse spécifique dans la zone mémoire du composant. Si l’emplacement est convenable, le Linker reviendra sur chaque espace réservé et remplacera la référence à un nom de variable ou à un nom de fonction par l'adresse qui vient de lui être attribuée.

A la sortie de ce processus, le programme est dans sa phase de traitement finale, le code hexadécimal du fichier prêt à être programmé dans le microcontrôleur est généré.

Selon les environnements de développement, le Linker peut dans cette étape, produire une cartographie qui montrera chaque variable et les blocs de code tels qu’ils seront rangés dans la mémoire du composant.

Le fichier hex :

C’est le fichier exécutable qui sera programmé dans la mémoire du microcontrôleur.

Le librarian : (L'archiver pour certains compilateurs)

Le Librarian (appelé Archiver par les compilateurs basés sur GCC) est un outil permettant de placer des fichiers objets dans un conteneur appelé bibliothèque (ou archive pour les compilateurs basés sur GCC). Ainsi, une bibliothèque n'est rien d'autre qu'une collection de fichiers objets (généralement liés) prêts pour être réutilisés par d'autres projets.