Compilazione di un'applicazione da utilizzare in ambienti altamente radioattivi


1456

Stiamo compilando un'applicazione C / C ++ incorporata che viene distribuita in un dispositivo schermato in un ambiente bombardato da radiazioni ionizzanti . Stiamo usando GCC e cross-compilation per ARM. Una volta distribuita, la nostra applicazione genera alcuni dati errati e si blocca più spesso di quanto vorremmo. L'hardware è progettato per questo ambiente e la nostra applicazione è in esecuzione su questa piattaforma da diversi anni.

Ci sono modifiche che possiamo apportare al nostro codice o miglioramenti in fase di compilazione che possono essere fatti per identificare / correggere errori software e corruzione della memoria causati da sconvolgimenti di singoli eventi ? Qualche altro sviluppatore ha avuto successo nel ridurre gli effetti dannosi degli errori soft su un'applicazione di lunga durata?


186
I valori in memoria stanno cambiando o stanno cambiando i valori nel processore? Se l'hardware è progettato per l'ambiente, il software dovrebbe funzionare come se fosse in esecuzione in un ambiente non radioattivo.
Thomas Matthews,

3
Se possibile, è necessario impostare un sistema di registrazione che memorizzi gli eventi nella memoria non volatile resistente alle radiazioni. Archivia informazioni sufficienti in modo da poter tracciare l'evento e trovare facilmente la causa principale.
Thomas Matthews,

2
Thomas Matthews Tutta la memoria ha un tasso di errore FIT e i produttori di hardware fanno molte promesse. La maggior parte dei problemi è probabilmente causata dalle SEU che modificano ram in fase di esecuzione.
torre

9
Questa è una combinazione di soluzione hardware / software, ma so che Texas Instruments (e probabilmente altri) produce chip integrati per applicazioni critiche per la sicurezza che consistono in due core duplicati, che funzionano in sequenza, mezzo ciclo fuori fase. Esistono interruzioni speciali e azioni di ripristino che vengono eseguite quando l'hardware rileva qualcosa di diverso tra i core, in modo da poter recuperare dagli errori. Credo che TI li contrassegni come processori di sicurezza "Hercules".
mbrig,

5
Motori robusti ridondanti, alcuni ingranaggi, alberi e cricchetti! Sostituire annualmente o più spesso in base alle dosi richieste. No davvero, la mia prima domanda con questo tipo di problemi è sempre stata, hai davvero bisogno di tanto software lì dentro? Sii il più analogico possibile.
jwdonahue,

Risposte:


814

Lavorando per circa 4-5 anni con lo sviluppo di software / firmware e test ambientali di satelliti miniaturizzati *, vorrei condividere la mia esperienza qui.

* (i satelliti miniaturizzati sono molto più inclini ai disturbi di un singolo evento rispetto ai satelliti più grandi a causa delle sue dimensioni relativamente ridotte e limitate per i suoi componenti elettronici )

Essere molto concisi e diretti: non esiste alcun meccanismo per recuperare da una situazione rilevabile ed errata da parte del software / firmware stesso senza , almeno, una copia della versione minima funzionante del software / firmware da qualche parte a scopo di recupero - e con il supporto hardware il recupero (funzionale).

Ora, questa situazione viene normalmente gestita sia a livello hardware che software. Qui, come da lei richiesto, condividerò ciò che possiamo fare a livello di software.

  1. ... scopo di recupero ... . Fornisci la possibilità di aggiornare / ricompilare / aggiornare il tuo software / firmware in un ambiente reale. Questa è una funzionalità quasi indispensabile per qualsiasi software / firmware in un ambiente fortemente ionizzato. Senza questo, si potrebbe avere ridondante software / hardware come molti come si vuole, ma a un certo punto, sono tutti andando a saltare in aria. Quindi, prepara questa funzione!

  2. ... versione minima funzionante ... Avere copie multiple, reattive, versione minima del software / firmware nel codice. Questa è come la modalità provvisoria in Windows. Invece di avere solo una versione completamente funzionante del tuo software, disponi di più copie della versione minima del tuo software / firmware. La copia minima di solito avrà dimensioni molto inferiori rispetto alla copia completa e quasi sempre avrà solo le seguenti due o tre funzioni:

    1. in grado di ascoltare i comandi da un sistema esterno,
    2. in grado di aggiornare l'attuale software / firmware,
    3. in grado di monitorare i dati di pulizia dell'operazione di base.
  3. ... copia ... da qualche parte ... Avere software / firmware ridondanti da qualche parte.

    1. È possibile, con o senza hardware ridondante, provare a disporre di software / firmware ridondanti in ARM uC. Ciò avviene normalmente avendo due o più software / firmware identici in indirizzi separati che si scambiano il battito cardiaco, ma solo uno sarà attivo alla volta. Se è noto che uno o più software / firmware non rispondono, passare all'altro software / firmware. Il vantaggio dell'utilizzo di questo approccio è che possiamo avere la sostituzione funzionale immediatamente dopo che si verifica un errore, senza alcun contatto con qualsiasi sistema / parte esterna che è responsabile di rilevare e riparare l'errore (nel caso del satellite, di solito è il Centro di controllo missione ( MCC)).

      A rigor di termini, senza hardware ridondante, lo svantaggio di farlo è che in realtà non è possibile eliminare tutti i singoli punti di errore. Per lo meno, avrai ancora un singolo punto di errore, che è lo switch stesso (o spesso l'inizio del codice). Tuttavia, per un dispositivo limitato dalle dimensioni in un ambiente altamente ionizzato (come i satelliti pico / femto), sarà ancora da prendere in considerazione la riduzione del singolo punto di guasto a un punto senza hardware aggiuntivo. Inoltre, il pezzo di codice per la commutazione sarebbe sicuramente molto meno del codice per l'intero programma, riducendo significativamente il rischio di inserirvi un singolo evento.

    2. Ma se non lo fai, dovresti avere almeno una copia nel tuo sistema esterno che può entrare in contatto con il dispositivo e aggiornare il software / firmware (nel caso del satellite, è di nuovo il centro di controllo della missione).

    3. Potresti anche avere la copia nella memoria permanente del dispositivo che può essere attivata per ripristinare il software / firmware del sistema in esecuzione
  4. ... rilevabile situazione errata .. L'errore deve essere rilevabile , generalmente dal circuito di correzione / rilevamento dell'errore hardware o da un piccolo codice per la correzione / rilevazione dell'errore. È meglio mettere tale codice piccolo, multiplo e indipendente dal software / firmware principale. Il suo compito principale è solo quello di controllare / correggere. Se il circuito hardware / firmware è affidabile(come ad esempio una radiazione più indurita delle altre o con più circuiti / logiche), potresti prendere in considerazione la possibilità di correggere gli errori. Ma se non lo è, è meglio farlo come rilevamento degli errori. La correzione può essere effettuata da un sistema / dispositivo esterno. Per la correzione degli errori, potresti prendere in considerazione l'uso di un algoritmo di correzione degli errori di base come Hamming / Golay23, perché possono essere implementati più facilmente sia nel circuito / software. Ma alla fine dipende dalle capacità della tua squadra. Per il rilevamento degli errori, viene normalmente utilizzato CRC.

  5. ... hardware che supporta il recupero Ora, arriva l'aspetto più difficile su questo problema. In definitiva, il ripristino richiede che l'hardware responsabile del ripristino sia almeno funzionale. Se l'hardware è permanentemente rotto (normalmente si verifica dopo che la sua dose di ionizzazione totale ha raggiunto un certo livello), allora (purtroppo) il software non può aiutare il recupero. Pertanto, l'hardware è giustamente la massima importanza per un dispositivo esposto ad alto livello di radiazioni (come il satellite).

Oltre al suggerimento di cui sopra per anticipare l'errore del firmware a causa di un singolo evento sconvolto, vorrei anche suggerirti di avere:

  1. Algoritmo di rilevamento e / o correzione errori nel protocollo di comunicazione tra sottosistemi. Questo è un altro quasi da avere per evitare segnali incompleti / errati ricevuti da altri sistemi

  2. Filtra nella tua lettura ADC. Evitare Non utilizzare l'ADC lettura diretta. Filtra per filtro mediano, filtro medio o qualsiasi altro filtro - non fidarti mai del singolo valore di lettura. Assaggia di più, non di meno, ragionevolmente.


401

La NASA ha un documento sul software indurito dalle radiazioni . Descrive tre attività principali:

  1. Monitoraggio regolare della memoria per errori, quindi eliminazione di tali errori,
  2. solidi meccanismi di recupero degli errori e
  3. la possibilità di riconfigurare se qualcosa non funziona più.

Si noti che la velocità di scansione della memoria dovrebbe essere abbastanza frequente da causare raramente errori multi-bit, poiché la maggior parte della memoria ECC può recuperare da errori a singolo bit, non errori multi-bit.

Il robusto recupero degli errori include il trasferimento del flusso di controllo (in genere il riavvio di un processo prima dell'errore), il rilascio delle risorse e il ripristino dei dati.

La loro principale raccomandazione per il ripristino dei dati è quella di evitarne la necessità, poiché i dati intermedi devono essere trattati come temporanei, in modo che il riavvio prima dell'errore ripristini anche i dati a uno stato affidabile. Sembra simile al concetto di "transazioni" nei database.

Discutono tecniche particolarmente adatte a linguaggi orientati agli oggetti come il C ++. Per esempio

  1. ECC basati su software per oggetti di memoria contigui
  2. Programmazione per contratto : verifica delle condizioni preliminari e postcondizioni, quindi verifica dell'oggetto per verificare che sia ancora in uno stato valido.

E, in tal caso, la NASA ha usato il C ++ per grandi progetti come Mars Rover .

L'astrazione e l'incapsulamento della classe C ++ hanno consentito un rapido sviluppo e test tra più progetti e sviluppatori.

Hanno evitato alcune funzionalità C ++ che potrebbero creare problemi:

  1. eccezioni
  2. Modelli
  3. Iostream (senza console)
  4. Eredità multipla
  5. Sovraccarico dell'operatore (diverso da newe delete)
  6. Allocazione dinamica (utilizzato un pool di memoria e un posizionamento dedicati newper evitare la possibilità di corruzione dell'heap di sistema).

28
In realtà sembra qualcosa in cui un linguaggio puro sarebbe bravo. Poiché i valori non cambiano mai, se sono danneggiati puoi semplicemente tornare alla definizione originale (che è quella che dovrebbe essere) e non farai accidentalmente la stessa cosa due volte (a causa della mancanza di effetti collaterali).
PyRulez,

20
RAII è una cattiva idea, perché non puoi fare affidamento sul fatto che funzioni correttamente o addirittura. Potrebbe danneggiare i dati in modo casuale, ecc. Volete davvero quanta immutabilità possibile e meccanismi di correzione degli errori. È molto più semplice buttare via le cose rotte piuttosto che cercare di ripararle in qualche modo (come fai a sapere esattamente quanto basta per tornare al vecchio stato corretto?). Probabilmente vuoi usare un linguaggio piuttosto stupido per questo, però: le ottimizzazioni potrebbero ferire più di quanto aiutino.
Luaan,

67
@PyRulez: i linguaggi puri sono un'astrazione, l'hardware non è puro. I compilatori sono abbastanza bravi a nascondere la differenza. Se il tuo programma ha un valore che logicamente non dovrebbe più usare dopo il passaggio X, il compilatore potrebbe sovrascriverlo con un valore calcolato nel passaggio X + 1. Ma questo significa che non puoi tornare indietro. Più formalmente, i possibili stati di un programma in un linguaggio puro formano un grafico aciclico, il che significa che due stati sono equivalenti e possono essere uniti quando gli stati raggiungibili da entrambi sono equivalenti. Questa fusione distrugge la differenza nei percorsi che portano a quegli stati.
Salterio,

2
@Vorac - Secondo la presentazione, la preoccupazione per i template C ++ è il bloat del codice.
1919

3
@DeerSpotter Il problema esatto è molto più grande di così. La ionizzazione può danneggiare i bit del tuo programma watcher in esecuzione. Quindi avrai bisogno di un osservatore di un osservatore, quindi - osservatore di un osservatore di un osservatore e così via ...
Agnius Vasiliauskas,

116

Ecco alcuni pensieri e idee:

Usa la ROM in modo più creativo.

Conserva tutto ciò che puoi nella ROM. Invece di calcolare le cose, archivia le tabelle di ricerca nella ROM. (Assicurati che il compilatore stia inviando le tue tabelle di ricerca alla sezione di sola lettura! Stampa gli indirizzi di memoria in fase di esecuzione per controllare!) Memorizza la tua tabella di vettore di interrupt nella ROM. Ovviamente, esegui alcuni test per vedere quanto è affidabile la tua ROM rispetto alla tua RAM.

Usa la tua RAM migliore per lo stack.

Le SEU nello stack sono probabilmente la fonte più probabile di arresti anomali, perché è dove vivono in genere cose come variabili di indice, variabili di stato, indirizzi di ritorno e puntatori di vario genere.

Implementare routine timer-tick e watchdog timer.

È possibile eseguire una routine di "controllo di integrità" ad ogni tick del timer, nonché una routine di controllo per gestire il blocco del sistema. Il codice principale potrebbe inoltre incrementare periodicamente un contatore per indicare l'avanzamento e la routine di controllo di integrità potrebbe garantire che ciò si sia verificato.

Implementare i codici di correzione degli errori nel software.

È possibile aggiungere ridondanza ai dati per poter rilevare e / o correggere errori. Ciò aumenterà i tempi di elaborazione, lasciando potenzialmente il processore esposto alle radiazioni per un tempo più lungo, aumentando così la possibilità di errori, quindi è necessario considerare il compromesso.

Ricorda le cache.

Controlla le dimensioni della cache della CPU. I dati a cui hai avuto accesso o modificato di recente saranno probabilmente all'interno di una cache. Credo che puoi disabilitare almeno alcune delle cache (a un costo elevato per le prestazioni); dovresti provare questo per vedere quanto sono sensibili le cache alle SEU. Se le cache sono più difficili della RAM, è possibile leggere e riscrivere regolarmente i dati critici per assicurarsi che rimangano nella cache e riportare la RAM in linea.

Usa i gestori di errori di pagina in modo intelligente.

Se si contrassegna una pagina di memoria come non presente, la CPU genererà un errore di pagina quando si tenta di accedervi. È possibile creare un gestore degli errori di pagina che esegue alcuni controlli prima di eseguire la manutenzione della richiesta di lettura. (I sistemi operativi per PC lo utilizzano per caricare in modo trasparente pagine che sono state scambiate su disco.)

Usa il linguaggio assembly per le cose critiche (che potrebbero essere tutto).

Con il linguaggio assembly, sai cosa c'è nei registri e cosa c'è nella RAM; si sa cosa tavoli speciali RAM CPU sta usando, e si può progettare le cose in un modo indiretto per mantenere il rischio verso il basso.

Utilizzalo objdumpper esaminare effettivamente il linguaggio assembly generato e capire quanto codice occupa ciascuna delle tue routine.

Se stai usando un grande sistema operativo come Linux, allora stai chiedendo problemi; c'è così tanta complessità e tante cose che vanno male.

Ricorda che è un gioco di probabilità.

Ha detto un commentatore

Ogni routine che scrivi per rilevare errori sarà soggetta al fallimento stesso della stessa causa.

Sebbene ciò sia vero, le possibilità di errori nei (diciamo) 100 byte di codice e dati richiesti per il corretto funzionamento di una routine di controllo sono molto inferiori rispetto alla possibilità di errori altrove. Se la tua ROM è abbastanza affidabile e quasi tutto il codice / dati è effettivamente nella ROM, allora le tue probabilità sono ancora migliori.

Usa hardware ridondante.

Utilizzare 2 o più configurazioni hardware identiche con codice identico. Se i risultati differiscono, è necessario attivare un ripristino. Con 3 o più dispositivi è possibile utilizzare un sistema di "voto" per provare a identificare quale è stato compromesso.


14
Al giorno d'oggi, è disponibile l'ECC tramite hardware, che consente di risparmiare tempo di elaborazione. Il primo passo sarebbe quello di scegliere un microcontrollore con ECC integrato.
Lundin,

23
Da qualche parte nella parte posteriore della mia mente c'è un riferimento all'hardware di volo avionico (forse la navetta spaziale?) In cui l'architettura ridondante è stata esplicitamente progettata per non essere identica (e da diversi team). Ciò mitiga la possibilità di un errore sistemico nella progettazione hardware / software, riducendo la possibilità che tutti i sistemi di voto si arrestino contemporaneamente quando si confrontano con gli stessi input.
Peter M,

8
@PeterM: AFAIK rivendicato anche per il software di volo per il Boeing 777: tre versioni di tre squadre in tre linguaggi di programmazione.
Ripristina Monica - M. Schröder il

7
La RAM @DanEsparza ha in genere un condensatore (DRAM) o alcuni transistor in feedback (SRAM) per l'archiviazione dei dati. Un evento di radiazione può caricare / scaricare in modo spurio il condensatore o modificare il segnale nel circuito di retroazione. La ROM in genere non ha bisogno della possibilità di essere scritta (almeno senza circostanze particolari e / o tensioni più elevate) e quindi potrebbe essere intrinsecamente più stabile a livello fisico.
nanofarad,

7
@DanEsparza: esistono diversi tipi di memorie ROM. Se la "ROM" è emulata da eeprom o flash readonly-at-5v ma-programmable-at-10v, allora effettivamente quella "ROM" è ancora soggetta a ionizzazione. Forse solo meno degli altri. Tuttavia, ci sono buone cose vecchie come la maschera ROM o la PROM basata su micce che penso avrebbero bisogno di una quantità davvero grave di radiazioni per iniziare a fallire. Non so comunque se ce ne siano ancora fabbricati.
quetzalcoatl,

105

Potresti anche essere interessato alla ricca letteratura sull'argomento della tolleranza algoritmica ai guasti. Ciò include il vecchio compito: scrivi una specie che ordina correttamente il suo input quando un numero costante di confronti fallirà (o, la versione leggermente più malvagia, quando il numero asintotico di confronti falliti si ridimensiona come log(n)per i nconfronti).

Un posto da cui iniziare a leggere è il documento di Huang e Abraham del 1984 " Tolleranza ai guasti basata su algoritmi per operazioni a matrice ". La loro idea è vagamente simile al calcolo crittografato omomorfo (ma non è proprio la stessa, dal momento che stanno tentando di rilevare / correggere errori a livello operativo).

Un discendente più recente di quel documento è la " tolleranza agli errori basata su algoritmo applicata all'algoritmo basato su algoritmi " di Bosilca, Delmas, Dongarra e Langou .


5
Mi piace molto la tua risposta. Questo è un approccio software più generico all'integrità dei dati e nel nostro prodotto finale verrà utilizzata una soluzione di tolleranza d'errore basata su algoritmi. Grazie!
Torre

41

La scrittura di codice per ambienti radioattivi non è molto diversa dalla scrittura di codice per qualsiasi applicazione mission-critical.

Oltre a ciò che è già stato menzionato, ecco alcuni suggerimenti vari:

  • Utilizzare le misure di sicurezza quotidiane "bread & butter" che dovrebbero essere presenti su qualsiasi sistema semi-professionale incorporato: watchdog interno, rilevamento interno a bassa tensione, monitor interno dell'orologio. Queste cose non dovrebbero nemmeno essere menzionate nel 2016 e sono standard su quasi tutti i moderni microcontrollori.
  • Se disponi di un MCU orientato alla sicurezza e / o automobilistico, avrà alcune funzionalità di watchdog, come una determinata finestra temporale, all'interno della quale devi aggiornare il watchdog. Questo è preferito se si dispone di un sistema in tempo reale di importanza critica.
  • In generale, usa un MCU adatto a questo tipo di sistemi e non qualche lanugine generica che hai ricevuto in un pacchetto di corn flakes. Quasi tutti i produttori di MCU al giorno d'oggi dispongono di MCU specializzate progettate per applicazioni di sicurezza (TI, Freescale, Renesas, ST, Infineon ecc. Ecc.). Questi hanno molte funzioni di sicurezza integrate, inclusi i core lock-step: ciò significa che ci sono 2 core CPU che eseguono lo stesso codice e devono concordare tra loro.
  • IMPORTANTE: è necessario garantire l'integrità dei registri MCU interni. Tutti i registri di controllo e di stato delle periferiche hardware che sono scrivibili possono trovarsi nella memoria RAM e sono quindi vulnerabili.

    Per proteggerti dalla corruzione dei registri, preferibilmente scegli un microcontrollore con funzionalità "write-once" integrate nei registri. Inoltre, è necessario memorizzare i valori predefiniti di tutti i registri hardware in NVM e copiarli a intervalli regolari nei registri. È possibile garantire l'integrità di variabili importanti allo stesso modo.

    Nota: utilizzare sempre la programmazione difensiva. Ciò significa che è necessario impostare tutti i registri nell'MCU e non solo quelli utilizzati dall'applicazione. Non vuoi che qualche periferica hardware casuale si riattivi improvvisamente.

  • Esistono tutti i tipi di metodi per verificare la presenza di errori nella RAM o nell'NVM: checksum, "schemi di deambulazione", software ECC ecc. La soluzione migliore al giorno d'oggi è quella di non utilizzare nessuno di questi, ma di utilizzare un MCU con ECC incorporato e controlli simili. Perché farlo nel software è complesso e il controllo degli errori in sé potrebbe quindi introdurre bug e problemi imprevisti.

  • Usa ridondanza. È possibile archiviare sia la memoria volatile che non volatile in due segmenti identici "mirror", che devono sempre essere equivalenti. Ogni segmento potrebbe avere un checksum CRC allegato.
  • Evitare l'uso di memorie esterne al di fuori dell'MCU.
  • Implementare un gestore delle eccezioni predefinito / gestore delle eccezioni predefinito per tutte le possibili interruzioni / eccezioni. Anche quelli che non stai usando. La routine predefinita non dovrebbe fare altro che spegnere la propria fonte di interrupt.
  • Comprendere e abbracciare il concetto di programmazione difensiva. Ciò significa che il tuo programma deve gestire tutti i casi possibili, anche quelli che non possono verificarsi in teoria. Esempi .

    Il firmware mission-critical di alta qualità rileva il maggior numero possibile di errori e li ignora in modo sicuro.

  • Non scrivere mai programmi che si basano su comportamenti mal specificati. È probabile che tale comportamento possa cambiare drasticamente con cambiamenti hardware imprevisti causati da radiazioni o EMI. Il modo migliore per garantire che il tuo programma sia libero da tali schifezze è quello di utilizzare uno standard di codifica come MISRA, insieme a uno strumento di analisi statica. Questo aiuterà anche con la programmazione difensiva e con l'eliminazione dei bug (perché non dovresti voler rilevare i bug in qualsiasi tipo di applicazione?).
  • IMPORTANTE: non implementare alcuna dipendenza dai valori predefiniti delle variabili di durata della memoria statica. Cioè, non fidarti dei contenuti predefiniti di .datao.bss . Potrebbe esserci un qualsiasi intervallo di tempo tra il punto di inizializzazione e il punto in cui la variabile viene effettivamente utilizzata, ci potrebbe essere stato un sacco di tempo perché la RAM si corrompesse. Invece, scrivi il programma in modo che tutte queste variabili siano impostate da NVM in fase di esecuzione, appena prima del momento in cui tale variabile viene utilizzata per la prima volta.

    In pratica ciò significa che se una variabile viene dichiarata nell'ambito del file o come static, non si dovrebbe mai usare =per inizializzarla (o si potrebbe, ma è inutile, perché non si può fare affidamento sul valore comunque). Impostalo sempre in fase di esecuzione, appena prima dell'uso. Se è possibile aggiornare ripetutamente tali variabili da NVM, quindi farlo.

    Allo stesso modo in C ++, non fare affidamento su costruttori per variabili di durata della memoria statica. Chiedi al costruttore (i) di chiamare una routine pubblica di "impostazione", che puoi anche chiamare in seguito in fase di esecuzione, direttamente dall'applicazione chiamante.

    Se possibile, rimuovere il codice di avvio "copia giù" che inizializza .datae .bss(e chiama i costruttori C ++) in modo da ottenere errori del linker se si scrive codice basato su tale. Molti compilatori hanno la possibilità di saltare questo, di solito chiamato "avvio minimo / veloce" o simile.

    Ciò significa che tutte le librerie esterne devono essere controllate in modo da non contenere tale affidamento.

  • Implementare e definire uno stato sicuro per il programma, in cui si tornerà in caso di errori critici.

  • L'implementazione di una segnalazione errori / registro errori è sempre utile.

Un modo per gestire la corruzione dei booleani (come nel tuo link di esempio) potrebbe essere quello di renderlo TRUEuguale per 0xffffffffpoi usarlo POPCNTcon una soglia.
wizzwizz4,

@ wizzwizz4 Dato che il valore 0xff è il valore predefinito della cella flash non programmata, sembra una cattiva idea.
Lundin,

%01010101010101010101010101010101, XOR poi POPCNT?
wizzwizz4,

1
@ wizzwizz4 O semplicemente il valore 0x1, come richiesto dallo standard C.
Lundin,

1
@ wizzwizz4 Perché usi alcuni o tutti i metodi sopra menzionati (ECC, CRC ecc.). In caso contrario, il raggio cosmico potrebbe anche capovolgere un singolo bit nella .textsezione, modificando un codice operativo o simile.
Lundin,

34

Potrebbe essere possibile usare C per scrivere programmi che si comportano in modo robusto in tali ambienti, ma solo se la maggior parte delle forme di ottimizzazione del compilatore sono disabilitate. I compilatori di ottimizzazione sono progettati per sostituire molti schemi di codifica apparentemente ridondanti con modelli "più efficienti" e potrebbero non avere la minima idea che il motivo per cui il programmatore sta testando x==42quando il compilatore sa che non c'è modo di xtrattenere nient'altro è perché il programmatore vuole impedire l'esecuzione di un determinato codice con xun altro valore - anche nei casi in cui l'unico modo in cui potrebbe contenere quel valore sarebbe se il sistema ricevesse un qualche tipo di problema elettrico.

Dichiarare le variabili volatileè spesso utile, ma potrebbe non essere una panacea. Di particolare importanza, notare che la codifica sicura richiede spesso che le operazioni pericolose abbiano interblocchi hardware che richiedono più passaggi per l'attivazione e che il codice sia scritto usando il modello:

... 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();

Se un compilatore traduce il codice in modo relativamente letterale, e se tutti i controlli per lo stato del sistema vengono ripetuti dopo il prepare_for_activation(), il sistema potrebbe essere robusto contro quasi ogni singolo evento glitch plausibile, anche quelli che corromperebbero arbitrariamente il contatore e lo stack del programma. Se un glitch si verifica subito dopo una chiamata a prepare_for_activation(), ciò implicherebbe che l'attivazione sarebbe stata appropriata (dal momento che non vi era altra ragione che prepare_for_activation()sarebbe stata chiamata prima del glitch). Se il glitch fa in modo che il codice raggiunga in modo prepare_for_activation()inappropriato, ma non ci sono eventi glitch successivi, non ci sarebbe modo per raggiungere successivamente il codice trigger_activation()senza aver superato il controllo di convalida o chiamando prima Cancel_preparations [se lo stack non funziona, l'esecuzione potrebbe procedere in un punto appena primatrigger_activation()dopo che il contesto che ha chiamato prepare_for_activation()ritorna, ma la chiamata a cancel_preparations()sarebbe avvenuta tra le chiamate a , rendendo così quest'ultima innocua.prepare_for_activation() etrigger_activation()

Tale codice può essere sicuro nei tradizionali C, ma non con i moderni compilatori C. Tali compilatori possono essere molto pericolosi in quel tipo di ambiente perché aggressivi si sforzano di includere solo codice che sarà rilevante in situazioni che potrebbero verificarsi attraverso un meccanismo ben definito e le cui conseguenze risultanti sarebbero anche ben definite. Il codice il cui scopo sarebbe quello di rilevare e ripulire dopo guasti può, in alcuni casi, finire per peggiorare le cose. Se il compilatore determina che il tentativo di recupero in alcuni casi invocherebbe un comportamento indefinito, potrebbe dedurre che le condizioni che richiederebbero tale recupero in tali casi non possono verificarsi, eliminando così il codice che avrebbe verificato per loro.


6
Realisticamente parlando, quanti compilatori moderni ci sono che non offrono -O0o un interruttore equivalente? GCC farà molte cose strane se gli dai il permesso , ma se gli chiedi di non farle, in genere è anche abbastanza letterale.
Leushenko,

24
Ci dispiace, ma questa idea è fondamentalmente pericolosa. La disabilitazione delle ottimizzazioni produce un programma più lento. O, in altre parole, hai bisogno di una CPU più veloce. In effetti, le CPU più veloci sono più veloci perché le cariche sui loro gate dei transistor sono più piccole. Questo li rende molto più sensibili alle radiazioni. La strategia migliore è quella di utilizzare un chip grande e lento in cui un singolo fotone ha molte meno probabilità di ribaltarsi un po 'e di recuperare la velocità -O2.
Salterio,

27
Un motivo secondario per cui -O0è una cattiva idea è perché emette istruzioni molto più inutili. Esempio: una chiamata non incorporata contiene istruzioni per salvare i registri, effettuare la chiamata, ripristinare i registri. Tutti questi possono fallire. Un'istruzione che non c'è non può fallire.
Salterio

15
Ancora un altro motivo per cui -O0è una cattiva idea: tende a memorizzare le variabili in memoria anziché in un registro. Ora non è certo che la memoria sia più sensibile alle SEU, ma i dati in volo sono più sensibili dei dati a riposo. Il movimento inutile dei dati dovrebbe essere evitato e -O2aiuta lì.
Salterio,

9
@MSalters: L'importante non è che i dati siano immuni alle interruzioni, ma piuttosto che il sistema sia in grado di gestire le interruzioni in modo da soddisfare i requisiti. Su molti compilatori la disabilitazione di tutte le ottimizzazioni produce codice che esegue un numero eccessivo di mosse da registro a registro, il che è negativo, ma la memorizzazione delle variabili in memoria è più sicura dal punto di vista del recupero rispetto al mantenimento in registri. Se uno ha due variabili in memoria che dovrebbero obbedire a qualche condizione (ad es. v1=v2+0xCAFEBABEE tutti gli aggiornamenti alle due variabili sono fatti ...
supercat

29

Questo è un argomento estremamente vasto. Fondamentalmente, non puoi davvero recuperare dalla corruzione della memoria, ma puoi almeno provare a fallire prontamente . Ecco alcune tecniche che potresti usare:

  • dati costanti checksum . Se si dispone di dati di configurazione che rimangono costanti per lungo tempo (inclusi i registri hardware configurati), calcolare il relativo checksum all'inizializzazione e verificarlo periodicamente. Quando vedi una mancata corrispondenza, è tempo di reinizializzare o ripristinare.

  • memorizzare variabili con ridondanza . Se si dispone di una variabile importante x, scrivere il suo valore in x1, x2ed x3e leggerlo come (x1 == x2) ? x2 : x3.

  • implementare il monitoraggio del flusso del programma . XOR una bandiera globale con un valore univoco in importanti funzioni / rami chiamati dal ciclo principale. L'esecuzione del programma in un ambiente privo di radiazioni con una copertura del test vicina al 100% dovrebbe fornire l'elenco dei valori accettabili della bandiera alla fine del ciclo. Ripristina se vedi deviazioni.

  • monitorare il puntatore dello stack . All'inizio del ciclo principale, confronta il puntatore dello stack con il suo valore atteso. Ripristina su deviazione.


27

Ciò che potrebbe aiutarti è un cane da guardia . I cani da guardia sono stati ampiamente utilizzati nell'informatica industriale negli anni '80. I guasti hardware erano molto più comuni allora - un'altra risposta si riferisce anche a quel periodo.

Un cane da guardia è una funzionalità hardware / software combinata. L'hardware è un semplice contatore che conta da un numero (diciamo 1023) a zero. È possibile utilizzare TTL o altra logica.

Il software è stato progettato in modo tale che una routine controlli il corretto funzionamento di tutti i sistemi essenziali. Se questa routine viene completata correttamente = trova il computer funzionante, reimposta il contatore su 1023.

Il design complessivo è tale che, in circostanze normali, il software impedisce che il contatore hardware raggiunga lo zero. Nel caso in cui il contatore raggiunga lo zero, l'hardware del contatore esegue la sua attività unica e reimposta l'intero sistema. Dal punto di vista del contatore, zero è uguale a 1024 e il contatore continua di nuovo il conto alla rovescia.

Questo watchdog garantisce il riavvio del computer collegato in molti, molti casi di errore. Devo ammettere che non ho familiarità con l'hardware in grado di svolgere tale funzione sui computer di oggi. Le interfacce verso l'hardware esterno sono ora molto più complesse di quanto non fossero in passato.

Uno svantaggio intrinseco del watchdog è che il sistema non è disponibile dal momento in cui fallisce fino a quando il contatore del watchdog raggiunge zero + tempo di riavvio. Mentre quel tempo è generalmente molto più breve di qualsiasi intervento esterno o umano, le apparecchiature supportate dovranno essere in grado di procedere senza il controllo del computer per quel periodo di tempo.


9
I watchdog da banco binari con circuiti integrati standard TTL rappresentano una soluzione degli anni '80. Non farlo. Oggi non esiste un unico MCU sul mercato senza circuiti integrati di sorveglianza. Tutto quello che devi controllare è se il watchdog integrato ha una sorgente di clock individuale (buono, molto probabilmente il caso) o se eredita il suo clock dall'orologio di sistema (cattivo).
Lundin,

1
O implementare il watchdog in un FPGA: ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20130013486.pdf
nn

2
Ancora ampiamente utilizzato nei processori integrati, per inciso.
Graham,

5
@Peter Mortensen Ti preghiamo gentilmente di interrompere la tua folle modifica su ogni risposta a questa domanda. Questa non è Wikipedia, e quei collegamenti non sono utili (e sono sicuro che tutti sanno come trovare Wikipedia comunque ...). Molte delle tue modifiche sono errate perché non conosci l'argomento. Sto eseguendo i rollback delle tue modifiche errate mentre le incontro. Non stai trasformando questa discussione meglio, ma peggio. Ferma la modifica.
Lundin,

Jack Ganssle ha un buon articolo sui cani da guardia: ganssle.com/watchdogs.htm
Igor Skochinsky,

23

Questa risposta presuppone che ti preoccupi di avere un sistema che funzioni correttamente, oltre che di avere un sistema che sia a costo minimo o veloce; la maggior parte delle persone che giocano con cose radioattive apprezzano la correttezza / sicurezza rispetto alla velocità / ai costi

Diverse persone hanno suggerito di apportare modifiche hardware (va bene - ci sono già molte cose buone nelle risposte e non ho intenzione di ripeterle tutte), e altre hanno suggerito la ridondanza (ottima in linea di principio), ma non credo qualcuno ha suggerito come tale ridondanza potrebbe funzionare nella pratica. Come fallire? Come fai a sapere quando qualcosa è andato storto? Molte tecnologie funzionano sulla base di tutto funzionerà e il fallimento è quindi una cosa difficile da affrontare. Tuttavia, alcune tecnologie informatiche distribuite progettate per la scala prevedono errori (dopo tutto con una scala sufficiente, il fallimento di un nodo di molti è inevitabile con qualsiasi MTBF per un singolo nodo); puoi sfruttarlo per il tuo ambiente.

Ecco alcune idee:

  • Assicurarsi che tutto l'hardware venga replicato nvolte (dove nè maggiore di 2 e preferibilmente dispari) e che ciascun elemento hardware possa comunicare tra loro. Ethernet è un modo ovvio per farlo, ma ci sono molti altri percorsi molto più semplici che offrirebbero una migliore protezione (ad es. CAN). Ridurre al minimo i componenti comuni (anche gli alimentatori). Ciò può significare, ad esempio, il campionamento di ingressi ADC in più punti.

  • Assicurarsi che lo stato dell'applicazione sia in un unico posto, ad esempio in una macchina a stati finiti. Questo può essere interamente basato su RAM, anche se non impedisce l'archiviazione stabile. Sarà quindi conservato in più punti.

  • Adottare un protocollo quorum per i cambi di stato. Vedi RAFT per esempio. Mentre lavori in C ++, ci sono librerie ben note per questo. Le modifiche all'FSM verranno apportate solo quando la maggioranza dei nodi è d'accordo. Utilizzare una libreria nota per lo stack di protocollo e il protocollo quorum invece di farne uno da solo, altrimenti tutto il buon lavoro sulla ridondanza verrà sprecato quando si blocca il protocollo quorum.

  • Assicurati di avere il checksum (ad es. CRC / SHA) il tuo FSM e di archiviare il CRC / SHA nello stesso FSM (oltre a trasmettere nel messaggio e controllare i messaggi stessi). Chiedere ai nodi di verificare regolarmente il proprio FSM rispetto a questi checksum, verificare i messaggi in arrivo e verificare che il loro checksum corrisponda al checksum del quorum.

  • Crea quanti più controlli interni possibili nel tuo sistema, facendo riavviare i nodi che rilevano il loro errore (è meglio che continuare a lavorare a metà purché tu abbia abbastanza nodi). Tentativo di lasciarli rimuovere in modo pulito dal quorum durante il riavvio nel caso in cui non si ripresentino. Al riavvio, fare in modo che eseguano il checksum dell'immagine del software (e qualsiasi altra cosa vengano caricati) ed eseguire un test completo della RAM prima di reintrodurre il quorum.

  • Usa l'hardware per supportarti, ma fallo con attenzione. È possibile ottenere RAM ECC, ad esempio, e leggerlo / scriverlo regolarmente per correggere gli errori ECC (e il panico se l'errore non è correggibile). Tuttavia (dalla memoria) la RAM statica è molto più tollerante alle radiazioni ionizzanti rispetto alla DRAM, in primo luogo, quindi potrebbe essere preferibile utilizzare invece la DRAM statica. Vedi anche il primo punto in "Cose che non farei".

Supponiamo che tu abbia l'1% di probabilità di errore di un dato nodo entro un giorno e facciamo finta di poter rendere gli errori completamente indipendenti. Con 5 nodi, ne avrai bisogno tre in un giorno, con una probabilità del 0,00001%. Con più, beh, hai avuto l'idea.

Cose che non farei:

  • Sottovalutare il valore di non avere il problema con cui iniziare. A meno che il peso non sia un problema, un grande blocco di metallo attorno al dispositivo sarà una soluzione molto più economica e più affidabile di quella che un team di programmatori può inventare. L'accoppiamento ottico degli ingressi di EMI è un problema, ecc. Qualunque cosa, cerca di procurarti i tuoi componenti per procurarti quelli con la migliore valutazione contro le radiazioni ionizzanti.

  • Crea i tuoi algoritmi . Le persone hanno già fatto queste cose. Usa il loro lavoro. La tolleranza agli errori e gli algoritmi distribuiti sono difficili. Usa il lavoro di altre persone dove possibile.

  • Usa complicate impostazioni del compilatore nella speranza ingenua di rilevare più errori. Se sei fortunato, potresti rilevare più guasti. Più probabilmente, all'interno del compilatore verrà utilizzato un percorso di codice che è stato meno testato, in particolare se lo si è eseguito da soli.

  • Utilizzare tecniche non testate nel proprio ambiente. La maggior parte delle persone che scrivono software ad alta disponibilità devono simulare le modalità di errore per verificare il corretto funzionamento dell'HA e, di conseguenza, perdere molte modalità di errore. Sei nella posizione "fortunata" di avere frequenti guasti su richiesta. Quindi prova ogni tecnica e assicurati che la sua applicazione migliori effettivamente l'MTBF di una quantità che superi la complessità per introdurla (con la complessità derivano bug). In particolare, applicalo ai miei consigli sugli algoritmi di quorum, ecc.


2
Probabilmente Ethernet non è un'ottima idea da utilizzare in applicazioni mission-critical. Né I2C, al di fuori del PCB stesso. Qualcosa di robusto come CAN sarebbe molto più adatto.
Lundin,

1
@Lundin Fair point, anche se qualsiasi cosa otticamente connessa (incl. Ethernet) dovrebbe essere OK.
circa il

1
Il supporto fisico non è tanto il motivo per cui Ethernet non è adatto, ma la mancanza di comportamenti deterministici in tempo reale. Anche se suppongo che al giorno d'oggi ci siano modi per fornire anche Ethernet in qualche modo affidabile, la raggruppo semplicemente con l'elettronica commerciale / giocattolo per abitudine.
Lundin,

1
@Lundin questo è un punto giusto, ma come sto suggerendo di usarlo per eseguire RAFT, ci sarà comunque (teoricamente) un comportamento in tempo reale non deterministico nell'algoritmo (ad es. Elezioni simultanee dei leader che porteranno a ripetere le elezioni simili a CSMA / CD). Se è necessario un comportamento rigoroso in tempo reale, probabilmente la mia risposta ha più problemi di ethernet (e nota alla testa della mia risposta ho detto che "corretto" probabilmente andrebbe a scapito di "veloce" spesso). Tuttavia ho incorporato il tuo punto di vista CAN.
circa

1
@Lundin: nessun sistema che coinvolge aspetti asincroni può essere completamente non deterministico. Penso che il comportamento peggiore di Ethernet possa essere limitato in assenza di interruzioni hardware se i protocolli software sono impostati in modo adeguato e i dispositivi hanno ID univoci e esiste un limite noto al numero di dispositivi (più dispositivi, più grande è il numero peggiore di tentativi).
supercat

23

Dato che richiedi specificamente soluzioni software e stai utilizzando C ++, perché non utilizzare il sovraccarico dell'operatore per creare i tuoi tipi di dati sicuri? Per esempio:

Invece di usare uint32_t(e double, int64_tecc.), Creane uno tuoSAFE_uint32_t che contiene un multiplo (minimo 3) di uint32_t. Sovraccaricare tutte le operazioni che si desidera eseguire (* + - / << >> = ==! = Etc) e fare in modo che le operazioni sovraccaricate vengano eseguite in modo indipendente su ciascun valore interno, ovvero non eseguire una volta e copiare il risultato. Sia prima che dopo, controlla che tutti i valori interni corrispondano. Se i valori non corrispondono, puoi aggiornare quello sbagliato al valore con quello più comune. Se non esiste un valore più comune, è possibile comunicare in modo sicuro che si è verificato un errore.

In questo modo non importa se si verifica corruzione nell'ALU, nei registri, nella RAM o su un bus, avrai comunque più tentativi e ottime possibilità di rilevare errori. Si noti tuttavia che questo funziona solo per le variabili che è possibile sostituire, ad esempio il puntatore dello stack sarà comunque sensibile.

Una storia secondaria: ho riscontrato un problema simile, anche su un vecchio chip ARM. Si è rivelata una toolchain che utilizzava una vecchia versione di GCC che, insieme al chip specifico che usavamo, in alcuni casi limite causava un bug che avrebbe (a volte) corrotto il passaggio di valori in funzioni. Assicurati che il tuo dispositivo non abbia problemi prima di incolpare l'attività radio e sì, a volte è un bug del compilatore =)


1
Alcuni di questi suggerimenti hanno qualcosa di simile a una mentalità simile a "multi-bit sanity check" per rilevare la corruzione, mi piace molto questo con il suggerimento dei tipi di dati personalizzati critici per la sicurezza, il più però
WearyWanderer,

2
Esistono sistemi al mondo in cui ogni nodo ridondante è stato progettato e sviluppato da diversi team, con un arbitro che si assicura che non si accontenti accidentalmente delle stesse soluzioni. In questo modo non li farai cadere tutti per lo stesso bug e transitori simili non manifestano simili modalità di fallimento.
jwdonahue,

16

Disclaimer: non sono un professionista della radioattività né ho lavorato per questo tipo di applicazione. Ma ho lavorato su errori lievi e ridondanza per l'archiviazione a lungo termine di dati critici, che è in qualche modo collegata (stesso problema, obiettivi diversi).

Il problema principale con la radioattività secondo me è che la radioattività può cambiare bit, quindi la radioattività può / manomettere qualsiasi memoria digitale . Questi errori sono generalmente chiamati errori soft , bit rot, ecc.

La domanda è quindi: come calcolare in modo affidabile quando la memoria non è affidabile?

Per ridurre significativamente il tasso di errori soft (a spese del sovraccarico computazionale poiché si tratterà principalmente di soluzioni basate su software), è possibile:

  • contare sul buon vecchio schema di ridondanza e più specificamente sui codici di correzione degli errori più efficienti(stesso scopo, ma algoritmi più intelligenti in modo da poter recuperare più bit con meno ridondanza). Questo è talvolta (erroneamente) anche chiamato checksumming. Con questo tipo di soluzione, dovrai archiviare lo stato completo del tuo programma in qualsiasi momento in una variabile / classe principale (o una struttura?), Calcolare un ECC e verificare che l'ECC sia corretto prima di fare qualsiasi cosa, e se no, ripara i campi. Questa soluzione tuttavia non garantisce che il tuo software possa funzionare (semplicemente che funzionerà correttamente quando può, o smette di funzionare in caso contrario, perché ECC può dirti se qualcosa non va, e in questo caso puoi interrompere il tuo software in modo che tu non ottenere risultati falsi).

  • oppure è possibile utilizzare strutture dati algoritmiche resilienti, che garantiscono, fino a un certo limite, che il tuo programma fornirà comunque risultati corretti anche in presenza di errori soft. Questi algoritmi possono essere visti come un mix di strutture algoritmiche comuni con schemi ECC mescolati nativamente, ma questo è molto più resistente di così, perché lo schema di resilienza è strettamente legato alla struttura, quindi non è necessario codificare procedure aggiuntive per controllare l'ECC, e di solito sono molto più veloci. Queste strutture forniscono un modo per garantire che il programma funzioni in qualsiasi condizione, fino al limite teorico degli errori soft. Puoi anche mescolare queste strutture resilienti con lo schema di ridondanza / ECC per maggiore sicurezza (o codificare le tue strutture dati più importanti come resilienti e il resto, i dati sacrificabili che puoi ricalcolare dalle strutture dati principali,

Se sei interessato a strutture di dati resilienti (che è un campo recente, ma interessante, di algoritmo e ingegneria di ridondanza), ti consiglio di leggere i seguenti documenti:

  • Introduzione di strutture dati di algoritmi resilienti di Giuseppe F.Italiano, Università di Roma "Tor Vergata"

  • Christiano, P., Demaine, ED, & Kishore, S. (2011). Strutture di dati tolleranti ai guasti senza perdite con sovraccarico aggiuntivo. In Algorithms and Data Structures (pagg. 243-254). Springer Berlin Heidelberg.

  • Ferraro-Petrillo, U., Grandoni, F., & Italiano, GF (2013). Strutture dati resistenti ai difetti di memoria: uno studio sperimentale di dizionari. Journal of Experimental Algorithmics (JEA), 18, 1-6.

  • Italiano, GF (2010). Algoritmi e strutture dati resilienti. In Algorithms and Complexity (pagg. 13-24). Springer Berlin Heidelberg.

Se sei interessato a saperne di più sul campo delle strutture di dati resilienti, puoi controllare le opere di Giuseppe F. Italiano (e farti strada attraverso i ref) e il modello Faulty-RAM (introdotto in Finocchi et al. 2005; Finocchi e Italiano 2008).

/ EDIT: ho illustrato la prevenzione / recupero da errori soft principalmente per la memoria RAM e l'archiviazione dei dati, ma non ho parlato di errori di calcolo (CPU) . Altre risposte hanno già indicato l'utilizzo di transazioni atomiche come nei database, quindi proporrò un altro schema più semplice: ridondanza e voto a maggioranza .

L'idea è che si esegue semplicemente x volte lo stesso calcolo per ogni calcolo che è necessario eseguire e si archivia il risultato in x variabili diverse (con x> = 3). È quindi possibile confrontare le variabili x :

  • se tutti sono d'accordo, allora non c'è nessun errore di calcolo.
  • se non sono d'accordo, è possibile utilizzare un voto a maggioranza per ottenere il valore corretto e poiché ciò significa che il calcolo è stato parzialmente danneggiato, è inoltre possibile attivare una scansione dello stato del sistema / programma per verificare che il resto sia corretto.
  • se il voto di maggioranza non è in grado di determinare un vincitore (tutti i valori di x sono diversi), allora è un segnale perfetto per te innescare la procedura di sicurezza (riavviare, inviare un avviso all'utente, ecc.).

Questo schema di ridondanza è molto veloce rispetto all'ECC (praticamente O (1)) e fornisce un segnale chiaro quando è necessario il fail - safe . Il voto di maggioranza è anche (quasi) garantito per non produrre mai output danneggiati e anche per recuperare da piccoli errori di calcolo , perché la probabilità che i calcoli x forniscano lo stesso output è infinitesimale (perché c'è un'enorme quantità di output possibili, è quasi impossibile casualmente ottieni 3 volte lo stesso, ancora meno possibilità se x> 3).

Quindi, con il voto a maggioranza sei al sicuro dall'output corrotto e con la ridondanza x == 3, puoi recuperare 1 errore (con x == 4 saranno 2 errori recuperabili, ecc. - l'equazione esatta è nb_error_recoverable == (x-2) dove x è il numero di ripetizioni di calcolo perché sono necessari almeno 2 calcoli concordanti per recuperare utilizzando il voto a maggioranza).

Lo svantaggio è che devi calcolare x volte anziché una volta, quindi hai un costo di calcolo aggiuntivo, ma la complessità lineare è così asintoticamente che non perdi molto per i benefici che ottieni. Un modo rapido per votare a maggioranza è calcolare la modalità su un array, ma è anche possibile utilizzare un filtro mediano.

Inoltre, se vuoi assicurarti che i calcoli siano condotti correttamente, se puoi creare il tuo hardware puoi costruire il tuo dispositivo con x CPU e cablare il sistema in modo che i calcoli vengano duplicati automaticamente tra le x CPU con la maggioranza dei voti fatta meccanicamente alla fine (usando ad esempio porte AND / OR). Questo è spesso implementato in aeroplani e dispositivi mission-critical (vedi tripla ridondanza modulare ). In questo modo, non si verificherebbe alcun sovraccarico computazionale (poiché i calcoli aggiuntivi verranno eseguiti in parallelo) e si avrà un altro livello di protezione da errori software (poiché la duplicazione del calcolo e il voto di maggioranza saranno gestiti direttamente dall'hardware e non da software - che può essere più facilmente danneggiato poiché un programma è semplicemente bit archiviati in memoria ...).


9

Un punto che nessuno sembra aver menzionato. Dici che stai sviluppando in GCC e cross-compilando su ARM. Come fai a sapere che non hai un codice che fa ipotesi su RAM libera, dimensione intera, dimensione del puntatore, quanto tempo impiega una determinata operazione, per quanto tempo il sistema funzionerà continuamente o cose del genere? Questo è un problema molto comune

La risposta è di solito test unitari automatizzati. Scrivere i cablaggi di prova che esercitano il codice sul sistema di sviluppo, quindi eseguire gli stessi cablaggi di prova sul sistema di destinazione. Cerca le differenze!

Controlla anche gli errata sul tuo dispositivo incorporato. Potresti scoprire che c'è qualcosa in "non farlo perché si arresta in modo anomalo, quindi abilita l'opzione del compilatore e il compilatore funzionerà attorno ad esso".

In breve, la fonte più probabile di arresti anomali sono i bug nel codice. Fino a quando non ti sarai assicurato dannatamente che non sia così, non preoccuparti (ancora) delle modalità di fallimento più esoteriche.


1
In effetti, da nessuna parte nel test della domanda l'autore menziona che l'applicazione è stata trovata per funzionare bene all'esterno dell'ambiente radioattivo.
Marc.2377,

9

Vuoi 3+ macchine slave con un master al di fuori dell'ambiente di radiazione. Tutto l'I / O passa attraverso il master che contiene un meccanismo di voto e / o tentativo. Gli slave devono avere un watchdog hardware ciascuno e la chiamata a scontrarli dovrebbe essere circondata da CRC o simili per ridurre la probabilità di urti involontari. Il bumping dovrebbe essere controllato dal master, quindi la connessione persa con il master equivale al riavvio in pochi secondi.

Un vantaggio di questa soluzione è che puoi usare la stessa API sia per il master che per gli slave, in modo che la ridondanza diventi una funzione trasparente.

Modifica: dai commenti sento la necessità di chiarire l '"idea CRC". La possibilità che lo schiavo urti il ​​proprio cane da guardia è quasi zero se si circonda l'urto con CRC o si eseguono controlli digest su dati casuali dal master. I dati casuali vengono inviati dal master solo quando lo slave sottoposto a controllo è allineato con gli altri. I dati casuali e CRC / digest vengono immediatamente cancellati dopo ogni bump. La frequenza di bump master-slave dovrebbe essere più del doppio del timeout del watchdog. I dati inviati dal master vengono generati in modo univoco ogni volta.


7
Sto cercando di capire uno scenario in cui puoi avere un maestro fuori dall'ambiente di radiazione, in grado di comunicare in modo affidabile con gli schiavi all'interno dell'ambiente di radiazione, dove non puoi semplicemente mettere gli schiavi fuori dall'ambiente di radiazione.
Fostandy,

1
@fostandy: gli schiavi misurano o controllano usando apparecchiature che richiedono un controller. Di 'un contatore Geiger. Il master non necessita di comunicazioni affidabili a causa della ridondanza degli slave.
Jonas Byström,

4
L'introduzione di un master non implica automaticamente una maggiore sicurezza. Se lo slave x è impazzito a causa della corruzione della memoria, in modo che si ripeta ripetutamente "il master è qui, il master è felice", allora nessuna quantità di CRC o ordini abbaiati dal master lo salverà. Dovresti dare al padrone la possibilità di tagliare il potere di quello schiavo. E se si verifica un errore di causa comune, l'aggiunta di più slave non aumenterà la sicurezza. Inoltre, tieni presente che la quantità di bug del software e la quantità di cose che possono rompersi aumentano con la complessità.
Lundin,

5
Detto questo, sarebbe ovviamente bello "esternalizzare" gran parte del programma in un posto meno esposto, mantenendo l'elettronica all'interno dell'ambiente radioattivo il più semplice possibile, se si dispone di tale opzione.
Lundin,

7

Che ne dici di eseguire molte istanze della tua applicazione. Se gli arresti anomali sono dovuti a modifiche casuali dei bit di memoria, è probabile che alcune delle tue istanze dell'app riescano e producano risultati accurati. Probabilmente è abbastanza facile (per qualcuno con background statistico) calcolare quanti casi hai bisogno data la probabilità di bit flop per ottenere il minimo errore complessivo che desideri.


2
Sicuramente un sistema incorporato preferirebbe di gran lunga le catture critiche per la sicurezza in un'istanza di un'applicazione robusta piuttosto che semplicemente innescare diverse istanze, aumentando i requisiti hardware e sperando in una certa fortuna in cieco fortuna che almeno un'istanza riesca a superare bene? Ho avuto l'idea ed è valida, ma mi appoggio maggiormente ai suggerimenti che non si basano sulla forza bruta
WearyWanderer,

7

Quello che chiedi è un argomento piuttosto complesso - non facilmente rispondibile. Altre risposte sono ok, ma hanno coperto solo una piccola parte di tutte le cose che devi fare.

Come visto nei commenti , non è possibile risolvere i problemi hardware al 100%, tuttavia è possibile con probabilità elevata ridurli o catturarli utilizzando varie tecniche.

Se fossi in te, creerei il software con il più alto livello di integrità di sicurezza (SIL-4). Ottieni il documento IEC 61513 (per l'industria nucleare) e seguilo.


11
O meglio, leggi i requisiti tecnici e implementa quelli che hanno senso. Gran parte degli standard SIL non ha senso, se li segui dogmaticamente finirai con prodotti non sicuri e pericolosi. La certificazione SIL oggi riguarda principalmente la produzione di una tonnellata di documentazione e la corruzione di una casa di prova. Il livello SIL non dice nulla sull'effettiva sicurezza del sistema. Invece, ti consigliamo di concentrarti sulle effettive misure di sicurezza tecniche. Ce ne sono alcuni molto buoni nei documenti SIL e alcuni completi senza senso.
Lundin,

7

Qualcuno ha menzionato l'uso di chip più lenti per impedire agli ioni di lanciare i bit con la stessa facilità. In modo simile, forse usare una CPU / RAM specializzata che utilizza effettivamente più bit per memorizzare un singolo bit. Fornendo così una tolleranza agli errori hardware perché sarebbe molto improbabile che tutti i bit vengano capovolti. Quindi 1 = 1111 ma dovrebbe essere colpito 4 volte per essere effettivamente capovolto. (4 potrebbe essere un numero negativo poiché se 2 bit vengono capovolti è già ambiguo). Quindi se vai con 8, ottieni 8 volte meno ram e un po 'di tempo di accesso più lento ma una rappresentazione dei dati molto più affidabile. Probabilmente potresti farlo sia a livello software con un compilatore specializzato (allocare x quantità di spazio in più per tutto) o implementazione del linguaggio (scrivere wrapper per strutture di dati che allocano le cose in questo modo).


7

Forse sarebbe utile sapere che significa che l'hardware deve essere "progettato per questo ambiente". Come corregge e / o indica la presenza di errori SEU?

In un progetto relativo all'esplorazione dello spazio, avevamo un MCU personalizzato, che avrebbe sollevato un'eccezione / interruzione sugli errori SEU, ma con un certo ritardo, cioè alcuni cicli potrebbero passare / le istruzioni potrebbero essere eseguite dopo quella insn che ha causato l'eccezione SEU.

Particolarmente vulnerabile era la cache di dati, quindi un gestore avrebbe invalidato la riga della cache offensiva e riavviato il programma. Solo che, a causa della natura imprecisa dell'eccezione, la sequenza di insn guidata dall'eccezione che genera insn potrebbe non essere riavviabile.

Abbiamo identificato le sequenze pericolose (non riavviabili) (come lw $3, 0x0($2), seguite da un insn, che modifica $2e non dipende dai dati $3), e ho apportato modifiche a GCC, quindi tali sequenze non si verificano (ad esempio come ultima risorsa, separando il due inss di anop ).

Solo qualcosa da considerare ...


7

Se l'hardware non funziona, è possibile utilizzare l'archiviazione meccanica per ripristinarlo. Se la tua base di codice è piccola e dispone di spazio fisico, puoi utilizzare un archivio di dati meccanici.

Inserisci qui la descrizione dell'immagine

Ci sarà una superficie di materiale che non sarà influenzata dalle radiazioni. Saranno presenti ingranaggi multipli. Un lettore meccanico girerà su tutti gli ingranaggi e sarà flessibile per muoversi su e giù. Down indica che è 0 e up indica che è 1. Da 0 e 1 puoi generare la tua base di codice.


2
Forse un supporto ottico come un CD-ROM soddisferebbe questa definizione. Avrebbe il vantaggio aggiuntivo di una grande capacità.
Wossname

2
Sì, sarà simile ma il cd rom userà di meno ma questo sarà un sistema completamente meccanico.
Hitul,

7
Mi chiedo se c'è una ragione per cui non usano i lettori di schede perforate nello spazio.
Soren,

3
@Soren La velocità e lo spazio fisico possono essere una ragione.
Hitul,

5

Utilizzare uno scheduler ciclico . Ciò consente di aggiungere tempi di manutenzione regolari per verificare la correttezza dei dati critici. Il problema più spesso riscontrato è la corruzione dello stack. Se il tuo software è ciclico, puoi reinizializzare lo stack tra i cicli. Non riutilizzare le pile per le chiamate di interruzione, impostare uno stack separato di ciascuna chiamata di interruzione importante.

Simile al concetto di watchdog sono i timer delle scadenze. Avviare un timer hardware prima di chiamare una funzione. Se la funzione non viene ripristinata prima dell'interruzione del timer di scadenza, ricaricare lo stack e riprovare. Se il problema persiste anche dopo 3/5 tentativi, è necessario ricaricare dalla ROM.

Dividi il tuo software in parti e isola queste parti per usare aree di memoria e tempi di esecuzione separati (specialmente in un ambiente di controllo). Esempio: acquisizione del segnale, dati di pre-acquisizione, algoritmo principale e implementazione / trasmissione dei risultati. Ciò significa che un errore in una parte non causerà errori nel resto del programma. Quindi, mentre stiamo riparando l'acquisizione del segnale, il resto delle attività continua su dati non aggiornati.

Tutto ha bisogno di CRC. Se esegui la RAM, anche il tuo .text ha bisogno di un CRC. Controllare regolarmente i CRC se si utilizza uno scheduler ciclico. Alcuni compilatori (non GCC) possono generare CRC per ogni sezione e alcuni processori hanno hardware dedicato per eseguire calcoli CRC, ma immagino che non rientrerebbe nell'ambito della tua domanda. Il controllo dei CRC richiede inoltre al controller ECC sulla memoria di riparare gli errori a bit singolo prima che diventi un problema.


4

Innanzitutto, progetta la tua applicazione in base al fallimento . Accertarsi che, come parte del normale funzionamento del flusso, si aspetti che venga ripristinato (a seconda dell'applicazione e del tipo di errore, soft o hard). Questo è difficile da ottenere perfetto: operazioni critiche che richiedono un certo grado di transazionalità potrebbero dover essere verificate e ottimizzate a livello di assieme in modo che un'interruzione in un punto chiave non possa comportare comandi esterni incoerenti. Errore rapido non appena viene rilevata una corruzione della memoria irrecuperabile o deviazione del flusso di controllo. Log errori se possibile.

In secondo luogo, ove possibile, correggere la corruzione e continuare . Ciò significa che il checksum e la correzione di tabelle costanti (e, se possibile, codice del programma) spesso; forse prima di ogni operazione principale o su un interrupt a tempo, e memorizzare variabili in strutture che correggono automaticamente (di nuovo prima di ogni operazione maggiore o su un interrupt a tempo prendono il voto della maggioranza da 3 e correggono se si tratta di una singola deviazione). Registra le correzioni, se possibile.

Terzo, fallimento del test . Impostare un ambiente di test ripetibile che lancia bit in memoria in modo casuale. Ciò ti consentirà di replicare le situazioni di corruzione e di aiutarti a progettare la tua applicazione attorno a esse.


3

Dati i commenti di supercat, le tendenze dei compilatori moderni e altre cose, sarei tentato di tornare ai tempi antichi e scrivere l'intero codice in allocazioni di memoria statica e assembly ovunque. Per questo tipo di totale affidabilità, penso che l'assemblaggio non comporti più una grande differenza percentuale del costo.


Sono un grande fan del linguaggio assembly (come puoi vedere dalle mie risposte ad altre domande), ma non credo sia una buona risposta. È abbastanza possibile sapere cosa aspettarsi dal compilatore per la maggior parte del codice C (in termini di valori che vivono nei registri rispetto alla memoria), e puoi sempre verificare che sia quello che ti aspettavi. Scrivere a mano un grande progetto in asm è solo una tonnellata di lavoro extra, anche se hai sviluppatori che sono molto a loro agio nel scrivere ARM asm. Forse se vuoi fare cose come calcolare lo stesso risultato 3 volte, scrivere alcune funzioni in asm ha senso. (i compilatori lo cederanno via)
Peter Cordes,

Il rischio maggiore che altrimenti deve essere bilanciato rispetto all'aggiornamento del compilatore può lasciare cambiamenti inaspettati.
Joshua,

1

Qui ci sono moltissime risposte, ma cercherò di riassumere le mie idee al riguardo.

Qualcosa si arresta in modo anomalo o non funziona correttamente potrebbe essere il risultato dei tuoi stessi errori, quindi dovrebbe essere facilmente risolvibile quando trovi il problema. Ma c'è anche la possibilità di guasti hardware - e questo è difficile se non impossibile da risolvere in generale.

Prima di tutto raccomanderei di provare a cogliere la situazione problematica effettuando il log (stack, registri, chiamate di funzione) - registrandoli da qualche parte nel file o trasmettendoli in qualche modo direttamente ("oh no - sto andando in crash").

Il ripristino da tale situazione di errore è il riavvio (se il software è ancora attivo e attivo) o il ripristino dell'hardware (ad es. Watchdog hw). Più facile iniziare dal primo.

Se il problema è legato all'hardware, la registrazione dovrebbe aiutarti a identificare in quale problema si verifica la chiamata di funzione e che può darti una conoscenza profonda di ciò che non funziona e dove.

Inoltre, se il codice è relativamente complesso - ha senso "dividerlo e conquistarlo" - ciò significa che rimuovere / disabilitare alcune chiamate di funzione in cui si sospetta il problema - in genere disabilitando metà del codice e abilitando un'altra metà - è possibile ottenere "funziona" / "non funziona" tipo di decisione dopo la quale puoi concentrarti su un'altra metà del codice. (Dove si trova il problema)

Se il problema si verifica dopo un po 'di tempo - allora si può sospettare l'overflow dello stack - quindi è meglio monitorare i registri dei punti dello stack - se crescono costantemente.

E se riesci a minimizzare completamente il tuo codice fino a quando il tipo di applicazione "ciao mondo" - e continua a fallire casualmente - allora ci si aspetta problemi hardware - e ci deve essere un "aggiornamento hardware" - il che significa inventare tale CPU / ram / ... combinazione hardware che tollererebbe meglio le radiazioni.

La cosa più importante è probabilmente come recuperare i registri se la macchina è completamente arrestata / ripristinata / non funziona - probabilmente la prima cosa che Bootstap dovrebbe fare - è tornare a casa se viene rilevata una situazione problematica.

Se è possibile nel tuo ambiente anche trasmettere un segnale e ricevere risposta - potresti provare a costruire una sorta di ambiente di debug remoto online, ma allora devi avere almeno i mezzi di comunicazione funzionanti e un processore / qualche ram funzionante. E per debug remoto intendo il tipo di approccio GDB / gdb stub o la propria implementazione di ciò che è necessario recuperare dall'applicazione (ad esempio, scaricare file di registro, scaricare stack di chiamate, scaricare ram, riavviare)


Siamo spiacenti, ma la domanda riguarda l'ambiente radioattivo in cui si verificano guasti hardware. La tua risposta riguarda l'ottimizzazione generale del software e come trovare i bug. Ma in questa situazione, i fallimenti non sono prodotti da bug
jeb

Sì, puoi incolpare anche la gravità terrestre, le ottimizzazioni del compilatore, la libreria di terze parti, l'ambiente radioattivo e così via. Ma sei sicuro che non siano i tuoi bug? :-) Salvo prova contraria, non ci credo. Ho eseguito una volta alcuni aggiornamenti del firmware e testando la situazione di spegnimento - il mio software è sopravvissuto a tutte le situazioni di spegnimento solo dopo aver corretto tutti i miei bug. (Oltre 4000 spegnimenti durante la notte) Ma è difficile credere che ci sia stato un bug in alcuni casi. Soprattutto quando parliamo di corruzione della memoria.
TarmoPikaro,

0

Ho davvero letto molte risposte fantastiche!

Ecco il mio 2 centesimo: costruire un modello statistico dell'anomalia memoria / registro, scrivendo un software per controllare la memoria o per eseguire frequenti confronti di registro. Inoltre, crea un emulatore, nello stile di una macchina virtuale in cui puoi sperimentare il problema. Immagino che se si cambiano le dimensioni della giunzione, la frequenza di clock, il fornitore, l'involucro, ecc. Si osserverebbe un comportamento diverso.

Anche la memoria del nostro PC desktop ha una certa percentuale di guasti, che tuttavia non pregiudica il lavoro quotidiano.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.