Aspect software
Le software est la partie la plus importante d'un système embarqué, du moins pour ceux basés sur un processeur. En effet, il représente la fonction réalisée par le système, son cerveau en quelque sorte. Cette section décrit quelles sont les différentes possibilités pour programmer un système embarqué et quelles sont les considérations particulières à prendre en compte. Il s'agit essentiellement d'une introduction des éléments de base puisque la suite du livre concerne essentiellement la programmation et les aspects software.
Tout au long de la section, il faut garder en tête qu'un software développé pour un système embarqué n'est généralement pas portable directement sur un autre, le lien avec le hardware étant plutôt fort. Cela renforce notamment l'utilisation de systèmes embarqués de plus haut niveau, exécutant un Linux embarqué. Dans ce cas, il est possible d'écrire un seul programme qui pourra être directement exécuté sur tout hardware étant capable d'exécuter un Linux embarqué.
Système réactif
Un système embarqué est un système réactif, c'est-à-dire qu'un comportement sera exécuté en réaction à des évènements provenant de son environnement. Si l'on s'en réfère à la définition proposée par Bergé et coll. (High-Level System Modeling: Specification Languages, par Jean-Michel Bergé, Oz Levia et Jacques Rouillard, chez Springer (1995), ISBN 978-1-4613-5973-9), un système embarqué est en interaction continuelle avec son environnement, rythmant son exécution :
“A reactive system is one that is in continual interaction with its environment and executes at a pace determined by that environment.”
C'est l'environnement qui impose son rythme au système embarqué et pas l'inverse. Par conséquent, il est important de bien connaitre cet environnement et les contraintes qu'il impose avant de choisir le type de système embarqué qui sera utilisé et de le programmer. Il faut en effet pouvoir au minimum suivre le rythme imposé par l'environnement.
Un système embarqué réagit essentiellement à deux types d'évènements en provenance de son environnement externe ou interne. Il y a des évènements périodiques comme on peut retrouver dans des machines tournantes ou des boucles de contrôle et des évènements non périodiques provoqués, par exemple, par la pression de boutons.
Enfin, concernant le développement logiciel à proprement parler, la programmation orientée évènements pourrait être un paradigme adapté pour développer des programmes adaptés aux systèmes embarqués.
Temps réel
Certains systèmes embarqués doivent être hautement réactifs, en ce sens qu'ils sont tenus de réagir à certains évènements endéans une deadline fixée; ils sont dits systèmes temps réel. Il y a essentiellement deux catégories de systèmes temps réel et on peut les différencier en examinant ce qui se passe si une deadline est manquée.
Dans le cas du système de contrôle d'un avion, si un système de sécurité n'a pas réagi endéans une deadline, les conséquences peuvent être graves si cela mène l'avion à un crash, par exemple. Dans le cas d'un système embarqué qui gère l'émetteur d'une communication satellite, si un paquet de données a été transmis un peu plus tard que prévu, les conséquences ne sont pas si graves.
La caractérisation temps réel d'un système embarqué n'est donc pas une question de vitesse de réaction, mais bien de respect de deadlines. Le temps restant avant la prochaine deadline peut être de quelques microsecondes, voire de quelques heures. La figure 14 montre plusieurs domaines et le type de contrainte temps réel (aucune, souple ou forte). Une simulation sur PC n'a pas de contraintes temps réel, elle se déroule simplement au rythme de ce que permet la puissance de la machine. On monte ensuite doucement en exigence avec les interfaces utilisateur et la diffusion de vidéos sur internet pour arriver aux contraintes temps réel souples avec le cruise control. De là, on continue d'augmenter les exigences avec la télécommunication, le contrôle aérien et enfin les moteurs contrôlés par de l'électronique, où il est très important de réagir endéans des deadlines imposées.
Polling et interruption
Le software interagit avec le hardware pour réaliser la fonction du système embarqué. Pour cela, il va notamment devoir communiquer avec différents périphériques, pour acquérir des données en provenance de senseurs, par exemple. Cette acquisition peut se faire de différentes manières, typiquement par polling ou avec le mécanisme d'interruption.
Le polling, également appelé attente active, consiste à régulièrement inspecter la valeur de registres d'état qui indiquent si les données attendues sont disponibles ou non. Une fois que c'est le cas, elles peuvent être lues pour ensuite être traitées. Le mécanisme d'interruption fonctionne dans l'autre sens, c'est-à-dire que c'est le périphérique qui va prévenir le processeur une fois que les données sont disponibles, en l'interrompant.
Dans le premier cas, le processeur étant en attente active, il ne peut rien faire d'autre en attendant que les données soient disponibles. Dans le second cas, il va pouvoir exécuter du code avant d'être interrompu. Ces deux solutions ont chacune des avantages et inconvénients, chacune étant plus adaptée à certaines situations concrètes, décrites plus loin.
Le choix sera d'autant plus critique dans le cadre d'un système temps réel, étant donné les contraintes fortes au niveau du respect des deadlines; on ne peut pas toujours se permettre d'attendre un périphérique.
Programmation
Un système embarqué doté d'un processeur a besoin de code pour faire quelque chose. Ce dernier est placé dans une mémoire accessible par le processeur afin de pouvoir être exécuté, instruction par instruction. Typiquement, le software d'un système embarqué sera présenté sous la forme d'une image ROM, définitive et non modifiable.
Étant donné un système embarqué, on connait la taille de la ROM qu'il possède et donc les adresses de tous les emplacements mémoire disponibles. Pour chacun de ces emplacements, il faut en définir le contenu binaire représentant des instructions à exécuter. Ces instructions sont écrites en respectant le jeu d'instructions du processeur utilisé, ce qui peut évidemment prendre beaucoup de temps à comprendre avant de commencer à programmer.
Pour éviter de mémoriser les codes machine de toutes les instructions d'un processeur donné, on peut programmer à l'aide d'un langage d'assemblage, représentation textuelle de plus haut niveau du code machine. Un programme écrit dans un langage d'assemblage est traduit en code machine par l'assembleur. Programmer à un tel niveau reste une tâche fortement consommatrice de temps, mais peut être plus efficace si l'on s'aide de librairies de code préexistant.
Enfin, pour gagner du temps de développement, il est possible d'utiliser un langage de haut niveau qui offre des constructions et abstractions plus pratiques et proches de la façon de penser du développeur et du problème qu'il veut résoudre. Un exemple d'un tel langage, adapté aux systèmes embarqués, et qui reste d'assez bas niveau et proche du hardware est le langage C. Un programme écrit dans un langage de haut niveau est compilé en code machine par le compilateur.
La figure 15 résume les trois principales possibilité de programmation d'un système embarqué : directement en langage machine, via un langage d'assemblage ou à l'aide d'un langage de haut niveau. La phase de traduction d'un langage d'assemblage est quasi immédiate et unique tandis que la phase de compilation d'un langage de haut niveau peut être faite de plusieurs manières et donc optimisée.
Développement
Développer un logiciel pour un système embarqué ne se fait pas complètement de la même manière que pour un logiciel destiné à un ordinateur « traditionnel ». En effet, il ne sera pas toujours possible de directement développer sur le système embarqué, mais il faudra le faire sur un système hôte. La figure 16 résume le processus de développement type. Une fois que le développeur a compilé un code machine sur le système hôte, il l'envoie sur le système cible ou il l'exécute sur un émulateur ou simulateur sur le système hôte. Dans le cas où le code est envoyé sur le système cible, il est possible de le débugger en runtime, par exemple avec le standard JTAG présenté plus loin dans ce chapitre.
Concernant la procédure de développement, il n'y en a évidemment pas une seule unique à suivre. Il est cependant important de coordonner le développement des aspects hardware avec celui des aspects software. La figure 17 reprend les grandes étapes de développement.
La première phase consiste évidemment à établir les spécifications précises du système à développer. Il faut notamment caractériser l'environnement d'exécution et identifier les contraintes qu'il impose. De plus, il faudra faire des choix par rapport aux niveaux de fiabilité et d'efficacité désirés.
À partir de ces spécifications, il faudra déterminer ce qui sera fait en hardware et en software, et décider des composants à utiliser. Plusieurs itérations peuvent être nécessaires à cette seconde étape, qui est par ailleurs très importante car c'est un point de non retour.
Vient ensuite le développement, en parallèle, des parties software et hardware qui seront ensuite intégrées, puis testées et validées. Une fois le système développé et déployé, on entre dans une phase importante de maintenance. Le système doit être monitoré, des bugs éventuels doivent être corrigés et des mises à jour peuvent être appliquées.
Debug
Débugger le software d'un système embarqué est une tâche plus compliquée que pour un logiciel développé sur un ordinateur « traditionnel ». Une possibilité consiste à utiliser les standards JTAG (le Joint Test Action Group (JTAG) est une association d'industries d'électroniques qui développe des méthodes pour vérifier et tester des circuits imprimés) qui offrent notamment un protocole basé sur une communication série pour accéder au processeur et bus système.
Système d'exploitation
Terminons ce tour des aspects software en introduisant la notion de système d'exploitation. Il s'agit d'un programme qui est exécuté sur le hardware et qui joue le rôle d'intermédiaire entre le hardware et les applications utilisateur. Sur un ordinateur « traditionnel », les systèmes d'exploitation les plus connus sont Windows, MacOS et Linux. Un système d'exploitation offre des abstractions des composants hardwares pour faciliter la programmation. Par exemple, on peut manipuler des fichiers sur le disque dur, des fenêtres sur l'écran, des processus sur le processeur, etc. Le développeur ne doit ainsi plus se soucier de savoir contrôler le hardware à très bas niveau, il exploite les services offerts par le système d'exploitation.