# Sistemi embedded e real-time per l'aeronautica

In questa lezione introduciamo i concetti di sistema embedded e real-time, elementi essenziali per progettare la strumentazione di bordo. Lo sviluppo di questi sistemi è ovviamente molto complesso e non trattabile in una sola lezione. Solitamente è il compito di Ingegneri Informatici ed Elettronici occuparsene e in questa lezione si vuole solo portare a conoscenza degli Ingegneri Aeronautici le dinamiche di sviluppo di questi software.

## Sistemi embedded

Definiamo prima di tutto cosa si intende per sistema embedded:
Dalla definizione è evidente che i sistemi avionici sono tutti sistemi embedded. Essi vengono utilizzati solo per la funzione per il quale sono programmati (a differenza di un normale Personal Computer) e la maggior parte delle volte utilizzano hardware appositamente progettato. La necessità di avere hardware/software dedicato è ovviamente dovuta al fatto di mantenere il livello di sicurezza entro termini ben precisi (è esperienza per tutti noi evidente che un Personal Computer non può garantire di non bloccarsi...)

### Software embedded

Il software embedded è ovviamente il software scritto per sistemi hardware embedded. Esso a differenza di una normale applicazione per PC, deve agire direttamente con il mondo esterno. Un software per un PC solitamente non agisce direttamente con le periferiche connesse, ma attraverso il sistema operativo, nel sistema embedded invece è il software stesso a controllare il singolo voltaggio sul singolo piedino del processore/scheda. Per questo motivo vengono utilizzati linguaggi di programmazione a basso livello (tipicamente C e Assembly, anche se in ambito aeronautico è spesso usato anche Ada

### Testing

Il testing è un processo molto complicato e lungo nella fase di sviluppo di un sistema embedded. Il testing può coinvolgere l'hardware, il software o l'intero sistema.

#### Testing hardware

Le schede hardware non sono assenti da errori di progettazione e (molto più frequenti) produzione. Ogni scheda prodotta viene testata attraverso un processo chiamato bare-board testing che consiste nel verificare la continuità o meno di due punti della scheda, secondo quando specificato dal progetto originale. Esistono diversi metodi per farlo:
* bed-of-nails: tradotto letto di chiodi, è una matrice di sonde metalliche che entrando a contatto con la scheda possono verificare la conduttività con altre sonde;
* flying probe: questa volta le sonde (solitamente 2) si spostano e vanno a toccare le varie piste per verificarle;
* Raggi X: una scansione a raggi X viene analizzata da un software a riconoscimento di immagine, questo è usato soprattutto per i componenti più delicati.
La verifica della continuità delle piste non è ovviamente indice del perfetto funzionamento della scheda. Per testarle si adottano dei circuiti interni appositamente inseriti nel progetto, che permettono di eseguire uno scan. Questa operazione consiste nell'inserire nei pin di input della scheda dei valori di tensione e verificare che gli output siano quelli attesi. Il grande svantaggio di questa tecnica è l'elevato costo al crescere della complessità del sistema, sia in termini economici di componenti, che di spazio, che di tempo necessario al testing.

#### Testing software

Il testing del software è una delicata operazione per i sistemi embedded. Spesso, ad esempio nei sistemi aeronautici, un bug software può compromettere alcune funzioni essenziali per la sicurezza di cose e persone, producendo di conseguenza risultati inaccettabili. Ad esempio nel 2015 è stato trovato un banale bug di integer overflow nelle centraline di controllo dei motori del Boeing 787 che poteva causare in determinate condizioni, lo spegnimento contemporaneo di entrambi i motori.
Il testing può essere suddiviso in varie categorie, e solitamente tutte le categorie di testing devono essere eseguite:
* Unit testing: il testing è effettuato su un'unica unità di codice per verificare che si comporti come ci si aspetti. Un tipico esempio per chi è pratico con la programmazione ad oggetti, è il testing della singola classe.
* Integration testing: ora che le singole unità di codice sono funzionanti, attraverso l'integration testing si va a verificare unendo le varie parti se le loro interfacce comunicano nel modo corretto e se nell'insieme, le unità producono gli output coerenti.
* System testing: questo macro-testing contiene molti testing di vario tipo (testing di installazione, testing di stress, testing di compatibilità, ecc.) e consiste nel verificare che nel suo complesso il sistema funzioni. È eseguito attraverso una tecnica black-box ovvero senza conoscere il codice all'interno dei singoli moduli, ma unicamente osservando il funzionamento dall'esterno del sistema.

#### BIST

I built-in self test sono dei test effettuati dal dispositivo stesso che deve essere testato. Consiste nell'inserire nel progetto una parte hardware dedicata al testing pre-funzionamento e durante il funzionamento stesso. Un tipico esempio di tutti i giorni è il Power-on self-test dei personal computer. Il loro compito è rilevare gli errori prima che il sistema entri in funzione, o segnalarlo durante il suo funzionamento.
Come si può facilmente intuire, questo tipo di test sono essenziali per l'avionica, sia per rilevare un problema prima del decollo, sia per identificare e disconnettere eventuali moduli malfunzionanti durante il volo stesso.

## Sistemi real-time

Per molte applicazione embedded e quasi tutte quelle aeronautiche, la velocità di risposta del sistema è cruciale per garantire la correttezza nel comportamento del sistema stesso. Per meglio specificare ancora, non è tanto la velocità il parametro importante, ma la certezza dell'esecuzione entro un tempo prefissato. Un esempio estremamente banale è il fly-by-wire: il computer dei comandi di volo deve analizzare la posizione del joystick del pilota e muovere di conseguenza le superfici di controllo entro un tempo accettabile (cosa succederebbe se il computer fosse sovraccarico e rispondesse con ritardi di 30 secondi alle azioni del pilota?).
Si necessita quindi di sistemi operativi real-time e applicazioni che garantiscano determinate tempistiche. Non è sufficiente inserire un processore più performante per risolvere i problemi (questo è visibile nei Personal Computer, anche con un processore veloce il sistema può rallentare, anche senza apparente motivo).
Un sistema operativo real time deve garantire:
* Determinismo, ovvero la possibilità di svolgere operazioni entro limiti di tempo prefissati;
* Prontezza nella risposta, legata alla precedente, rappresenta la minimizzazione del tempo utilizzato dal sistema operativo per eseguire le operazioni interne (context switching, gestione interrupt, ecc.);
* Controllo utente, l'utente deve poter agire su molti più parametri di un normale PC, come paginazione, priorità dei processi, uso della memoria di massa, ecc.
* Affidabilità e operatività fail-soft, i sistemi devono far fronte a determinati fallimenti per poter continuare a funzionare, almeno in parte.
Ovviamente lo sviluppo di sistemi real-time è molto più costoso di sistemi normali, e non permette di scindere completamente applicazione-sistema operativo, che invece devono strettamente interagire per garantire le condizioni sopra mostrate. In base alle necessità, i sistemi operativi real-time possono essere acquistati (e in questo caso si parla di COTS (Commercial Off-The-Sheld), altrimenti si sviluppano internamente.
Come già detto, i sistemi real-time per l'aeronautica devono rispettare dei precisi limiti temporali. Questi sistemi sono detti hard real-time perché il fallimento nel rispetto dei limiti temporali comporta una criticità per la funzione svolta dal sistema.

### Scheduling

Lo scheduling è il meccanismo con il quale il sistema operativo gestisce l'assegnazione delle risorse (CPU, periferiche, ecc.) ad ogni processo. Il modulo del sistema operativo che si occupa di questo si chiama scheduler.
Possiamo dividere gli scheduler in due categorie:
* non pre-emptive (senza prelazione): lo scheduler decide quale processo avviare e gli assegna le risorse. Il processo cede volontariamente il controllo allo scheduler e le risorse assegnate, teoricamente al termine del quanto di tempo assegnato.
* pre-emptive (con prelazione): in questo caso lo scheduler ha la facoltà di interrompere un processo in qualsiasi momento, anche a metà esecuzione. Il vantaggio principale è che il software non deve gestire la scadenza del quanto di tempo e che lo scheduler può interrompere in qualsiasi momento l'esecuzione (ad esempio perché un altro processo con priorità più alta richiede l'esecuzione). Lo svantaggio è che in parte deve essere implementato in hardware e una maggior complessità nel kernel.
Vista la criticità di questi algoritmi, la letteratura è davvero ricca per la gestione dei processi in situazioni real-time.
Per concludere questa breve introduzione agli scheduler, identifichiamo quattro grandi classi di scheduler real-time:
* statici basati su tabelle: lo scheduler predispone l'esecuzione dei processi conoscendo le loro deadline, definendo a tempo di esecuzione il momento in cui lanciare un processo
* statici con pre-rilascio e priorità: non viene creata una tabella statica che indica i tempi in cui avviare i processi ma la priorità che questi processi dovranno avere
* dinamici basati su pianificazione: lo scheduler è creato solo a tempo di esecuzione e un processo viene avviato solo se è possibile soddisfare i suoi vincoli temporali e quelli degli altri processi.
* dinamici best-effort: il sistema non pianifica nulla ma gestisce i processi in esecuzione secondo i vincoli. Se i vincoli temporali non sono rispettati lo scheduler blocca alcuni processi in esecuzione, per permettere l'esecuzione degli altri.