
La gestion des erreurs n'est pas la chose la plus courante pour les systèmes d'exploitation conçus pour les applications de systèmes embarqués. Il s'agit d'un résultat inévitable de ressources limitées, car tous les systèmes embarqués ont certaines restrictions. Et seul un petit nombre de ces systèmes ont la capacité de se comporter comme des systèmes de bureau, c'est-à -dire d'offrir à l'utilisateur la possibilité de choisir des actions en cas d'événements exceptionnels.
Dans Nucleus SE, en général, il existe trois types de vérification des erreurs:
- des moyens pour vérifier la santé de la configuration sélectionnée pour s'assurer que les paramètres sélectionnés ne conduisent pas à des erreurs;
- éventuellement inclus du code pour vérifier le comportement d'exécution;
- Certaines fonctions API qui contribuent au développement de code plus robuste.
Tout cela sera discuté dans cet article, ainsi que quelques idées concernant les diagnostics par l'utilisateur.
Articles précédents de la série: Vérifier les paramètres
Nucleus SE a été conçu en mettant l'accent sur une configurabilité utilisateur élevée, ce qui devrait garantir la meilleure utilisation des ressources disponibles. Une telle configurabilité est une tâche complexe, car le nombre de paramètres possibles et les interdépendances entre eux sont énormes. Comme indiqué dans de nombreux articles précédents, la plupart des étapes utilisateur pour configurer Nucleus SE sont effectuées à l'aide des directives
#define dans le fichier
nuse_config.h .
Pour aider Ă identifier les erreurs de configuration,
nuse_config_check.h est inclus dans le fichier
nuse_config.c via
#include , qui effectue des vérifications d'intégrité sur les directives
#define . Voici un extrait de ce fichier:
/*** Tasks and task control ***/ #if NUSE_TASK_NUMBER < 1 || NUSE_TASK_NUMBER > 16 #error NUSE: invalid number of tasks - must be 1-16 #endif #if NUSE_TASK_RELINQUISH && (NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER) #error NUSE: NUSE_Task_Relinquish() selected - not valid with priority scheduler #endif #if NUSE_TASK_RESUME && !NUSE_SUSPEND_ENABLE #error NUSE: NUSE_Task_Resume() selected - task suspend not enabled #endif #if NUSE_TASK_SUSPEND && !NUSE_SUSPEND_ENABLE #error NUSE: NUSE_Task_Suspend() selected - task suspend not enabled #endif #if NUSE_INITIAL_TASK_STATE_SUPPORT && !NUSE_SUSPEND_ENABLE #error NUSE: Initial task state enabled - task suspend not enabled #endif /*** Partition pools ***/ #if NUSE_PARTITION_POOL_NUMBER > 16 #error NUSE: invalid number of partition pools - must be 0-16 #endif #if NUSE_PARTITION_POOL_NUMBER == 0 #if NUSE_PARTITION_ALLOCATE #error NUSE: NUSE_Partition_Allocate() enabled – no partition pools configured #endif #if NUSE_PARTITION_DEALLOCATE #error NUSE: NUSE_Partition_Deallocate() enabled – no partition pools configured #endif #if NUSE_PARTITION_POOL_INFORMATION #error NUSE: NUSE_Partition_Pool_Information() enabled – no partition pools configured #endif #endif
Le code ci-dessus effectue les vérifications suivantes:
- vérifier qu'au moins une, mais pas plus de seize tâches sont configurées;
- Confirmation que les fonctions API sélectionnées sont compatibles avec le planificateur sélectionné et d'autres paramètres spécifiés;
- vérification que pas plus de seize instances d'autres objets du noyau ont été créées;
- Confirmation que les fonctions API liées aux objets non déclarés n'ont pas été sélectionnées.
- s'assurer que les fonctions API pour les signaux et l'heure système ne sont pas utilisées lorsque la prise en charge de ces services est désactivée;
- vérification du type d'ordonnanceur sélectionné et des paramètres associés.
Dans tous les cas, la détection d'erreurs conduit à l'exécution de la directive
#error lors de la compilation. Cela provoque généralement l'arrêt de la compilation et l'affichage du message correspondant.
Ce fichier ne garantit pas l'impossibilité de créer une configuration / configuration incorrecte, mais le rend très peu probable.
Vérification des paramètres de l'API
Comme Nucleus RTOS, Nucleus SE a la possibilité d'inclure du code pour vérifier les paramètres d'appel des fonctions API au moment de l'exécution. Habituellement, cela n'est utilisé que lors du débogage et des tests initiaux, car dans le code de programme final, une consommation excessive de mémoire n'est pas souhaitable.
La vérification des paramètres est activée en définissant le paramètre
NUSE_API_PARAMETER_CHECKING dans le fichier
nuse_config.h sur
TRUE . Cela conduit à la compilation du code supplémentaire requis. Voici un exemple de vérification des paramètres d'une fonction API:
STATUS NUSE_Mailbox_Send(NUSE_MAILBOX mailbox, ADDR *message, U8 suspend) { STATUS return_value; #if NUSE_API_PARAMETER_CHECKING if (mailbox >= NUSE_MAILBOX_NUMBER) { return NUSE_INVALID_MAILBOX; } if (message == NULL) { return NUSE_INVALID_POINTER; } #if NUSE_BLOCKING_ENABLE if ((suspend != NUSE_NO_SUSPEND) && (suspend != NUSE_SUSPEND)) { return NUSE_INVALID_SUSPEND; } #else if (suspend != NUSE_NO_SUSPEND) { return NUSE_INVALID_SUSPEND; } #endif #endif
Une telle vérification des paramètres peut conduire au fait que l'appel API produira un code d'erreur. Il s'agit d'une valeur négative de la forme
NUSE_INVALID_xxx (par exemple,
NUSE_INVALID_POINTER ) - un ensemble complet de définitions est contenu dans le fichier
nuse_codes.h .
Pour traiter les valeurs d'erreur, un code d'application supplémentaire (éventuellement créé à l'aide d'une compilation conditionnelle) peut être ajouté, cependant, pour les détecter, il est préférable d'utiliser les outils de surveillance des données des débogueurs de firmware modernes.
La vérification des paramètres entraîne une consommation de mémoire supplémentaire (code supplémentaire) et affecte les performances du code, par conséquent, son utilisation affectera l'ensemble du système. Étant donné que l'intégralité du code source de Nucleus SE est disponible pour le développeur, la vérification et le débogage peuvent être effectués manuellement sur le code d'application final si une précision absolue est requise.
Vérification de la pile des tâches
Jusqu'à ce que le planificateur d'exécution jusqu'à l'exécution soit utilisé, Nucleus SE offre la possibilité de vérifier la pile de tâches, qui est similaire à une fonction similaire dans Nucleus RTOS et affiche l'espace restant sur la pile. Cet appel d'utilitaire API (
NUSE_Task_Check_Stack () ) a été décrit en détail dans un
article précédent (# 12). Quelques idées pour vérifier les erreurs de pile sont fournies plus loin dans cet article dans la section Diagnostics personnalisés.
Informations sur la version
Nucleus RTOS et Nucleus SE ont une fonction API qui renvoie simplement les informations de version / build du noyau.
Appel de l'API Nucleus RTOS
Prototype d'appel de service:
CHAR * NU_Release_Information (VOID);
Paramètres: aucun.
Valeur de retour:
Pointeur vers une chaîne contenant des informations de version se terminant par un octet nul.
Appel API Nucleus SE
Cet appel d'API prend en charge la fonctionnalité principale de l'API Nucleus RTOS.
Prototype d'appel de service:
char * NUSE_Release_Information (void);
Paramètres: aucun.
Valeur de retour:
Pointeur vers une chaîne contenant des informations de version se terminant par un octet nul.
Passer un appel pour obtenir des informations sur l'assemblage Nucleus SE
L'implémentation de cet appel API est assez simple. Un pointeur est renvoyé sur la ligne constante
NUSE_Release_Info , qui est déclarée et initialisée dans le fichier
nuse_globals.c .
Cette ligne a la forme Nucleus SE -
Xyymmmdd , oĂą:
X - état de construction:
A = alpha;
B = bĂŞta;
R = libération
aa - année de sortie
mm - mois de sortie
jj - jour de sortie
Compatible avec Nucleus RTOS
Nucleus RTOS contient la prise en charge optionnelle du magazine historique. Le noyau enregistre les détails des différentes actions du système. Il existe des fonctions API qui permettent aux programmes de:
- activer / désactiver la journalisation;
- créer une entrée de journal;
- Obtenez une entrée de journal.
Cette fonctionnalité n'est pas prise en charge dans Nucleus SE.
Nucleus RTOS dispose également de plusieurs macros de gestion des erreurs qui vous permettent de ne pas effectuer de confirmation d'erreur (ASSERT) et offrent la possibilité d'appeler des fonctions d'erreur critique définies par l'utilisateur. Ils sont éventuellement inclus dans l'assemblage du système d'exploitation. Nucleus SE ne prend pas en charge cette fonctionnalité.
Diagnostics utilisateur
Jusqu'à présent, dans cet article, nous avons examiné les outils de diagnostic et de vérification des erreurs fournis par Nucleus SE lui-même. Maintenant, il vaut la peine de dire comment les outils de diagnostic définis par l'utilisateur ou orientés vers une application spécifique peuvent être implémentés en utilisant les outils fournis par le noyau et / ou nos connaissances sur sa structure interne et son implémentation
Diagnostics spécifiques à l'application
Dans presque toutes les applications, vous pouvez ajouter du code supplémentaire pour vérifier son intégrité lors de l'exécution. Le noyau multitâche facilite et simplifie la création d'une tâche spéciale pour ce travail. Évidemment, dans cet article, nous ne considérerons pas les cas de diagnostic trop inhabituels, mais considérons quelques idées générales.
Vérifications de la mémoire
De toute évidence, le bon fonctionnement de la mémoire est essentiel à l'intégrité de tout système de processeur. Il n'est pas moins évident qu'une erreur critique ne vous permettra pas d'exécuter, pas seulement des diagnostics, mais l'ensemble du produit logiciel dans son ensemble (
Note du traducteur: Au fait, c'est exactement le cas que nous avons examiné dans l' article «Fake Blue Pill» ). Cependant, il existe des situations où une certaine erreur apparaît, ce qui est une source de grave préoccupation, mais n'interfère pas avec l'exécution du code. Les tests de mémoire sont un sujet assez compliqué qui dépasse le cadre de cet article, donc je ne donnerai que quelques idées générales.
Les deux erreurs les plus courantes qui se produisent dans la RAM sont:
- «Bits collants» lorsque le bit a une valeur de 0 ou 1, qui ne peut pas être modifiée;
- «Diaphonie» lorsque des bits adjacents provoquent des interférences entre eux.
Les deux erreurs peuvent être vérifiées en écrivant et en lisant tour à tour certains modèles de test dans chaque zone RAM. Certaines vérifications ne peuvent être effectuées qu'au démarrage, avant même la formation de la pile (
note du traducteur: dans l'article mentionné ci-dessus, il s'est avéré que c'était la première utilisation de la pile qui détruisait tout à la fois ). Par exemple, une vérification «unité en cours d'exécution», dans laquelle chaque bit de mémoire se voit attribuer une valeur de un, et tous les autres bits sont vérifiés pour s'assurer qu'ils sont égaux à zéro. D'autres modèles de test au niveau du bit peuvent être effectués pendant le fonctionnement, à condition que, même si la zone RAM est endommagée, la commutation de contexte ne se produira pas. L'utilisation des macros de restriction des sections critiques de
Nucleus SE NUSE_CS_Enter () et
NUSE_CS_Exit () est assez simple et évolutive.
Différents types de ROM sont également sujets à des erreurs périodiques, mais il n'y a pas beaucoup d'outils pour les vérifier. Une somme de contrôle calculée après assemblage du code pourrait être utile ici. Cette vérification peut être effectuée au démarrage et éventuellement au moment de l'exécution.
Une erreur dans la logique d'adressage de la mémoire peut affecter à la fois la ROM et la RAM. Vous pouvez développer une vérification spéciale pour cette erreur, mais elle sera très probablement détectée dans le cadre des vérifications décrites ci-dessus.
Test des périphériques
En plus du CPU, les circuits périphériques peuvent également être sujets aux erreurs. Bien sûr, cela varie considérablement d'un système à l'autre, cependant, dans la plupart des appareils, il existe différentes façons de vérifier leur intégrité à l'aide d'un logiciel de diagnostic. Par exemple, un canal de communication peut avoir un mode de vérification de bouclage dans lequel toutes les données entrant dans le canal sont immédiatement renvoyées.
Service de surveillance
Les développeurs intégrés utilisent souvent un chien de garde. Il s'agit d'un périphérique qui interrompt le CPU et attend une réponse, ou (plus préférablement) nécessite un accès périodique depuis le logiciel. Dans les deux cas, un résultat courant d'une horloge de surveillance est une réinitialisation du système.
L'utilisation efficace d'un chien de garde dans un environnement multitâche est un problème complexe. Si vous effectuez une tâche qui y accède périodiquement (horloge de surveillance), cela confirmera que cette tâche particulière fonctionne. Une solution possible pourrait être la mise en œuvre de la «tâche de répartiteur». Un exemple d'une telle tâche sera donné ci-dessous.
Vérification du dépassement de pile
Si vous n'utilisez pas le Planificateur d'exécution jusqu'à la fin, une pile sera créée pour chaque tâche dans l'application Nucleus SE. L'intégrité de ces piles est très importante, mais la quantité de RAM est susceptible d'être limitée, par conséquent, il est important de rendre la taille d'application optimale. La prévision statique des exigences pour la pile de chaque tâche est possible, mais très difficile. La pile doit être suffisamment grande pour même les fonctions les plus imbriquées, ainsi que le gestionnaire d'interruption le plus exigeant. Une approche plus simple pour résoudre ce problème serait d'utiliser des tests d'exécution exhaustifs.
De manière générale, il existe deux approches pour vérifier la pile. Si vous utilisez un débogueur de logiciel intégré sophistiqué, les limites de la pile peuvent être surveillées et toutes les violations seront détectées. L'
emplacement et la taille des piles Nucleus SE sont disponibles dans les structures de données ROM globales:
NUSE_Task_Stack_Base [] et
NUSE_Task_Stack_Size [] .
Une alternative est le test d'exécution. Une approche courante consiste à utiliser des «mots de garde» à la fin de chaque pile, généralement le premier élément de chaque zone des données de la pile. Ces mots sont initialisés avec une valeur non nulle reconnue. La tâche de service / diagnostic vérifie ensuite si ces mots ont changé et effectue les actions appropriées. Écraser le mot de sécurité ne signifie pas que la pile est pleine, mais indique que cela est sur le point de se produire. Par conséquent, le logiciel peut continuer à fonctionner, vous devrez peut-être prendre des mesures correctives ou signaler une erreur à l'utilisateur.
Tâche du superviseur
Malgré le fait que Nucleus SE ne réserve aucune des seize tâches possibles à ses propres besoins, l'utilisateur peut sélectionner une tâche pour les diagnostics. Il peut s'agir d'une tâche de faible priorité qui utilise simplement n'importe quel temps de processeur «libre» ou d'une tâche de haute priorité qui s'exécute périodiquement, en prenant une courte période de temps, ce qui garantit que les diagnostics sont effectués régulièrement.
Voici un exemple de fonctionnement d'une tâche similaire.
Les indicateurs de signal de la tâche de répartiteur sont utilisés pour suivre le fonctionnement de six tâches système critiques. Chacune de ces tâches utilise un indicateur spécifique (du bit 0 au bit 5) et doit le définir régulièrement. La tâche de répartiteur réinitialise tous les indicateurs, puis suspend son travail pendant une certaine période de temps. Lorsqu'elle reprend le travail, elle s'attend à ce que les six tâches soient "vérifiées" en définissant l'indicateur approprié, puis elle recherche une correspondance exacte avec la valeur de
b00111111 (Ă partir du fichier
nuse_binary.h ). Si tout répond aux exigences, il réinitialise les indicateurs et s'arrête à nouveau. Sinon, il appelle la routine de gestion des erreurs critiques, qui à son tour peut, par exemple, redémarrer le système.
Dans une implémentation alternative, des groupes de drapeaux d'événements pourraient être utilisés. Cela a du sens si les signaux ne sont pas utilisés ailleurs dans l'application (sinon cela entraînera une utilisation excessive de la RAM par toutes les tâches) et surtout si les indicateurs d'événement sont utilisés à d'autres fins.
Traçage et profilage
Malgré le fait que de nombreux débogueurs de logiciels embarqués modernes ont un haut degré de personnalisation et peuvent être utilisés pour fonctionner avec RTOS, le débogage d'une application multithread peut toujours être difficile. Une approche largement utilisée est le profilage post-exécution, dans lequel le code (RTOS) est mis en œuvre afin qu'un audit détaillé de son travail puisse être analysé rétrospectivement. En règle générale, la mise en œuvre d'un tel service comprend deux composants:
- Un code supplémentaire est ajouté au RTOS pour consigner les actions. En règle générale, il sera enveloppé dans des directives de préprocesseur pour utiliser la compilation conditionnelle. Ce code enregistre plusieurs octets d'informations lorsqu'un événement important se produit (par exemple, appeler une fonction API ou changer de contexte). Ces informations peuvent comprendre:
- adresse actuelle (PC);
- ID de la tâche en cours (index);
- indices d'autres objets utilisés;
- code correspondant à l'opération effectuée.
- Tâche allouée pour décharger le tampon d'informations de profil sur un stockage externe, généralement sur l'ordinateur hôte.
L'analyse des données ainsi obtenues nécessitera également un certain travail, mais ce n'est pas plus compliqué que d'utiliser une feuille de calcul Excel régulière.
Dans le prochain article, nous examinerons en détail la compatibilité de Nucleus SE et Nucleus RTOS.
À propos de l'auteur: Colin Walls travaille dans l'industrie électronique depuis plus de trente ans, consacrant la majeure partie de son temps au micrologiciel. Il est maintenant ingénieur firmware chez Mentor Embedded (une division de Mentor Graphics). Colin Walls intervient souvent lors de conférences et séminaires, auteur de nombreux articles techniques et de deux livres sur le firmware. Vit au Royaume-Uni.
Blog professionnel
de Colin , e-mail: colin_walls@mentor.com.