Lo sviluppo di codice indipendente dal compilatore per i microcontrollori conferisce un elevato livello di flessibilità a qualsiasi progetto di sistema embedded.
I progettisti di sistemi embedded devono affrontare problematiche quali cicli di sviluppo sempre più brevi, sono sottoposti a carichi di lavoro sempre maggiori e si trovano a operare costantemente sotto pressione. Una delle modalità più trascurate per ridurre i rischi di progetto e risparmiare tempo e sforzi progettuali nel lo sviluppo di un sistema embedded è l’indipendenza dal compilatore. Lo sviluppo di codice indipendente dal compilatore per i microcontrollori conferisce un elevato livello di flessibilità a qualsiasi progetto.
Il tool migliore
L’indipendenza dal compilatore dà ai progettisti la possibilità di scegliere i compilatori in base alle reali necessità del progetto. Il confronto tra compilatori differenti evidenzia in modo chiaro le prestazioni delle diverse tool-chain (ovvero l’insieme dei programmi usati nello sviluppo di un prodotto) quando si trovano a operare nelle medesime condizioni. I compilatori hanno caratteristiche proprie che li differenziano gli uni dagli altri. Un compilatore può ottimizzare il codice assembler generato in modo che le sue dimensioni risultino molto ridotte, un altro può eseguire un’ottimizzazione in termini di velocità, un terzo può non effettuare alcuna ottimizzazione. Le ottimizzazioni in termini di dimensioni possono risultare importanti nel caso la dimensione del codice dell’applicazione sia un fattore decisivo del processo di selezione di un microcontrollore. Se la dimensione del codice non rappresenta un fattore di scelta, il costo della licenza può costituire un problema. Al giorno d’oggi, il costo dei compilatori per micro 8051 può variare da parecchie migliaia di dollari per licenza a zero. La generazione di un progetto indipendente dal compilatore garantisce al progettista un livello di flessibilità molto superiore, consentendo il passaggio da una too chain a un’altra con estrema facilità. Il ricorso a una tool chain specifica può rappresentare un ostacolo per uno sviluppatore nel caso il compilatore scelto non sia più disponibile. Per esempio, se uno sviluppatore ha realizzato un intero progetto che prevede il ricorso a un solo compilatore, l’impossibilità di accedere a quella determinata tool chain potrebbe risultare pregiudizievole per il progetto stesso. Sempre rifacendosi al medesimo esempio, nel caso lo sviluppatore abbia scritto il codice in una forma indipendente dal compilatore, il passaggio a una nuova tool chain potrebbe richiedere solamente un ridotto numero di modifiche al file di intestazione (header file). A livello di codice, sarebbe necessario apportare un numero ridotto di modifiche di piccola entità per evitare avvertimenti o errori del compilatore ma, complessivamente, il passaggio tra diversi compilatori risulterebbe alquanto semplificato.
Come realizzare l’indipendenza dal compilatore
La realizzazione di un progetto indipendente dal compilatore è molto semplice: il progettista deve solamente aggiungere un file di intestazione con le definizioni di macro per la sintassi C comune specifica del compilatore.Se si tiene contro delle differenze nella sintassi specifica del compilatore nel file di intestazione al posto di scrivere un intero progetto destinato a uno specifico compilatore, si ottiene una maggiore possibilità di adattamento nel caso il codice debba venire utilizzato con un’altra tool chain oppure il compilatore scelto non sia più disponibile. I vantaggi e la semplicità di adattamento di questo metodo di programmazione rendono più efficiente e flessibile il processo di sviluppo del codice per microcontrollori. I vantaggi sono immediati: il codice sorgente risulta più leggibile mentre il tempo richiesto per il debug viene drasticamente ridotto. Le variazioni relative alla modalità di definizione di interrupt, puntatori e variabili possono essere modificati in una locazione e diffuse attraverso il resto del progetto. Oltre a ciò è possibile integrare definizioni globali e strutture specifiche nel file di intestazione che rappresentano un valido ausilio per quel che riguarda l’ordinamento dei byte (endianness) del compilatore. Si tratta di un fattore importante in quanto il fatto di non tener conto della variazione dell’ordinamento dei byte durante la scrittura del codice può determinare errori difficili da rilevare.
Riutilizzo del codice
A prescindere dalla maggiore flessibilità e dalla semplicità nel passaggio da un compilatore all’altro, il codice indipendente dal compilatore risulta molto più leggibile rispetto a un codice scritto per uno specifico compilatore. Questa maggiore leggibilità si traduce in una riduzione del tempo di debug, che a sua volta si riflette favorevolmente sul costo totale del progetto. Nel caso di un’applicazione che prevede l’utilizzo da parte di diversi ingegneri del medesimo codice sorgente nel progetto finale, il codice sorgente deve operare con più di una tool chain. In questo caso lo sviluppatore dovrebbe indicare alcuni tool che sono stati collaudati con istruzioni relative alla modalità di integrazione di differenti tool chain. Tale flessibilità aumenta la possibilità di riutilizzo del codice tra diversi utenti.
Modalità di realizzazione
L’utilizzo di definizioni di macro nel codice sorgente al posto della sintassi specifica del compilatore rappresenta l’elemento fondamentale per la realizzazione di un progetto indipendente dal compilatore. Il solo file aggiuntivo necessario è il file di intestazione con le definizioni di macro per la particolare tool chain supportata. QuestO file contiene un elenco di istruzioni “if” condizionali dove l’argomento dell’istruzione “if” è il nome del compilatore che, nel caso venga scelto, farà in modo che le definizioni di macro per quel determinato compilatore vengano utilizzate nel corso del progetto. Quindi è necessario modificare una sola linea di codice nel file di definizione di macro al fine di variare parecchie linee nel codice sorgente. Ciò risulta particolarmente utile quando si cerca di integrare una nuova tool chain in un progetto. Per esempio, nel caso venga adoperata una definizione non corretta di un interrupt nel file di definizione di macro, lo sviluppatore deve solamente cambiare una particolare definizione di macro invece di modificare ogni definizione della routine di servizio dell’interrupt (ISR – Interrupt Service Routine) nel codice sorgente. Al fine di includere un nuovo compilatore in un progetto, lo sviluppatore deve aggiungere una nuova sezione al file di definizione di macro mediante un’istruzione “if” supplementare e una lista di definizione di macro. A questo punto lo sviluppatore deve disporre della sintassi specifica del compilatore per ogni definizione di macro, reperibile nella documentazione fornita congiuntamente al compilatore. Dopo aver effettuato questa piccola modifica al file di definizione di macro, la nuova tool chain risulterà compatibile con il progetto. L’idea di utilizzare un file di intestazione con la definizione di macro non differisce da quella che prevede l’uso di un file di intestazione specifico del microcontrollore per bit, indirizzi e nomi di registri di funzioni particolari (SFR - Special Function Register). Il file di intestazione con la definizione di macro è un file di intestazione incluso progettato per semplificare il processo di sviluppo del codice. Ogni compilatore è caratterizzato da una propria specifica sintassi per:
» Interrupts
» Prototipi di interrupt
» Suddivisione in banchi (Banking) di registri
» Definizioni di segmenti di memoria
» Localizzazioni di variabili nei segmenti di memoria
» Puntatori a segmenti di memoria
» NOPs (No-operations)
» Dichiarazione di registri SFR (Special Function Register)
» Dichiarazione dei bit dei registri SFR.
Ciascuna di queste può impedire a un progetto di diventare indipendente dal compilatore se viene usata la sintassi specifica del compilatore. Gli altri problemi potenziali che possono sorgere quando si passa tra differenti tool chain riguardano differenze di ordinamento dei byte, puntatori generici, inizializzazione e localizzazione di variabili. I compilatori di tipo big endian memorizzano il byte più significativo di un numero a più byte nella locazione di memoria inferiore, mentre i compilatori little endian memorizzano in questa locazione il byte meno significativo di un numero a più byte (figura 1).
Questa fondamentale differenza è da tenere in considerazione nel caso sia prevista nel codice sorgente una qualsiasi manipolazione di un numero a più byte. Anche il formato dei puntatori generici può variare. In un puntatore generico a tre byte alcuni compilatori potrebbero utilizzare il byte meno significativo per memorizzare il segmento di memoria target e i restanti due byte per l’indirizzo, mentre altri utilizzeranno il byte più significativo per immagazzinare il segmento di memoria target e i restanti due per l’inriabile localizzata in un indirizzo di memoria specifico può provocare problemi potenziali in quanto non tutti i compilatori consentono a una variabile localizzata di essere inizializzata allo stesso tempo.
Considerazioni conclusive
I benefici e la semplicità di generare codice indipendente dal compilatore per microcontrollori sono elementi che qualsiasi sviluppatore dovrebbe prendere in considerazione. La realizzazione di un progetto indipendente dal compilatore assicura elevati livelli di flessibilità. I rischi legati alla dipendenza dal compilatore sono notevolmente ridotti, mentre è possibile eseguire un esame e un confronto accurato tra i diversi compilatori. Anche in termini di efficienza si ottiene un notevole incremento: le variazioni di una sola linea di codice nel file di intestazione si propagano attraverso tutto il progetto, con riflessi favorevoli sul tempo di debug.
Questo è’ un bel tema. Ed è la stessa problematica del codice riutilizzabile per PC. Per anni si è cercato di rendere universale il codice per qualsiasi piattaforma ma ancora non si può parlare di portabilità al 100% nel vero senso della parola.
Pensate che in futuro si raggiungerà questo fatidico traguardo?
Portabilità al 100%? Non credo. Bisognerà combattere con molti problemi sia tecnici che non.
Già. Innanzitutto c’è il problema della CPU o GPU o MCU. Ogni azienda costruisce i propri processori con i propri set di istruzioni e questo fatto non può essere evitato.
Il problema è piuttosto complesso, ci sono poi delle situazioni limite dove spesso il compilatore fa la differenza. Un esempio sono i DSP attuali, con architetture così complesse dove l’ottimizzazione del codice eseguita dal compilatore supera quella che si potrebbe fare a mano. Comunque se si riesce a raggiungere un elevato grado di portabilità (ad es. 80-90%) è secondo me già un ottimo risultato.