Quand devez-vous utiliser un système d’exploitation en temps réel (RTOS) pour un projet intégré? Qu’est-ce que cela apporte et quels sont les coûts? Heureusement, il existe des définitions techniques strictes, qui peuvent également aider à déterminer si un RTOS est le bon choix pour un projet.

La partie «temps réel» du nom couvre notamment le principe de base d’un RTOS: la garantie que certains types d’opérations se termineront dans un laps de temps prédéfini et déterministe. En «temps réel», nous trouvons des catégories distinctes: temps réel dur, ferme et doux, avec des sanctions de moins en moins sévères pour le non-respect de la date limite. À titre d’exemple de scénario en temps réel difficile, imaginez un système dans lequel le contrôleur intégré doit répondre aux données de capteur entrantes dans un laps de temps spécifique. Si la conséquence du non-respect d’une telle échéance brise les composants en aval du système, au sens figuré ou littéral, l’échéance est difficile.

En comparaison, le temps réel doux serait le genre d’opération où il serait formidable que le contrôleur réponde dans ce laps de temps, mais si cela prend un peu plus de temps, ce serait tout à fait bien aussi. Certains systèmes d’exploitation sont capables de durer en temps réel, tandis que d’autres ne le sont pas. Ceci est principalement un facteur de leur conception fondamentale, en particulier le planificateur.

Dans cet article, nous examinerons divers systèmes d’exploitation, pour voir où ils se situent dans ces définitions et quand vous souhaitez les utiliser dans un projet.

Une question d’échelle

Différents systèmes d’exploitation intégrés s’adressent à différents types de systèmes et ont différents ensembles de fonctionnalités. Le plus minimaliste des RTOS populaires est probablement FreeRTOS, qui fournit un planificateur et avec lui des primitives multi-threading comprenant des threads, des mutex, des sémaphores et des méthodes d’allocation de tas thread-safe. En fonction des besoins du projet, vous pouvez choisir parmi un certain nombre de méthodes d’allocation dynamique, ainsi qu’autoriser uniquement l’allocation statique.

À l’autre extrémité de l’échelle, nous trouvons des RTOS tels que VxWorks, QNX et Linux avec des correctifs de planification en temps réel appliqués. Il s’agit généralement de systèmes d’exploitation certifiés POSIX ou compatibles, qui offrent la commodité de développer pour une plate-forme hautement compatible avec les plates-formes de bureau classiques, tout en offrant un certain degré de garantie de performances en temps réel, grâce à leur modèle de planification.

Encore une fois, un RTOS n’est et un RTOS que si l’ordonnanceur est livré avec une garantie d’un certain niveau de déterminisme lors du changement de tâche.

Temps réel: définition «immédiate»

Même en dehors du domaine des systèmes d’exploitation, les performances en temps réel des processeurs peuvent différer considérablement. Cela devient particulièrement évident lorsque l’on regarde les microcontrôleurs et le nombre de cycles nécessaires pour qu’une interruption soit traitée. Pour les microcontrôleurs Cortex-M populaires, par exemple, la latence d’interruption est comprise entre 12 cycles (M3, M4, M7) et 23+ (M1), dans le meilleur des cas. Divisez par la vitesse du processeur et vous avez environ un quart de microseconde.

En comparaison, lorsque nous examinons la gamme de microcontrôleurs 8051 de Microchip, nous pouvons voir dans le «  Manuel du matériel des microcontrôleurs Atmel 8051  » dans la section 2.16.3 («  Temps de réponse  ») que selon la configuration d’interruption, la latence d’interruption peut être n’importe où de 3 à 8 cycles. Sur les plates-formes x86, l’histoire est encore plus compliquée, en raison de la nature quelque peu alambiquée des IRQ x86. Encore une fois, une fraction de microseconde.

Cette latence place une limite absolue sur les meilleures performances en temps réel qu’un RTOS peut accomplir, bien qu’en raison de la surcharge liée à l’exécution d’un planificateur, un RTOS ne se rapproche pas de cette limite. C’est pourquoi, pour les meilleures performances en temps réel absolues, une approche déterministe à boucle d’interrogation unique avec des routines de gestion d’interruption rapide pour les événements entrants est de loin la plus déterministe.

Si l’interruption ou un autre changement de contexte coûte des cycles, l’exécution plus rapide du processeur sous-jacent peut également évidemment réduire la latence, mais s’accompagne d’autres compromis, dont le moindre n’est pas la consommation d’énergie plus élevée et les besoins de refroidissement accrus.

Ajout de quelques fils sympas

Comme FreeRTOS le démontre, le point principal de l’ajout d’un système d’exploitation est d’ajouter le support multi-tâches (et multi-threading). Cela signifie un module de planification qui peut utiliser une sorte de mécanisme de planification pour découper le temps du processeur en «tranches» dans lesquelles différentes tâches ou threads peuvent être actifs. Alors que le planificateur multi-tâches le plus simple est un planificateur de style coopératif, où chaque thread cède volontairement pour laisser les autres threads faire leur travail, cela présente l’inconvénient distinct que chaque thread a le pouvoir de tout ruiner pour les autres threads.

La plupart des systèmes d’exploitation en temps réel utilisent à la place un planificateur préemptif. Cela signifie que les threads d’application n’ont aucun contrôle sur le moment où ils s’exécutent ou sur la durée. Au lieu de cela, une routine d’interruption déclenche le planificateur pour choisir le prochain thread à exécuter, en prenant soin de différencier les tâches préemptives de celles qui ne le sont pas. Les soi-disant routines du noyau, par exemple, peuvent être marquées comme non préemptives, car leur interruption peut provoquer une instabilité ou une corruption du système.

Bien que Windows et Linux, dans leur configuration habituelle, utilisent un planificateur préemptif, ces planificateurs ne sont pas considérés comme adaptés aux performances en temps réel, car ils sont réglés pour prioriser les tâches de premier plan. Les tâches destinées à l’utilisateur, telles qu’une interface utilisateur graphique, continueront de fonctionner correctement même si les tâches d’arrière-plan peuvent faire face à une pénurie de cycles de processeur. C’est ce qui rend certaines tâches en temps réel sur les systèmes d’exploitation de bureau une telle corvée, nécessitant diverses solutions de contournement.

Une bonne démonstration de la différence avec un planificateur préemptif focalisé en temps réel peut être trouvée dans la version x86 du QNX RTOS. Bien que cela fonctionne correctement sur un système de bureau x86, l’interface graphique commencera à se bloquer et à devenir lente lorsque les tâches d’arrière-plan sont effectuées, car le planificateur ne donnera pas de traitement spécial aux tâches de premier plan (l’interface graphique). L’objectif du correctif en temps réel du noyau Linux modifie également le comportement par défaut du planificateur pour mettre la gestion des interruptions au premier plan, sans faire de distinction entre les tâches individuelles sauf si elles sont configurées pour le faire en définissant explicitement les priorités des threads.

RTOS ou pas, c’est la question

À ce stade, il doit être clair ce que l’on entend par «temps réel» et vous pouvez avoir une idée de savoir si un projet bénéficierait d’un RTOS, d’un système d’exploitation simple ou d’une approche «superloop» basée sur les interruptions. Il n’y a pas de réponse unique ici, mais en général, on cherche à trouver un équilibre entre les performances en temps réel requises et le temps et le budget disponibles. Ou dans le cas d’un projet de loisir dans la mesure où l’on peut se donner la peine de l’optimiser.

La première chose à considérer est de savoir s’il y a des échéances strictes dans le projet. Imaginez que vous avez quelques capteurs attachés à une carte qui doivent être interrogés exactement aux mêmes intervalles et le résultat écrit sur une carte SD. Si une sorte de gigue entre des lectures de plus de quelques dizaines de cycles rendrait les résultats inutiles, vous avez une exigence en temps réel difficile de ce nombre de cycles.

Nous savons que le matériel sous-jacent (MCU, SoC, etc.) a une latence d’interruption fixe ou dans le pire des cas. Cela détermine le meilleur scénario. Dans le cas d’une approche à boucle unique pilotée par interruption, nous pouvons probablement facilement répondre à ces exigences, car nous pouvons résumer la latence d’interruption dans le pire des cas, le coût du cycle de notre routine d’interruption (ISR) et le temps le plus défavorable. prendre pour traiter et écrire les données sur la carte SD. Ce serait hautement déterministe.

Dans le cas de notre exemple de capteurs et de carte SD, la version RTOS ajouterait probablement une surcharge par rapport à la version à boucle unique, en raison de la surcharge de son planificateur. Mais alors imaginez que l’écriture sur la carte SD prenait beaucoup de temps et que vous vouliez également gérer les entrées d’utilisateurs peu fréquentes.

Avec un RTOS, comme les échantillons doivent être prélevés aussi près que possible, vous voudrez rendre cette tâche non préemptive et lui donner une échéance de planification difficile. Les tâches d’écriture sur la carte SD et toute entrée utilisateur, avec une priorité inférieure. Si l’utilisateur a beaucoup tapé, le RTOS peut revenir à la gestion de la collecte de données au milieu du traitement des chaînes, par exemple, pour fixer une date limite. Vous, le programmeur, n’avez pas à vous en soucier.

En bref: un RTOS offre une planification déterministe, tandis qu’une boucle unique pilotée par interruption élimine complètement le besoin de planification, en plus de vous assurer que votre superloop tourne assez fréquemment.

Conforts de créature

Quand on retire le rideau, il est évident que pour le matériel du processeur, des concepts comme les «threads» et les mécanismes de synchronisation des threads tels que les mutex et les sémaphores ne sont que des concepts logiciels qui sont implémentés à l’aide de fonctionnalités matérielles. Au plus profond de nous, nous savons tous qu’un MCU monocœur n’exécute pas vraiment toutes les tâches simultanément lorsqu’un planificateur exécute son devoir multitâche.

Pourtant, un RTOS – même minimaliste comme FreeRTOS – nous permet d’utiliser ces concepts logiciels sur une plate-forme lorsque nous devons simultanément rester aussi proches que possible du matériel pour des raisons de performances. Ici, nous trouvons l’équilibre entre performances et commodité, FreeRTOS nous laissant à nous-mêmes lorsqu’il s’agit d’interagir avec le reste du système. D’autres RTOS, comme NuttX, QNX et VxWorks, offrent un environnement compatible POSIX à part entière qui prend en charge au moins un sous-ensemble de code Linux standard.

S’il est facile de penser à FreeRTOS, par exemple, comme un RTOS que l’on placerait sur un MCU, il fonctionne tout aussi bien sur de grands SoC. De même, ChibiOS / RT fonctionne avec plaisir sur tout, d’un MCU AVR 8 bits à un système x86 costaud. La clé ici est de trouver le bon équilibre entre les exigences du projet et ce que l’on pourrait appeler le confort de la créature qui facilite le développement du système cible.

Pour les RTOS qui ajoutent également une couche d’abstraction matérielle (par exemple ChibiOS, QNX, RT Linux, etc.), la partie HAL facilite le portage entre différents systèmes cibles, ce qui peut également être considéré comme un argument en sa faveur. En fin de compte, cependant, opter pour une boucle unique, un RTOS simple, un RTOS compliqué ou «juste un système d’exploitation» est une décision qui dépend en fin de compte du contexte du projet.