Compilation d'une application pour une utilisation dans des environnements hautement radioactifs


1456

Nous compilons une application C / C ++ embarquée qui est déployée dans un appareil blindé dans un environnement bombardé de rayonnements ionisants . Nous utilisons GCC et la compilation croisée pour ARM. Une fois déployée, notre application génère des données erronées et se bloque plus souvent que nous le souhaiterions. Le matériel est conçu pour cet environnement et notre application fonctionne sur cette plate-forme depuis plusieurs années.

Y a-t-il des changements que nous pouvons apporter à notre code, ou des améliorations au moment de la compilation qui peuvent être apportées pour identifier / corriger les erreurs logicielles et la corruption de mémoire causées par des perturbations d'un seul événement ? D'autres développeurs ont-ils réussi à réduire les effets néfastes des erreurs logicielles sur une application de longue durée?


186
Les valeurs de la mémoire changent-elles ou les valeurs du processeur changent-elles? Si le matériel est conçu pour l'environnement, le logiciel doit fonctionner comme s'il fonctionnait dans un environnement non radioactif.
Thomas Matthews

3
Si possible, vous devez configurer un système de journalisation qui stocke les événements dans une mémoire non volatile résistante aux radiations. Stockez suffisamment d'informations pour pouvoir suivre l'événement et trouver facilement la cause première.
Thomas Matthews

2
@Thomas Matthews Toute la mémoire a un taux d'erreur FIT, et les fabricants de matériel font beaucoup de promesses. La plupart des problèmes sont probablement dus à la modification de la RAM par les SEU lors de l'exécution.
tour le

9
Il s'agit d'une solution matérielle / logicielle combinée, mais je sais que Texas Instruments (et probablement d'autres) fabrique des puces intégrées pour des applications critiques pour la sécurité qui se composent de deux cœurs en double, fonctionnant en mode verrouillé, un demi-cycle d'horloge hors phase. Il existe des interruptions spéciales et des actions de réinitialisation qui sont prises lorsque le matériel détecte quelque chose de différent entre les cœurs, afin que vous puissiez récupérer des erreurs. Je crois que TI les qualifie de processeurs de sécurité "Hercules".
mbrig

5
Moteurs robustes redondants, certains engrenages, arbres et cliquets! Remplacez-les annuellement ou plus souvent selon les débits de dose. Non vraiment, ma première question avec ce genre de problèmes a toujours été: avez-vous vraiment besoin de tant de logiciels là-dedans? Soyez aussi analogique que possible.
jwdonahue

Réponses:


814

Travaillant depuis environ 4-5 ans avec le développement de logiciels / micrologiciels et les tests d'environnement de satellites miniaturisés *, je voudrais partager mon expérience ici.

* ( les satellites miniaturisés sont beaucoup plus sujets aux perturbations d'un seul événement que les satellites plus gros en raison de ses tailles relativement petites et limitées pour ses composants électroniques )

Pour être très concis et direct: il n'y a pas de mécanisme pour récupérer d'une situation détectable et erronée par le logiciel / micrologiciel lui-même sans , au moins, une copie de la version de travail minimale du logiciel / micrologiciel quelque part à des fins de récupération - et avec le support matériel la récupération (fonctionnelle).

Maintenant, cette situation est normalement gérée au niveau matériel et logiciel. Ici, comme vous le demandez, je partagerai ce que nous pouvons faire au niveau logiciel.

  1. ... des fins de récupération ... . Permet de mettre à jour / recompiler / reflasher votre logiciel / firmware dans un environnement réel. Il s'agit d'une fonctionnalité presque indispensable pour tout logiciel / micrologiciel dans un environnement hautement ionisé. Sans cela, vous pourriez avoir autant de logiciels / matériels redondants que vous le souhaitez, mais à un moment donné, ils vont tous exploser. Alors, préparez cette fonctionnalité!

  2. ... version de travail minimale ... Avoir réactif, plusieurs copies, la version minimale du logiciel / firmware dans votre code. C'est comme le mode sans échec sous Windows. Au lieu d'avoir une seule version entièrement fonctionnelle de votre logiciel, ayez plusieurs copies de la version minimale de votre logiciel / firmware. La copie minimale aura généralement une taille beaucoup moins importante que la copie complète et n'aura presque toujours que les deux ou trois fonctionnalités suivantes:

    1. capable d'écouter les commandes d'un système externe,
    2. capable de mettre à jour le logiciel / firmware actuel,
    3. capable de surveiller les données d'entretien de base de l'opération.
  3. ... copier ... quelque part ... Disposer d'un logiciel / firmware redondant quelque part.

    1. Vous pouvez, avec ou sans matériel redondant, essayer d'avoir un logiciel / firmware redondant dans votre ARM uC. Cela se fait normalement en ayant deux ou plusieurs logiciels / micrologiciels identiques dans des adresses distinctes qui s'envoient des battements de cœur - mais un seul sera actif à la fois. Si un ou plusieurs logiciels / micrologiciels ne répondent pas, passez à l'autre logiciel / micrologiciel. L'avantage d'utiliser cette approche est que nous pouvons avoir un remplacement fonctionnel immédiatement après qu'une erreur se soit produite - sans aucun contact avec le système / la partie externe qui est responsable de détecter et de réparer l'erreur (dans le cas d'un satellite, il s'agit généralement du Centre de contrôle de mission ( MCC)).

      À strictement parler, sans matériel redondant, l'inconvénient est que vous ne pouvez pas éliminer tous les points de défaillance. À tout le moins, vous aurez toujours un seul point de défaillance, qui est le commutateur lui-même (ou souvent le début du code). Néanmoins, pour un appareil limité par sa taille dans un environnement hautement ionisé (comme les satellites pico / femto), la réduction du point de défaillance unique à un point sans matériel supplémentaire vaudra la peine d'être envisagée. De plus, le morceau de code pour la commutation serait certainement beaucoup moins que le code pour l'ensemble du programme - réduisant considérablement le risque d'y inclure un événement unique.

    2. Mais si vous ne le faites pas, vous devez avoir au moins une copie dans votre système externe qui peut entrer en contact avec l'appareil et mettre à jour le logiciel / firmware (dans le cas du satellite, c'est à nouveau le centre de contrôle de mission).

    3. Vous pouvez également avoir la copie dans votre mémoire de stockage permanente de votre appareil qui peut être déclenchée pour restaurer le logiciel / firmware du système en cours d'exécution
  4. ... situation erronée détectable .. L'erreur doit être détectable , généralement par le circuit de correction / détection d'erreurs matérielles ou par un petit code pour la correction / détection d'erreurs. Il est préférable de mettre ce code petit, multiple et indépendant du logiciel / firmware principal. Sa tâche principale est uniquement de vérifier / corriger. Si le circuit matériel / firmware est fiable(comme il est plus durci par les radiations que les autres - ou ayant plusieurs circuits / logiques), alors vous pourriez envisager de corriger les erreurs avec. Mais si ce n'est pas le cas, il vaut mieux en faire une détection d'erreur. La correction peut être effectuée par un système / appareil externe. Pour la correction d'erreurs, vous pourriez envisager d'utiliser un algorithme de correction d'erreurs de base comme Hamming / Golay23, car ils peuvent être mis en œuvre plus facilement à la fois dans le circuit / logiciel. Mais cela dépend en fin de compte des capacités de votre équipe. Pour la détection d'erreurs, normalement CRC est utilisé.

  5. ... matériel prenant en charge la récupération Maintenant, vient à l'aspect le plus difficile sur cette question. En fin de compte, la récupération nécessite que le matériel qui est responsable de la récupération soit au moins fonctionnel. Si le matériel est définitivement cassé (se produit normalement après que sa dose totale d'ionisation atteigne un certain niveau), il n'y a (malheureusement) aucun moyen pour le logiciel d'aider à la récupération. Ainsi, le matériel est à juste titre la préoccupation la plus importante pour un appareil exposé à un niveau de rayonnement élevé (tel qu'un satellite).

En plus de la suggestion ci-dessus d'anticiper l'erreur du firmware due à un événement unique, je voudrais également vous suggérer d'avoir:

  1. Algorithme de détection et / ou de correction d'erreur dans le protocole de communication inter-sous-système. C'est un autre presque indispensable pour éviter les signaux incomplets / incorrects reçus d'un autre système

  2. Filtrez votre lecture ADC. N'utilisez pas la lecture ADC directement. Filtrez-le par filtre médian, filtre moyen ou tout autre filtre - ne faites jamais confiance à une seule valeur de lecture. Échantillon plus, pas moins - raisonnablement.


401

La NASA a un document sur les logiciels durcis aux radiations . Il décrit trois tâches principales:

  1. Surveillance régulière de la mémoire pour les erreurs, puis nettoyage de ces erreurs,
  2. des mécanismes de récupération d'erreur robustes, et
  3. la possibilité de reconfigurer si quelque chose ne fonctionne plus.

Notez que le taux de balayage de la mémoire doit être suffisamment fréquent pour que des erreurs multi-bits se produisent rarement, car la plupart de la mémoire ECC peut récupérer des erreurs mono-bit, et non des erreurs multi-bits.

Une récupération d'erreur robuste comprend le transfert du flux de contrôle (généralement le redémarrage d'un processus à un point avant l'erreur), la libération des ressources et la restauration des données.

Leur principale recommandation pour la restauration des données est d'éviter d'en avoir besoin, en traitant les données intermédiaires comme temporaires, de sorte que le redémarrage avant l'erreur ramène également les données à un état fiable. Cela ressemble au concept de "transactions" dans les bases de données.

Ils discutent de techniques particulièrement adaptées aux langages orientés objet tels que C ++. Par exemple

  1. ECC basés sur logiciel pour les objets de mémoire contigus
  2. Programmation par contrat : vérification des conditions préalables et postconditions, puis vérification de l'objet pour vérifier qu'il est toujours dans un état valide.

Et, il se trouve que la NASA a utilisé C ++ pour des projets majeurs tels que Mars Rover .

L'abstraction et l'encapsulation des classes C ++ ont permis un développement et des tests rapides entre plusieurs projets et développeurs.

Ils ont évité certaines fonctionnalités C ++ qui pouvaient créer des problèmes:

  1. Exceptions
  2. Modèles
  3. Iostream (pas de console)
  4. Héritage multiple
  5. Surcharge de l'opérateur (autre que newetdelete )
  6. Allocation dynamique (utilisé un pool de mémoire dédié et un placement newpour éviter la possibilité de corruption du tas du système).

28
Cela ressemble en fait à quelque chose qu'un langage pur serait bon. Étant donné que les valeurs ne changent jamais, si elles sont endommagées, vous pouvez simplement revenir à la définition d'origine (ce qu'elle est censée être), et vous ne ferez pas accidentellement la même chose deux fois (en raison du manque d'effets secondaires).
PyRulez

20
RAII est une mauvaise idée, car vous ne pouvez pas compter sur son bon fonctionnement ou même pas du tout. Cela pourrait endommager vos données de manière aléatoire, etc. Vous voulez vraiment autant d'immuabilité que possible, et des mécanismes de correction d'erreurs en plus. Il est beaucoup plus facile de jeter des objets cassés que d'essayer de les réparer d'une manière ou d'une autre (comment en savez-vous exactement assez pour revenir à l'ancien état correct?). Vous voudrez probablement utiliser un langage plutôt stupide pour cela, cependant - les optimisations pourraient faire plus de mal qu'elles ne l'aident.
Luaan

67
@PyRulez: les langues pures sont une abstraction, le matériel n'est pas pur. Les compilateurs sont assez bons pour cacher la différence. Si votre programme a une valeur qu'il ne devrait logiquement plus utiliser après l'étape X, le compilateur peut la remplacer par une valeur calculée à l'étape X + 1. Mais cela signifie que vous ne pouvez pas revenir en arrière. Plus formellement, les états possibles d'un programme dans un langage pur forment un graphe acyclique, ce qui signifie que deux états sont équivalents et peuvent être fusionnés lorsque les états accessibles à partir des deux sont équivalents. Cette fusion détruit la différence de chemins menant à ces États.
MSalters du

2
@Vorac - Selon la présentation, le problème avec les modèles C ++ est le gonflement du code.
2019 à 9h18

3
@DeerSpotter Le problème exact est bien plus important que cela. L'ionisation peut endommager des morceaux de votre programme d'observation en cours d'exécution. Ensuite, vous aurez besoin d'un observateur d'un observateur, puis - observateur d'un observateur d'un observateur et ainsi de suite ...
Agnius Vasiliauskas

116

Voici quelques réflexions et idées:

Utilisez la ROM de manière plus créative.

Stockez tout ce que vous pouvez dans la ROM. Au lieu de calculer des choses, stockez les tables de recherche dans la ROM. (Assurez-vous que votre compilateur génère vos tables de recherche dans la section en lecture seule! Imprimez les adresses mémoire au moment de l'exécution pour vérifier!) Stockez votre table de vecteur d'interruption dans la ROM. Bien sûr, exécutez quelques tests pour voir la fiabilité de votre ROM par rapport à votre RAM.

Utilisez votre meilleure RAM pour la pile.

Les SEU de la pile sont probablement la source la plus probable de plantages, car c'est là que vivent généralement des éléments comme les variables d'index, les variables d'état, les adresses de retour et les pointeurs de diverses sortes.

Implémentez des routines d'horloge de minuterie et de surveillance.

Vous pouvez exécuter une routine de "vérification de l'état d'esprit" à chaque tic du minuteur, ainsi qu'une routine de surveillance pour gérer le verrouillage du système. Votre code principal peut également incrémenter périodiquement un compteur pour indiquer la progression, et la routine de vérification de l'intégrité peut garantir que cela s'est produit.

Implémentez des codes de correction d'erreur dans le logiciel.

Vous pouvez ajouter une redondance à vos données pour pouvoir détecter et / ou corriger les erreurs. Cela augmentera le temps de traitement, laissant potentiellement le processeur exposé aux rayonnements pendant plus longtemps, augmentant ainsi le risque d'erreurs, vous devez donc envisager le compromis.

N'oubliez pas les caches.

Vérifiez la taille de vos caches CPU. Les données auxquelles vous avez accédé ou modifié récemment seront probablement dans un cache. Je pense que vous pouvez désactiver au moins certains des caches (à un coût élevé de performance); vous devriez essayer ceci pour voir dans quelle mesure les caches sont sensibles aux SEU. Si les caches sont plus robustes que la RAM, vous pouvez régulièrement lire et réécrire les données critiques pour vous assurer qu'elles restent dans le cache et ramener la RAM en ligne.

Utilisez intelligemment les gestionnaires de défauts de page.

Si vous marquez une page mémoire comme non présente, le CPU émettra une erreur de page lorsque vous essayez d'y accéder. Vous pouvez créer un gestionnaire de défauts de page qui effectue une vérification avant de répondre à la demande de lecture. (Les systèmes d'exploitation PC l'utilisent pour charger de manière transparente les pages qui ont été échangées sur le disque.)

Utilisez le langage d'assemblage pour les choses critiques (qui pourraient être tout).

Avec le langage d'assemblage, vous savez ce qui est dans les registres et ce qui est dans la RAM; tu sais quelles tables RAM spéciales le processeur utilise, et vous pouvez concevoir les choses de manière détournée pour limiter vos risques.

Utilisation objdump pour regarder réellement le langage d'assemblage généré et déterminer la quantité de code que chacune de vos routines prend.

Si vous utilisez un gros système d'exploitation comme Linux, vous demandez des ennuis; il y a tellement de complexité et tant de choses qui tournent mal.

N'oubliez pas que c'est un jeu de probabilités.

Un commentateur a dit

Chaque routine que vous écrivez pour détecter les erreurs sera sujette à l'échec de la même cause.

Bien que cela soit vrai, les chances d'erreurs dans les (disons) 100 octets de code et de données nécessaires pour qu'un programme de vérification fonctionne correctement sont beaucoup plus faibles que les chances d'erreurs ailleurs. Si votre ROM est assez fiable et que presque tout le code / les données sont réellement dans la ROM, alors vos chances sont encore meilleures.

Utilisez du matériel redondant.

Utilisez 2 configurations matérielles identiques ou plus avec un code identique. Si les résultats diffèrent, une réinitialisation doit être déclenchée. Avec 3 appareils ou plus, vous pouvez utiliser un système de "vote" pour essayer d'identifier celui qui a été compromis.


14
De nos jours, ECC est disponible via le matériel, ce qui économise le temps de traitement. La première étape serait de choisir un microcontrôleur avec ECC intégré.
Lundin

23
Quelque part dans le fond de mon esprit se trouve une référence à l'avionique (peut-être la navette spatiale?) Du matériel de vol où l'architecture redondante a été explicitement conçue pour ne pas être identique (et par différentes équipes). Cela atténue la possibilité d'une erreur systémique dans la conception matérielle / logicielle, ce qui réduit la possibilité que tous les systèmes de vote se bloquent en même temps lorsqu'ils sont confrontés aux mêmes entrées.
Peter M

8
@PeterM: AFAIK qui est également revendiqué pour le logiciel de vol du Boeing 777: trois versions par trois équipes dans trois langages de programmation.
Rétablir Monica - M. Schröder

7
@DanEsparza RAM possède généralement un condensateur (DRAM) ou quelques transistors en rétroaction (SRAM) stockant des données. Un événement de rayonnement peut faussement charger / décharger le condensateur ou modifier le signal dans la boucle de rétroaction. La ROM n'a généralement pas besoin d'être écrite (au moins sans circonstances spéciales et / ou tensions plus élevées) et peut donc être intrinsèquement plus stable au niveau physique.
nanofarad

7
@DanEsparza: Il existe plusieurs types de mémoires ROM. Si la "ROM" est émulée par ex. Eeprom ou flash en lecture seule à 5 V mais programmable à 10 V, alors en fait cette "ROM" est toujours sujette à l'ionisation. Peut-être un peu moins que les autres. Cependant, il y a de bonnes choses hardcore comme la ROM de masque ou la PROM basée sur un fusible qui, je pense, auraient besoin d'une quantité de rayonnement vraiment sérieuse pour commencer à échouer. Je ne sais cependant pas s'il en existe encore.
quetzalcoatl

105

Vous pouvez également être intéressé par la riche littérature sur le sujet de la tolérance algorithmique aux pannes. Cela inclut l'ancienne affectation: Écrivez un tri qui trie correctement son entrée lorsqu'un nombre constant de comparaisons échoue (ou, la version légèrement plus mauvaise, lorsque le nombre asymptotique de comparaisons échouées évolue comme log(n)pour les ncomparaisons).

Un article pour commencer à lire est l'article de Huang et Abraham de 1984 " Algorithm-Based Fault Tolerance for Matrix Operations ". Leur idée est vaguement similaire au calcul crypté homomorphe (mais ce n'est pas vraiment la même chose, car ils tentent de détecter / corriger les erreurs au niveau de l'opération).

Un descendant plus récent de cet article est Bosilca, Delmas, Dongarra, et Langou " La tolérance aux pannes basée sur l'algorithme appliquée au calcul haute performance ".


5
J'aime vraiment ta réponse. Il s'agit d'une approche logicielle plus générique de l'intégrité des données, et une solution de tolérance aux pannes basée sur l'algorithme sera utilisée dans notre produit final. Merci!
tour

41

L'écriture de code pour les environnements radioactifs n'est pas vraiment différente de l'écriture de code pour toute application critique.

En plus de ce qui a déjà été mentionné, voici quelques conseils divers:

  • Utilisez les mesures de sécurité quotidiennes "pain et beurre" qui devraient être présentes sur tout système embarqué semi-professionnel: chien de garde interne, détecteur de basse tension interne, moniteur d'horloge interne. Ces choses ne devraient même pas avoir besoin d'être mentionnées en 2016 et elles sont standard sur à peu près tous les microcontrôleurs modernes.
  • Si vous avez un MCU de sécurité et / ou orienté automobile, il aura certaines fonctionnalités de surveillance, telles qu'une fenêtre de temps donnée, à l'intérieur de laquelle vous devez actualiser le chien de garde. Ceci est préférable si vous avez un système en temps réel critique pour la mission.
  • En général, utilisez un MCU adapté à ce type de systèmes, et non des peluches génériques courantes que vous avez reçues dans un paquet de corn flakes. Aujourd'hui, presque tous les fabricants de microcontrôleurs ont des microcontrôleurs spécialisés conçus pour les applications de sécurité (TI, Freescale, Renesas, ST, Infineon, etc.). Ceux-ci ont de nombreuses fonctionnalités de sécurité intégrées, y compris des cœurs de verrouillage: ce qui signifie qu'il y a 2 cœurs de processeur exécutant le même code, et ils doivent être en accord les uns avec les autres.
  • IMPORTANT: vous devez garantir l'intégrité des registres MCU internes. Tous les registres de contrôle et d'état des périphériques matériels accessibles en écriture peuvent se trouver dans la mémoire RAM et sont donc vulnérables.

    Pour vous protéger contre les corruptions de registre, choisissez de préférence un microcontrôleur avec des fonctionnalités intégrées de "réécriture unique" des registres. De plus, vous devez stocker les valeurs par défaut de tous les registres matériels dans NVM et copier ces valeurs dans vos registres à intervalles réguliers. Vous pouvez garantir l'intégrité des variables importantes de la même manière.

    Remarque: utilisez toujours une programmation défensive. Cela signifie que vous devez configurer tous les registres dans le MCU et pas seulement ceux utilisés par l'application. Vous ne voulez pas qu'un périphérique matériel aléatoire se réveille soudainement.

  • Il existe toutes sortes de méthodes pour vérifier les erreurs dans la RAM ou la NVM: sommes de contrôle, "schémas de marche", logiciel ECC, etc. contrôles similaires. Parce que faire cela dans un logiciel est complexe, et la vérification des erreurs en elle-même pourrait donc introduire des bogues et des problèmes inattendus.

  • Utilisez la redondance. Vous pouvez stocker la mémoire volatile et non volatile dans deux segments "miroir" identiques, qui doivent toujours être équivalents. Chaque segment peut avoir une somme de contrôle CRC attachée.
  • Évitez d'utiliser des mémoires externes en dehors du MCU.
  • Implémentez une routine de service d'interruption par défaut / gestionnaire d'exceptions par défaut pour toutes les interruptions / exceptions possibles. Même ceux que vous n'utilisez pas. La routine par défaut ne doit rien faire sauf arrêter sa propre source d'interruption.
  • Comprendre et adopter le concept de programmation défensive. Cela signifie que votre programme doit gérer tous les cas possibles, même ceux qui ne peuvent pas se produire en théorie. Exemples .

    Un micrologiciel critique de haute qualité détecte autant d'erreurs que possible, puis les ignore de manière sûre.

  • N'écrivez jamais de programmes qui reposent sur un comportement mal spécifié. Il est probable qu'un tel comportement puisse changer radicalement avec des modifications matérielles inattendues causées par les radiations ou les EMI. La meilleure façon de vous assurer que votre programme est exempt de ces conneries est d'utiliser une norme de codage comme MISRA, avec un outil d'analyse statique. Cela aidera également à la programmation défensive et à l'élimination des bogues (pourquoi ne voudriez-vous pas détecter des bogues dans tout type d'application?).
  • IMPORTANT: n'implémentez aucune dépendance des valeurs par défaut des variables de durée de stockage statique. Autrement dit, ne faites pas confiance au contenu par défaut du .dataou.bss . Il pourrait y avoir un certain temps entre le point d'initialisation et le moment où la variable est réellement utilisée, il aurait pu y avoir beaucoup de temps pour que la RAM soit corrompue. Au lieu de cela, écrivez le programme de sorte que toutes ces variables soient définies à partir de NVM au moment de l'exécution, juste avant le moment où une telle variable est utilisée pour la première fois.

    En pratique, cela signifie que si une variable est déclarée à la portée du fichier ou en tant que static, vous ne devez jamais utiliser= pour l'initialiser (ou vous pourriez, mais c'est inutile, car vous ne pouvez pas compter sur la valeur de toute façon). Réglez-le toujours au moment de l'exécution, juste avant utilisation. S'il est possible de mettre à jour ces variables à plusieurs reprises à partir de NVM, faites-le.

    De même en C ++, ne vous fiez pas aux constructeurs pour les variables de durée de stockage statique. Demandez au (x) constructeur (s) d'appeler une routine de "configuration" publique, que vous pouvez également appeler plus tard au moment de l'exécution, directement à partir de l'application appelante.

    Si possible, supprimez entièrement le code de démarrage "copie vers le bas" qui initialise .dataet .bss(et appelle les constructeurs C ++), afin que vous obteniez des erreurs de l'éditeur de liens si vous écrivez du code en vous appuyant sur de tels codes. De nombreux compilateurs ont la possibilité de sauter ceci, généralement appelé "démarrage minimal / rapide" ou similaire.

    Cela signifie que toutes les bibliothèques externes doivent être vérifiées afin qu'elles ne contiennent pas une telle dépendance.

  • Implémentez et définissez un état sûr pour le programme, où vous reviendrez en cas d'erreurs critiques.

  • La mise en œuvre d'un système de rapport d'erreur / journal des erreurs est toujours utile.

Une façon de traiter les booléens corrompus (comme dans votre lien d'exemple) pourrait être de rendre TRUEégal à 0xffffffffutiliser ensuite POPCNTavec un seuil.
wizzwizz4

@ wizzwizz4 Étant donné que la valeur 0xff est la valeur par défaut de la cellule flash non programmée, cela ressemble à une mauvaise idée.
Lundin

%01010101010101010101010101010101, XOR puis POPCNT?
wizzwizz4

1
@ wizzwizz4 Ou simplement la valeur 0x1, comme requis par la norme C.
Lundin

1
@ wizzwizz4 Pourquoi vous utilisez certaines ou toutes les méthodes mentionnées ci-dessus (ECC, CRC, etc.). Sinon, le rayon cosmique peut également retourner un seul bit dans votre .textsection, en changeant un code op ou similaire.
Lundin

34

Il peut être possible d'utiliser C pour écrire des programmes qui se comportent de manière robuste dans de tels environnements, mais uniquement si la plupart des formes d'optimisation du compilateur sont désactivées. Les compilateurs d'optimisation sont conçus pour remplacer de nombreux modèles de codage apparemment redondants par des modèles "plus efficaces", et peuvent ne pas avoir d'indication que la raison pour laquelle le programmeur teste x==42lorsque le compilateur sait qu'il n'y a aucun moyen de xcontenir autre chose est parce que le programmeur veut empêcher l'exécution de certains codes avec la xdétention d'une autre valeur - même dans les cas où la seule façon de conserver cette valeur serait que le système reçoive une sorte de problème électrique.

Déclarer des variables volatileest souvent utile, mais peut ne pas être une panacée. Il est particulièrement important de noter que le codage sûr nécessite souvent que les opérations dangereuses aient des verrouillages matériels qui nécessitent plusieurs étapes pour s'activer et que le code soit écrit à l'aide du modèle:

... code that checks system state
if (system_state_favors_activation)
{
  prepare_for_activation();
  ... code that checks system state again
  if (system_state_is_valid)
  {
    if (system_state_favors_activation)
      trigger_activation();
  }
  else
    perform_safety_shutdown_and_restart();
}
cancel_preparations();

Si un compilateur traduit le code de façon relativement littérale, et si toutes les vérifications de l'état du système sont répétées après le prepare_for_activation(), le système peut être robuste contre presque tous les événements de glitch simple plausibles, même ceux qui corrompraient arbitrairement le compteur et la pile du programme. Si un pépin se produit juste après un appel à prepare_for_activation(), cela impliquerait que l'activation aurait été appropriée (car il n'y a aucune autre raison prepare_for_activation()aurait été appelée avant le pépin). Si le problème fait que le code atteint de prepare_for_activation()façon inappropriée, mais qu'il n'y a aucun événement de problème subséquent, il n'y aurait aucun moyen pour le code d'atteindre ultérieurement trigger_activation()sans avoir passé la vérification de validation ou appelé d'abord cancel_preparations [si la pile est défectueuse, l'exécution pourrait passer à un endroit juste avanttrigger_activation()après le contexte qui a appelé prepare_for_activation()renvoie, mais l'appel à cancel_preparations()aurait eu lieu entre les appels à , rendant ainsi ce dernier appel inoffensif.prepare_for_activation() ettrigger_activation()

Un tel code peut être sûr en C traditionnel, mais pas avec les compilateurs C modernes. De tels compilateurs peuvent être très dangereux dans ce type d'environnement car agressifs, ils s'efforcent d'inclure uniquement du code qui sera pertinent dans des situations qui pourraient survenir via un mécanisme bien défini et dont les conséquences résultantes seraient également bien définies. Un code dont le but serait de détecter et de nettoyer les pannes peut, dans certains cas, finir par aggraver les choses. Si le compilateur détermine que la tentative de récupération invoquerait dans certains cas un comportement indéfini, il peut en déduire que les conditions qui nécessiteraient une telle récupération dans de tels cas ne peuvent pas se produire, éliminant ainsi le code qui les aurait vérifiées.


6
De manière réaliste, combien de compilateurs modernes existe-t-il qui n'offrent pas -O0ou un commutateur équivalent? GCC fera beaucoup de choses étranges si vous lui donnez la permission , mais si vous lui demandez de ne pas les faire, il est généralement aussi assez littéral.
Leushenko

24
Désolé, mais cette idée est fondamentalement dangereuse. La désactivation des optimisations produit un programme plus lent. Ou, en d'autres termes, vous avez besoin d'un processeur plus rapide. En fait, les CPU plus rapides sont plus rapides car les charges sur leurs grilles de transistor sont plus petites. Cela les rend beaucoup plus sensibles aux radiations. La meilleure stratégie consiste à utiliser une grosse puce lente où un seul photon est beaucoup moins susceptible de renverser un peu et de gagner en vitesse avec -O2.
MSalters du

27
Une deuxième raison pour laquelle -O0une mauvaise idée est qu'elle émet des instructions beaucoup plus inutiles. Exemple: un appel non en ligne contient des instructions pour sauvegarder les registres, passer l'appel, restaurer les registres. Tout cela peut échouer. Une instruction qui n'est pas là ne peut pas échouer.
MSalters du

15
Encore une autre raison pour laquelle -O0c'est une mauvaise idée: elle a tendance à stocker des variables en mémoire plutôt que dans un registre. Maintenant, il n'est pas certain que la mémoire soit plus sensible aux SEU, mais les données en vol sont plus sensibles que les données au repos. Les mouvements de données inutiles doivent être évités et -O2y contribuent.
MSalters du

9
@MSalters: L'important n'est pas que les données soient à l'abri des perturbations, mais plutôt que le système soit capable de gérer les perturbations de manière à répondre aux exigences. Sur de nombreux compilateurs, la désactivation de toutes les optimisations génère un code qui effectue un nombre excessif de mouvements de registre à registre, ce qui est mauvais, mais le stockage de variables en mémoire est plus sûr du point de vue de la récupération que de les conserver dans les registres. Si l'on a deux variables en mémoire qui sont censées obéir à une condition (par exemple v1=v2+0xCAFEBABEet toutes les mises à jour des deux variables sont effectuées ...
supercat

29

C'est un sujet extrêmement large. Fondamentalement, vous ne pouvez pas vraiment récupérer d'une corruption de mémoire, mais vous pouvez au moins essayer d' échouer rapidement . Voici quelques techniques que vous pourriez utiliser:

  • données constantes de somme de contrôle . Si vous avez des données de configuration qui restent constantes pendant longtemps (y compris les registres matériels que vous avez configurés), calculez sa somme de contrôle à l'initialisation et vérifiez-la périodiquement. Lorsque vous voyez un décalage, il est temps de réinitialiser ou de réinitialiser.

  • stocker les variables avec redondance . Si vous avez une variable importante x, écrire sa valeur x1, x2et x3et que vous lisez (x1 == x2) ? x2 : x3.

  • mettre en œuvre la surveillance du déroulement du programme . XOR un indicateur global avec une valeur unique dans les fonctions / branches importantes appelées depuis la boucle principale. L'exécution du programme dans un environnement sans rayonnement avec une couverture de test proche de 100% devrait vous donner la liste des valeurs acceptables du drapeau à la fin du cycle. Réinitialisez si vous voyez des écarts.

  • surveiller le pointeur de pile . Au début de la boucle principale, comparez le pointeur de pile avec sa valeur attendue. Remise à zéro en cas d'écart.


27

Ce qui pourrait vous aider, c'est un chien de garde . Les chiens de garde ont été largement utilisés en informatique industrielle dans les années 1980. Les pannes matérielles étaient alors beaucoup plus courantes - une autre réponse se réfère également à cette période.

Un chien de garde est une fonction combinée matériel / logiciel. Le matériel est un simple compteur qui décompte d'un nombre (disons 1023) à zéro. TTL ou une autre logique pourrait être utilisée.

Le logiciel a été conçu de telle sorte qu'une routine surveille le bon fonctionnement de tous les systèmes essentiels. Si cette routine se termine correctement = trouve que l'ordinateur fonctionne correctement, il remet le compteur à 1023.

La conception globale est telle que, dans des circonstances normales, le logiciel empêche que le compteur matériel atteigne zéro. Si le compteur atteint zéro, le matériel du compteur effectue sa tâche unique et réinitialise l'ensemble du système. Du point de vue du compteur, zéro est égal à 1024 et le compteur continue à nouveau de décompter.

Ce chien de garde garantit que l'ordinateur connecté est redémarré dans de très nombreux cas d'échec. Je dois admettre que je ne connais pas le matériel capable d'exécuter une telle fonction sur les ordinateurs d'aujourd'hui. Les interfaces avec le matériel externe sont désormais beaucoup plus complexes qu'auparavant.

Un inconvénient inhérent du chien de garde est que le système n'est pas disponible à partir du moment où il échoue jusqu'à ce que le compteur du chien de garde atteigne zéro + heure de redémarrage. Bien que ce délai soit généralement beaucoup plus court que toute intervention externe ou humaine, l'équipement pris en charge devra pouvoir fonctionner sans contrôle informatique pendant cette période.


9
Les compteurs binaires de surveillance avec circuits intégrés TTL standard sont en effet une solution des années 80. Ne fais pas ça. Aujourd'hui, il n'existe pas un seul microcontrôleur sur le marché sans circuits de surveillance intégrés. Tout ce que vous devez vérifier est de savoir si le chien de garde intégré a une source d'horloge individuelle (bonne, très probablement le cas) ou s'il hérite de son horloge de l'horloge système (mauvaise).
Lundin

1
Ou implémentez le chien de garde dans un FPGA: ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20130013486.pdf
nos

2
Accessoirement encore largement utilisé dans les processeurs embarqués.
Graham

5
@Peter Mortensen Veuillez arrêter votre série de modifications sur chaque réponse à cette question. Ce n'est pas Wikipédia, et ces liens ne sont pas utiles (et je suis sûr que tout le monde sait comment trouver Wikipédia de toute façon ...). Beaucoup de vos modifications sont incorrectes car vous ne connaissez pas le sujet. Je fais des annulations sur vos modifications incorrectes lorsque je les rencontre. Vous ne tournez pas ce fil mieux, mais pire. Arrêtez l'édition.
Lundin

Jack Ganssle a un bon article sur les chiens de garde: ganssle.com/watchdogs.htm
Igor Skochinsky

23

Cette réponse suppose que vous êtes soucieux d'avoir un système qui fonctionne correctement, en plus d'avoir un système à coût minimum ou rapide; la plupart des gens qui jouent avec des objets radioactifs apprécient l'exactitude / la sécurité par rapport à la vitesse / au coût

Plusieurs personnes ont suggéré des modifications matérielles que vous pouvez apporter (très bien - il y a déjà beaucoup de bonnes choses dans les réponses et je n'ai pas l'intention de les répéter toutes), et d'autres ont suggéré la redondance (excellente en principe), mais je ne pense pas quelqu'un a suggéré comment cette redondance pourrait fonctionner dans la pratique. Comment échouez-vous? Comment savoir quand quelque chose a «mal tourné»? De nombreuses technologies fonctionnent sur la base que tout fonctionnera, et l'échec est donc une chose délicate à gérer. Cependant, certaines technologies informatiques distribuées conçues pour l'évolutivité s'attendent à une défaillance (après tout, avec une ampleur suffisante, la défaillance d'un nœud parmi plusieurs est inévitable avec n'importe quel MTBF pour un seul nœud); vous pouvez exploiter cela pour votre environnement.

Voici quelques idées:

  • Assurez-vous que votre matériel entier est répliqué nfois (où nest supérieur à 2, et de préférence impair), et que chaque élément matériel peut communiquer avec l'autre élément matériel. Ethernet est un moyen évident de le faire, mais il existe de nombreuses autres voies beaucoup plus simples qui offriraient une meilleure protection (par exemple CAN). Minimisez les composants communs (même les blocs d'alimentation). Cela peut signifier par exemple l'échantillonnage des entrées ADC à plusieurs endroits.

  • Assurez-vous que l'état de votre application est en un seul endroit, par exemple dans une machine à états finis. Cela peut être entièrement basé sur la RAM, mais n'empêche pas un stockage stable. Il sera ainsi stocké en plusieurs endroits.

  • Adoptez un protocole de quorum pour les changements d'état. Voir RAFT par exemple. Comme vous travaillez en C ++, il existe des bibliothèques bien connues pour cela. Les modifications apportées au FSM ne seraient apportées que si la majorité des nœuds sont d'accord. Utilisez une bonne bibliothèque connue pour la pile de protocoles et le protocole de quorum plutôt que d'en lancer une vous-même, sinon tout votre bon travail sur la redondance sera gaspillé lorsque le protocole de quorum raccroche.

  • Assurez-vous que la somme de contrôle (par exemple CRC / SHA) de votre FSM et stockez le CRC / SHA dans le FSM lui-même (ainsi que la transmission dans le message et la somme de contrôle des messages eux-mêmes). Demandez aux nœuds de vérifier régulièrement leur FSM par rapport à ces sommes de contrôle, les messages entrants de somme de contrôle et vérifiez que leur somme de contrôle correspond à la somme de contrôle du quorum.

  • Créez autant de vérifications internes que possible dans votre système, ce qui permet aux nœuds qui détectent leur propre échec de redémarrer (c'est mieux que de continuer à travailler à moitié si vous avez suffisamment de nœuds). Essayez de les laisser se retirer proprement du quorum lors du redémarrage au cas où ils ne reviendraient pas. Au redémarrage, demandez-leur de contrôler l'image du logiciel (et tout ce qu'ils chargent) et de faire un test de RAM complet avant de se réintroduire dans le quorum.

  • Utilisez du matériel pour vous soutenir, mais faites-le avec soin. Vous pouvez obtenir la RAM ECC, par exemple, et la lire / écrire régulièrement pour corriger les erreurs ECC (et paniquer si l'erreur n'est pas corrigible). Cependant (à partir de la mémoire), la RAM statique est beaucoup plus tolérante aux rayonnements ionisants que la DRAM en premier lieu, il peut donc être préférable d'utiliser une DRAM statique à la place. Voir également le premier point sous «Choses que je ne ferais pas».

Supposons que vous ayez 1% de chances de défaillance d'un nœud donné en une journée, et supposons que vous pouvez rendre les défaillances entièrement indépendantes. Avec 5 nœuds, vous aurez besoin de trois pour échouer en une journée, ce qui représente une chance de 0,00001%. Avec plus, eh bien, vous avez l'idée.

Choses que je ne ferais pas :

  • Sous-estimer la valeur de ne pas avoir le problème pour commencer. À moins que le poids ne soit un problème, un gros bloc de métal autour de votre appareil sera une solution beaucoup moins chère et plus fiable qu'une équipe de programmeurs. Il en va de même pour le couplage optique des entrées EMI, etc.

  • Lancez vos propres algorithmes . Les gens ont déjà fait ça. Utilisez leur travail. La tolérance aux pannes et les algorithmes distribués sont difficiles. Utilisez le travail des autres lorsque cela est possible.

  • Utilisez des paramètres de compilation compliqués dans l'espoir naïf de détecter plus d'échecs. Si vous êtes chanceux, vous pouvez détecter plus d'échecs. Plus probablement, vous utiliserez un chemin de code dans le compilateur qui a été moins testé, en particulier si vous l'avez roulé vous-même.

  • Utilisez des techniques non testées dans votre environnement. La plupart des personnes qui écrivent des logiciels à haute disponibilité doivent simuler des modes de défaillance pour vérifier leur fonctionnement HA correctement, et manquer de nombreux modes de défaillance en conséquence. Vous êtes dans la position «chanceuse» d'avoir des pannes fréquentes sur demande. Testez donc chaque technique et assurez-vous que son application réelle améliore le MTBF d'une quantité qui dépasse la complexité pour l'introduire (avec la complexité vient les bogues). Appliquez particulièrement ceci à mes conseils concernant les algorithmes de quorum, etc.


2
Ethernet n'est probablement pas une excellente idée à utiliser dans les applications critiques. I2C non plus, en dehors du PCB lui-même. Quelque chose de robuste comme CAN conviendrait beaucoup mieux.
Lundin

1
@Lundin Fair point, bien que tout ce qui est connecté optiquement (y compris Ethernet) devrait être OK.
abligh

1
Le support physique n'est pas tant la raison pour laquelle Ethernet n'est pas adapté, mais le manque de comportement déterministe en temps réel. Bien que je suppose qu'il existe de nos jours des moyens de fournir un Ethernet quelque peu fiable, je le regroupe simplement avec de l'électronique commerciale / jouet par habitude.
Lundin

1
@Lundin, c'est un bon point, mais comme je suggère de l'utiliser pour exécuter RAFT, il y aura de toute façon (théoriquement) un comportement en temps réel non déterministe dans l'algorithme (par exemple, des élections simultanées de dirigeants entraînant une nouvelle élection similaire à CSMA / CD). Si un comportement strict en temps réel est nécessaire, ma réponse a sans doute plus de problèmes que Ethernet (et notez en tête de ma réponse que j'ai dit que «correct» était souvent au détriment de «rapide»). J'ai toutefois intégré votre point de vue sur le CAN.
abligh

1
@Lundin: Aucun système impliquant des aspects asynchrones ne peut être totalement non déterministe. Je pense que le pire des cas d'Ethernet peut être limité en l'absence de perturbations matérielles si les protocoles logiciels sont configurés de manière appropriée et que les appareils ont des ID uniques et qu'il existe une limite connue au nombre d'appareils (plus il y a d'appareils, plus ils sont grands dans le pire des cas).
supercat

23

Puisque vous demandez spécifiquement des solutions logicielles et que vous utilisez C ++, pourquoi ne pas utiliser la surcharge d'opérateur pour créer vos propres types de données sûrs? Par exemple:

Au lieu d'utiliser uint32_t(et double, int64_tetc.), créez le vôtre SAFE_uint32_tqui contient un multiple (minimum de 3) de uint32_t. Surchargez toutes les opérations que vous souhaitez (* + - / << >> = ==! = Etc) et effectuez les opérations surchargées indépendamment sur chaque valeur interne, c'est-à-dire ne le faites pas une seule fois et copiez le résultat. Avant et après, vérifiez que toutes les valeurs internes correspondent. Si les valeurs ne correspondent pas, vous pouvez mettre à jour la mauvaise avec la valeur la plus courante. S'il n'y a pas de valeur la plus courante, vous pouvez notifier en toute sécurité qu'il y a une erreur.

De cette façon, peu importe si une corruption se produit dans l'ALU, les registres, la RAM ou sur un bus, vous aurez toujours plusieurs tentatives et une très bonne chance de détecter des erreurs. Notez cependant que cela ne fonctionne que pour les variables que vous pouvez remplacer - votre pointeur de pile par exemple sera toujours sensible.

Une histoire parallèle: j'ai rencontré un problème similaire, également sur une ancienne puce ARM. Il s'est avéré être une chaîne d'outils qui utilisait une ancienne version de GCC qui, avec la puce spécifique que nous avons utilisée, a déclenché un bogue dans certains cas extrêmes qui corromprait (parfois) les valeurs transmises aux fonctions. Assurez-vous que votre appareil n'a aucun problème avant de le blâmer sur la radio-activité, et oui, parfois c'est un bug du compilateur =)


1
Certaines de ces suggestions ont quelque chose dans un état d'esprit similaire de `` contrôle d'intégrité multi-bits '' pour détecter la corruption, j'aime beaucoup celui-ci avec la suggestion de types de données personnalisés critiques pour la sécurité
WearyWanderer

2
Il existe des systèmes dans le monde où chaque nœud redondant a été conçu et développé par différentes équipes, avec un arbitre pour s'assurer qu'ils ne se sont pas accidentellement installés sur les mêmes solutions. De cette façon, vous ne les avez pas tous pour le même bogue et les transitoires similaires ne manifestent pas de modes de défaillance similaires.
jwdonahue

16

Avertissement: je ne suis pas un professionnel de la radioactivité et je n'ai pas travaillé pour ce type d'application. Mais j'ai travaillé sur les erreurs logicielles et la redondance pour l'archivage à long terme des données critiques, qui est quelque peu lié (même problème, objectifs différents).

À mon avis, le principal problème avec la radioactivité est que la radioactivité peut changer de bit, donc la radioactivité peut / va altérer toute mémoire numérique . Ces erreurs sont généralement appelées erreurs logicielles, pourriture de bits, etc.

La question est alors: comment calculer de manière fiable lorsque votre mémoire n'est pas fiable?

Pour réduire considérablement le taux d'erreurs logicielles (au détriment des frais de calcul car il s'agira principalement de solutions logicielles), vous pouvez soit:

  • s'appuyer sur le bon vieux schéma de redondance , et plus spécifiquement sur les codes de correction d'erreurs plus efficaces (même objectif, mais des algorithmes plus intelligents pour que vous puissiez récupérer plus de bits avec moins de redondance). Ceci est parfois (à tort) également appelé somme de contrôle. Avec ce type de solution, vous devrez à tout moment stocker l'état complet de votre programme dans une variable / classe principale (ou une structure?), Calculer un ECC et vérifier que l'ECC est correct avant de faire quoi que ce soit, et si non, réparez les champs. Cette solution ne garantit cependant pas que votre logiciel peut fonctionner (simplement qu'il fonctionnera correctement quand il le pourra, ou cessera de fonctionner sinon, car ECC peut vous dire si quelque chose ne va pas, et dans ce cas, vous pouvez arrêter votre logiciel pour que vous n'obtiennent pas de faux résultats).

  • ou vous pouvez utiliser des structures de données algorithmiques résilientes, qui garantissent, jusqu'à une certaine limite, que votre programme donnera toujours des résultats corrects même en présence d'erreurs logicielles. Ces algorithmes peuvent être vus comme un mélange de structures algorithmiques communes avec des schémas ECC mélangés nativement, mais cela est beaucoup plus résilient que cela, car le schéma de résilience est étroitement lié à la structure, de sorte que vous n'avez pas besoin de coder des procédures supplémentaires pour vérifier l'ECC, et ils sont généralement beaucoup plus rapides. Ces structures permettent de garantir que votre programme fonctionnera dans toutes les conditions, jusqu'à la limite théorique des erreurs logicielles. Vous pouvez également mélanger ces structures résilientes avec le schéma de redondance / ECC pour une sécurité supplémentaire (ou coder vos structures de données les plus importantes comme résilientes, et le reste, les données consommables que vous pouvez recalculer à partir des structures de données principales,

Si vous êtes intéressé par les structures de données résilientes (qui est un nouveau domaine récent mais passionnant en algorithmique et en ingénierie de redondance), je vous conseille de lire les documents suivants:

  • Introduction aux structures de données des algorithmes résilients par Giuseppe F.Italiano, Universita di Roma "Tor Vergata"

  • Christiano, P., Demaine, ED et Kishore, S. (2011). Structures de données tolérantes aux pannes sans perte avec surcharge supplémentaire. Dans Algorithms and Data Structures (pp. 243-254). Springer Berlin Heidelberg.

  • Ferraro-Petrillo, U., Grandoni, F., et Italiano, GF (2013). Structures de données résilientes aux défauts de mémoire: une étude expérimentale des dictionnaires. Journal of Experimental Algorithmics (JEA), 18, 1-6.

  • Italiano, GF (2010). Algorithmes résilients et structures de données. Dans Algorithms and Complexity (pp. 13-24). Springer Berlin Heidelberg.

Si vous souhaitez en savoir plus sur le domaine des structures de données résilientes, vous pouvez consulter les travaux de Giuseppe F. Italiano (et parcourir les références) et le modèle Faulty-RAM (présenté dans Finocchi et al.2005; Finocchi et Italiano 2008).

/ EDIT: J'ai illustré la prévention / récupération des erreurs logicielles principalement pour la mémoire RAM et le stockage de données, mais je n'ai pas parlé des erreurs de calcul (CPU) . D'autres réponses ont déjà indiqué l'utilisation de transactions atomiques comme dans les bases de données, je proposerai donc un autre schéma plus simple: redondance et vote majoritaire .

L'idée est que vous effectuez simplement x fois le même calcul pour chaque calcul que vous devez faire, et stockez le résultat dans x variables différentes (avec x> = 3). Vous pouvez ensuite comparer vos variables x :

  • s'ils sont tous d'accord, il n'y a pas du tout d'erreur de calcul.
  • s'ils ne sont pas d'accord, vous pouvez utiliser un vote majoritaire pour obtenir la valeur correcte, et comme cela signifie que le calcul a été partiellement corrompu, vous pouvez également déclencher une analyse d'état du système / programme pour vérifier que le reste est correct.
  • si le vote majoritaire ne peut pas déterminer un gagnant (toutes les valeurs x sont différentes), alors c'est un signal parfait pour vous de déclencher la procédure de sécurité (redémarrer, lancer une alerte à l'utilisateur, etc.).

Ce schéma de redondance est très rapide par rapport à l'ECC (pratiquement O (1)) et il vous fournit un signal clair lorsque vous avez besoin d'une sécurité intégrée . Le vote majoritaire est également (presque) garanti de ne jamais produire de sortie corrompue et également de récupérer des erreurs de calcul mineures , car la probabilité que x calculs donnent la même sortie est infinitésimale (car il y a une énorme quantité de sorties possibles, il est presque impossible de obtenir au hasard 3 fois la même chose, encore moins de chances si x> 3).

Donc, avec un vote majoritaire, vous êtes à l'abri de la sortie corrompue, et avec la redondance x == 3, vous pouvez récupérer 1 erreur (avec x == 4 ce sera 2 erreurs récupérables, etc. - l'équation exacte est nb_error_recoverable == (x-2)où x est le nombre de répétitions de calcul car vous avez besoin d'au moins 2 calculs d'accord pour récupérer à l'aide du vote majoritaire).

L'inconvénient est que vous devez calculer x fois au lieu d'une fois, vous avez donc un coût de calcul supplémentaire, mais la complexité linéaire est si asymptotique que vous ne perdez pas grand-chose pour les avantages que vous gagnez. Un moyen rapide de voter à la majorité consiste à calculer le mode sur un tableau, mais vous pouvez également utiliser un filtre médian.

De plus, si vous voulez vous assurer que les calculs sont effectués correctement, si vous pouvez créer votre propre matériel, vous pouvez construire votre appareil avec x CPU et câbler le système afin que les calculs soient automatiquement dupliqués sur les x CPU avec un vote majoritaire effectué. mécaniquement à la fin (en utilisant des portes ET / OU par exemple). Cela est souvent mis en œuvre dans les avions et les appareils critiques (voir triple redondance modulaire ). De cette façon, vous n'auriez pas de surcharge de calcul (puisque les calculs supplémentaires seront effectués en parallèle), et vous avez une autre couche de protection contre les erreurs logicielles (puisque la duplication des calculs et le vote majoritaire seront gérés directement par le matériel et non par logiciel - qui peut plus facilement être corrompu car un programme est simplement des bits stockés en mémoire ...).


9

Un point que personne ne semble avoir mentionné. Vous dites que vous développez dans GCC et effectuez une compilation croisée sur ARM. Comment savez-vous que vous n'avez pas de code qui fait des hypothèses sur la RAM libre, la taille entière, la taille du pointeur, combien de temps il faut pour effectuer une certaine opération, combien de temps le système fonctionnera en continu ou diverses choses comme ça? Il s'agit d'un problème très courant.

La réponse est généralement un test unitaire automatisé. Écrivez des faisceaux de test qui exercent le code sur le système de développement, puis exécutez les mêmes faisceaux de test sur le système cible. Recherchez les différences!

Vérifiez également les errata sur votre appareil intégré. Vous pouvez trouver qu'il y a quelque chose à propos de "ne faites pas ça parce que ça va planter, alors activez cette option du compilateur et le compilateur le contournera".

En bref, votre source de plantage la plus probable est les bogues dans votre code. Jusqu'à ce que vous vous soyez assuré que ce n'est pas le cas, ne vous inquiétez pas (encore) des modes de défaillance plus ésotériques.


1
En effet, nulle part dans le test de la question, l'auteur mentionne que l'application s'est avérée fonctionner correctement en dehors de l'environnement radioactif.
Marc.2377

9

Vous voulez 3+ machines esclaves avec un maître en dehors de l'environnement de rayonnement. Toutes les E / S passent par le maître qui contient un mécanisme de vote et / ou de nouvelle tentative. Les esclaves doivent chacun avoir un chien de garde matériel et l'appel à les heurter doit être entouré de CRC ou similaires pour réduire la probabilité de heurt involontaire. Le contournement doit être contrôlé par le maître, donc la connexion perdue avec le maître équivaut à redémarrer en quelques secondes.

Un avantage de cette solution est que vous pouvez utiliser la même API pour le maître que pour les esclaves, de sorte que la redondance devient une fonctionnalité transparente.

Edit: D'après les commentaires, je ressens le besoin de clarifier "l'idée du CRC". La possibilité que l'esclave se heurte à son propre chien de garde est proche de zéro si vous entourez la bosse de CRC ou que vous contrôlez les données aléatoires du maître. Ces données aléatoires ne sont envoyées par le maître que lorsque l'esclave examiné est aligné avec les autres. Les données aléatoires et CRC / digest sont immédiatement effacés après chaque bosse. La fréquence de coupure maître-esclave doit être plus du double du délai d'attente du chien de garde. Les données envoyées par le maître sont générées à chaque fois de manière unique.


7
J'essaie de comprendre un scénario où vous pouvez avoir un maître en dehors de l'environnement de rayonnement, capable de communiquer de manière fiable avec des esclaves à l'intérieur de l'environnement de rayonnement, où vous ne pourriez pas simplement mettre les esclaves en dehors de l'environnement de rayonnement.
fostandy

1
@fostandy: Les esclaves mesurent ou contrôlent en utilisant un équipement qui a besoin d'un contrôleur. Dites un compteur Geiger. Le maître n'a pas besoin d'une communication fiable en raison de la redondance des esclaves.
Jonas Byström

4
L'introduction d'un maître ne signifie pas automatiquement une sécurité accrue. Si l'esclave x est devenu fou en raison d'une corruption de mémoire, de sorte qu'il se répète à plusieurs reprises "le maître est ici, le maître est heureux", alors aucun montant de CRC ou d'ordres aboyés par le maître ne le sauvera. Il faudrait donner au maître la possibilité de couper l'alimentation de cet esclave. Et si vous avez une erreur de cause commune, l'ajout d'esclaves supplémentaires n'augmentera pas la sécurité. Gardez également à l'esprit que la quantité de bogues logiciels et la quantité de choses qui peuvent se briser augmentent avec la complexité.
Lundin

5
Cela étant dit, il serait bien sûr "d'externaliser" autant de programme vers un endroit moins exposé, tout en gardant l'électronique à l'intérieur de l'environnement radioactif aussi simple que possible, si vous avez cette option.
Lundin

7

Que diriez-vous d'exécuter de nombreuses instances de votre application. Si les plantages sont dus à des modifications aléatoires des bits de mémoire, il est probable que certaines de vos instances d'application s'en sortiront et produiront des résultats précis. Il est probablement assez facile (pour quelqu'un ayant des antécédents statistiques) de calculer le nombre d'instances dont vous avez besoin, compte tenu de la probabilité de flop binaire pour obtenir la plus petite erreur globale que vous souhaitez.


2
Un système embarqué préfèrerait sûrement de beaucoup les captures critiques pour la sécurité dans une instance d'une application robuste que de simplement déclencher plusieurs instances, augmentant les exigences matérielles et espérant dans une certaine mesure la chance aveugle qu'au moins une instance réussisse? Je comprends l'idée et elle est valide, mais je penche davantage vers les suggestions qui ne reposent pas sur la force brute
WearyWanderer

7

Ce que vous demandez est un sujet assez complexe - difficile à répondre. D'autres réponses sont correctes, mais elles ne couvraient qu'une petite partie de tout ce que vous devez faire.

Comme on le voit dans les commentaires , il n'est pas possible de résoudre les problèmes matériels à 100%, mais il est possible avec une forte probabilité de les réduire ou de les attraper en utilisant diverses techniques.

Si j'étais vous, je créerais le logiciel du plus haut niveau d'intégrité de sécurité (SIL-4). Obtenez le document CEI 61513 (pour l'industrie nucléaire) et suivez-le.


11
Ou plutôt, lisez les exigences techniques et implémentez celles qui ont du sens. Une grande partie des normes SIL est absurde, si vous les suivez de manière dogmatique, vous vous retrouverez avec des produits dangereux et dangereux. Aujourd'hui, la certification SIL consiste principalement à produire une tonne de documentation, puis à corrompre une maison d'essai. Le niveau SIL ne dit rien sur la sécurité réelle du système. Au lieu de cela, vous voudrez vous concentrer sur les mesures de sécurité techniques réelles. Il y en a de très bons dans les documents SIL, et il y en a de complètement absurdes.
Lundin

7

Quelqu'un a mentionné l'utilisation de puces plus lentes pour empêcher les ions de retourner les bits aussi facilement. De la même manière, utilisez peut-être un processeur / ram spécialisé qui utilise en fait plusieurs bits pour stocker un seul bit. Fournissant ainsi une tolérance aux pannes matérielles car il est très peu probable que tous les bits soient retournés. Donc 1 = 1111 mais devrait être touché 4 fois pour être retourné. (4 peut être un mauvais nombre car si 2 bits sont retournés, c'est déjà ambigu). Donc, si vous optez pour 8, vous obtenez 8 fois moins de RAM et un temps d'accès plus lent mais une représentation des données beaucoup plus fiable. Vous pourriez probablement faire cela à la fois au niveau logiciel avec un compilateur spécialisé (allouer x quantité d'espace supplémentaire pour tout) ou l'implémentation du langage (écrire des wrappers pour les structures de données qui allouent les choses de cette façon).


7

Il serait peut-être utile de savoir si cela signifie que le matériel est "conçu pour cet environnement". Comment corrige-t-il et / ou indique-t-il la présence d'erreurs SEU?

Dans un projet lié à l'exploration spatiale, nous avions un MCU personnalisé, qui déclencherait une exception / interruption sur les erreurs SEU, mais avec un certain retard, c'est-à-dire que certains cycles pourraient passer / des instructions seraient exécutées après celle insn qui a provoqué l'exception SEU.

Le cache de données était particulièrement vulnérable, donc un gestionnaire invaliderait la ligne de cache incriminée et redémarrerait le programme. Seulement cela, en raison de la nature imprécise de l'exception, la séquence d'insns dirigée par l'exception levant insn peut ne pas être redémarrable.

Nous avons identifié les séquences dangereuses (non redémarrables) (comme lw $3, 0x0($2), suivies d'un insn, qui modifie $2et ne dépend pas des données $3), et j'ai apporté des modifications à GCC, de sorte que de telles séquences ne se produisent pas (par exemple, en dernier recours, en séparant le deux insns par a nop).

Juste quelque chose à considérer ...


7

Si votre matériel tombe en panne, vous pouvez utiliser le stockage mécanique pour le récupérer. Si votre base de code est petite et dispose d'un espace physique, vous pouvez utiliser un magasin de données mécaniques.

Entrez la description de l'image ici

Il y aura une surface de matériau qui ne sera pas affectée par le rayonnement. Plusieurs engrenages seront là. Un lecteur mécanique fonctionnera sur tous les engrenages et sera flexible pour monter et descendre. Down signifie qu'il est 0 et up signifie qu'il est 1. De 0 et 1, vous pouvez générer votre base de code.


2
Peut-être qu'un support optique tel qu'un CD-ROM répondrait à cette définition. Il aurait l'avantage supplémentaire d'une grande capacité.
Wossname

2
Oui, ce sera similaire, mais le CD-ROM utilisera moins, mais ce sera un système entièrement mécanique.
Hitul

7
Je me demande s'il y a une raison pour laquelle ils n'utilisent pas de lecteurs de cartes perforées dans l'espace.
Soren

3
@Soren La vitesse et l'espace physique peuvent être une raison.
Hitul

5

Utilisez un planificateur cyclique . Cela vous donne la possibilité d'ajouter des temps de maintenance réguliers pour vérifier l'exactitude des données critiques. Le problème le plus souvent rencontré est la corruption de la pile. Si votre logiciel est cyclique, vous pouvez réinitialiser la pile entre les cycles. Ne réutilisez pas les piles pour les appels d'interruption, configurez une pile distincte pour chaque appel d'interruption important.

Semblable au concept Watchdog, il y a des temporisateurs. Démarrez un minuteur matériel avant d'appeler une fonction. Si la fonction ne revient pas avant l'interruption du temporisateur, rechargez la pile et réessayez. S'il échoue toujours après 3/5 essais, vous devez recharger à partir de la ROM.

Divisez votre logiciel en parties et isolez ces parties pour utiliser des zones de mémoire et des temps d'exécution séparés (en particulier dans un environnement de contrôle). Exemple: acquisition de signal, pré-transmission des données, algorithme principal et implémentation / transmission des résultats. Cela signifie qu'un échec dans une partie n'entraînera pas d'échecs dans le reste du programme. Ainsi, pendant que nous réparons l'acquisition du signal, le reste des tâches se poursuit sur les données périmées.

Tout a besoin de CRC. Si vous exécutez en dehors de la RAM, même votre .text a besoin d'un CRC. Vérifiez régulièrement les CRC si vous utilisez un planificateur cyclique. Certains compilateurs (pas GCC) peuvent générer des CRC pour chaque section et certains processeurs ont du matériel dédié pour faire des calculs de CRC, mais je suppose que cela tomberait hors de la portée de votre question. La vérification des CRC invite également le contrôleur ECC sur la mémoire à réparer les erreurs de bit unique avant qu'il ne devienne un problème.


4

Tout d'abord, concevez votre application en fonction de l'échec . Assurez-vous que dans le cadre d'un fonctionnement à débit normal, il s'attend à se réinitialiser (en fonction de votre application et du type de défaillance, soit légère, soit dure). Cela est difficile à obtenir: les opérations critiques qui nécessitent un certain degré de transactionnalité peuvent devoir être vérifiées et modifiées au niveau de l'assemblage afin qu'une interruption à un point clé ne puisse pas entraîner des commandes externes incohérentes. Échec rapide dès qu'une corruption de mémoire irrécupérable ou un écart de flux de contrôle est détecté. Enregistrez les échecs si possible.

Deuxièmement, si possible, corrigez la corruption et continuez . Cela signifie une somme de contrôle et une correction fréquente des tables (et du code du programme si vous le pouvez); peut-être avant chaque opération majeure ou sur une interruption temporisée, et le stockage des variables dans des structures qui se corrigent automatiquement (encore une fois avant chaque opération majeure ou sur une interruption temporisée, prenez un vote majoritaire de 3 et corrigez s'il s'agit d'une seule déviation). Enregistrez les corrections si possible.

Troisièmement, l' échec du test . Configurez un environnement de test reproductible qui retourne les bits en mémoire de manière aléatoire. Cela vous permettra de reproduire les situations de corruption et aidera à concevoir votre application autour d'eux.


3

Compte tenu des commentaires de supercat, des tendances des compilateurs modernes et d'autres choses, je serais tenté de retourner dans les temps anciens et d'écrire le code entier en assemblage et en allocations de mémoire statique partout. Pour ce type de fiabilité absolue, je pense que l'assemblage n'entraîne plus une grande différence en pourcentage du coût.


Je suis un grand fan du langage d'assemblage (comme vous pouvez le voir dans mes réponses à d'autres questions), mais je ne pense pas que ce soit une bonne réponse. Il est assez possible de savoir à quoi s'attendre du compilateur pour la plupart du code C (en termes de valeurs vivant dans les registres par rapport à la mémoire), et vous pouvez toujours vérifier que c'est ce que vous attendiez. L'écriture à la main d'un grand projet en asm n'est qu'une tonne de travail supplémentaire, même si vous avez des développeurs qui sont très à l'aise pour écrire ARM asm. Peut-être que si vous voulez faire des choses comme calculer le même résultat 3 fois, l'écriture de certaines fonctions en asm est logique. (les compilateurs le supprimeront)
Peter Cordes

Le risque plus élevé sinon qui doit être mis en balance avec lui est la mise à niveau du compilateur peut vous laisser des changements inattendus.
Joshua

1

Voici une énorme quantité de réponses, mais je vais essayer de résumer mes idées à ce sujet.

Quelque chose plante ou ne fonctionne pas correctement peut être le résultat de vos propres erreurs - il devrait être facile de le réparer lorsque vous localisez le problème. Mais il y a aussi la possibilité de pannes matérielles - et c'est difficile, sinon impossible à résoudre dans l'ensemble.

Je recommanderais d'abord d'essayer de saisir la situation problématique en vous connectant (pile, registres, appels de fonction) - soit en les enregistrant quelque part dans un fichier, soit en les transmettant directement ("oh non - je plante").

La récupération à partir d'une telle situation d'erreur est soit un redémarrage (si le logiciel est toujours actif et en cours), soit une réinitialisation matérielle (par exemple, des chiens de garde hw). Plus facile à démarrer depuis le premier.

Si le problème est lié au matériel - la journalisation devrait vous aider à identifier dans quel problème d'appel de fonction se produit et qui peut vous donner une connaissance interne de ce qui ne fonctionne pas et où.

De plus, si le code est relativement complexe - il est logique de le «diviser et le conquérir» - ce qui signifie que vous supprimez / désactivez certains appels de fonction là où vous soupçonnez un problème - en désactivant généralement la moitié du code et en activant une autre moitié - vous pouvez obtenir «ça marche» / "ne fonctionne pas" type de décision après quoi vous pouvez vous concentrer sur une autre moitié de code. (Où est le problème)

Si un problème survient après un certain temps - alors un débordement de pile peut être suspecté - alors il est préférable de surveiller les registres de points de pile - s'ils augmentent constamment.

Et si vous parvenez à minimiser complètement votre code jusqu'à ce que le type d'application "hello world" - et il échoue toujours au hasard - alors des problèmes matériels sont attendus - et il doit y avoir une "mise à niveau matérielle" - ce qui signifie inventer un tel cpu / ram / ... -une combinaison matérielle qui tolérerait mieux le rayonnement.

La chose la plus importante est probablement la façon dont vous récupérez vos journaux si la machine est complètement arrêtée / réinitialisée / ne fonctionne pas - probablement la première chose que le bootstap devrait faire - est de revenir à la maison si une situation problématique est découverte.

S'il est également possible dans votre environnement de transmettre un signal et de recevoir une réponse - vous pouvez essayer de construire une sorte d'environnement de débogage à distance en ligne, mais vous devez alors avoir au moins des supports de communication fonctionnant et un processeur / un ram en état de fonctionnement. Et par débogage à distance, je veux dire soit une approche de type stub GDB / gdb, soit votre propre implémentation de ce que vous devez récupérer de votre application (par exemple, télécharger des fichiers journaux, télécharger la pile d'appels, télécharger ram, redémarrer)


Désolé, mais la question concerne l'environnement radioactif où des pannes matérielles se produiront. Votre réponse concerne l'optimisation générale du logiciel et la façon de trouver des bogues. Mais dans cette situation, les échecs ne sont pas produits par des bugs
jeb

Oui, vous pouvez également blâmer la gravité terrestre, les optimisations du compilateur, la bibliothèque tierce, l'environnement radioactif, etc. Mais êtes-vous sûr que ce ne sont pas vos propres bugs? :-) Sauf preuve du contraire - je ne le crois pas. J'ai exécuté une fois une mise à jour du firmware et des tests de mise hors tension - mon logiciel n'a survécu à toutes les situations de mise hors tension qu'après avoir corrigé tous mes propres bugs. (Plus de 4000 coupures de courant pendant la nuit) Mais il est difficile de croire qu'il y avait un bug dans certains cas. Surtout quand nous parlons de corruption de mémoire.
TarmoPikaro

0

J'ai vraiment lu beaucoup de bonnes réponses!

Voici mon 2 centime: construire un modèle statistique de l'anomalie mémoire / registre, en écrivant un logiciel pour vérifier la mémoire ou pour effectuer des comparaisons fréquentes de registres. De plus, créez un émulateur, dans le style d'une machine virtuelle où vous pouvez expérimenter le problème. Je suppose que si vous modifiez la taille de la jonction, la fréquence d'horloge, le fournisseur, le boîtier, etc., vous observerez un comportement différent.

Même la mémoire de notre ordinateur de bureau présente un certain taux d'échec, ce qui ne nuit cependant pas au travail quotidien.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.