È più che evidente la tendenza in atto che sta facendo migrare gli sviluppatori di sistemi embedded dal linguaggio assembly a quello C. Decidere di utilizzare il linguaggio C per le applicazioni basate su piattaforme di computing embedded, anche molto spinte, è una scelta che può comportare rischi di insuccesso. I sistemi embedded sono nella maggior parte dei casi basati su dispositivi programmabili come i microcontrollori, i digital signal processor, i processori grafici, ecc…
Il linguaggio C
Il linguaggio C nello sviluppo dei sistemi commerciali viene utilizzato sin dall'introduzione dei primi personal computer. Per tali sistemi sono stati sviluppati compilatori molto poco efficienti e affatto ottimizzanti, quindi capaci di generare codice molto poco efficiente per tali sistemi. Le applicazioni commerciali compensavano le inefficienze del codice con un'ampia capacità di memoria e soprattutto, grazie alla legge di Moore, con una crescente velocità del processore conseguente alla diminuzione della geometria di integrazione. Per decenni i programmatori di sistemi embedded hanno ignorato il linguaggio C, malgrado la sua diffusione, proprio per colpa dei compilatori. Di tanto in tanto qualche produttore di compilatori C ne rilasciava una versione per microcontrollori o per Dsp, ma la qualità del codice prodotto era tanto bassa da indurre gli sviluppatori a scartare l'ipotesi di utilizzo del linguaggio C nello sviluppo dei sistemi embedded.
Uno dei principali problemi che il compilatore C deve essere in grado di gestire per i sistemi embedded è l'aritmetica intera e la limitata precisione dell'Alu. L'aritmetica intera, tipica proprio dei microcontrollori a 8 e 16 bit, impone molta attenzione al modo in cui i numeri reali e la loro manipolazione aritmetica viene implementata su tali architetture. I compilatori C sono molto astratti rispetto a questo aspetto dell'architettura di computing, quindi non hanno un modello compilativo ottimizzante rispetto a questo aspetto. Alcuni compilatori C rendono a tale scopo estensioni che consentono di supportare specificamente i processori embedded relativamente all'aritmetica fixed-point e all'uso di variabili hardware come i registri. C'è comunque una forte motivazione al passaggio dal linguaggio assembly al linguaggio C per i sistemi embedded. La motivazione è la portabilità da piattaforma a piattaforma e la leggibilità. Inoltre, la rapidità di sviluppo dell'applicazione con il linguaggio C garantisce di centrare più facilmente i requisiti di time-to-market. Questi aspetti diventano ulteriormente significativi se il codice deve essere trasferito da uno sviluppatore all'altro e deve essere manutenuto a lungo nel tempo. Ovviamente, queste motivazioni possono essere soddisfatte solo se il codice eseguibile è efficiente e compatto. Ciò può essere garantito solo da un compilatore ottimizzante.
Scegliere il compilatore C giusto
Scegliere il compilatore C giusto per lo sviluppo di applicazioni embedded è sicuramente il passo più importante che uno sviluppatore deve compiere quando decide di passare dalla programmazione assembly a quella C. I compilatori C embedded sono ormai disponibili da parte di numerosi produttori di compilatori di linguaggio C o C++. Compilatori di ottima qualità sono disponibili anche da parte di organizzazioni non commerciali sotto forma di shareware. Il caso più noto di compilatore shareware è il Gcc Gnu, spesso utilizzato dai produttori di microcontrollori come compilatore reference per le loro famiglie di dispositivi (viene fornito in forma gratuita tra i tool di sviluppo). I criteri da tenere ben presente per la scelta del compilatore C sono fondamentalmente i seguenti:
• compatibilità Ansi;
• portabilità;
• capacità di generare codice compatto;
La compatibilità Ansi è strettamente legata alla portabilità del codice. Questa è una diretta conseguenza della natura ad alto livello del codice C. Teoricamente, qualsiasi compilatore C potrebbe compilare qualsiasi codice C. Nella realtà ciò non è vero in quanto i compilatori non Ansi-C compatibili compilano linee di codice che altri compilatori non compilano, rendendo in tal modo la portabilità del codice critica. La capacità ottimizzante del compilatore C è la caratteristica del compilatore che consente di ottenere codice compatto ed efficiente nell'esecuzione. Ovviamente si tratta di requisiti mutuamente contrastanti, nel senso che per ottimizzare uno dei due bisogna rilassare l'altro. Il compilatore deve avere almeno la capacità di ottimizzare (a scelta) o l'uno o l'altro. Un buon compilatore ottimizzante è capace di offrire anche un'azione di ottimizzazione ponderata. La portabilità del codice è peculiare sia per prodotti finali che svolgono funzioni applicative simili, sia per prodotti finali soggetti a una rapida e frequente aggiornabilità.
L'ambiente di sviluppo, di cui il compilatore C fa parte, è un altro aspetto di scelta importante, considerando la complessità delle applicazioni embedded e quindi la necessità di strumenti di produttività durante la fase di progettazione, ma anche durante quella di verifica, validazione e documentazione del codice. Oltre ai compilatori C open source, sono disponibili compilatori proprietari, che vengono offerti direttamente dal produttore del microcontrollore, o più in generale di dispositivi programmabili. Nella versione base è normalmente incluso gratuitamente nel EVM come versione base. Le versioni più ricche e complete sono invece a pagamento. Un ambiente di sviluppo commerciale che include il linguaggio C in una suite di sviluppo per sistemi embedded è CodeWarrior di Metrowerks. Questo ambiente di sviluppo, soprattutto da quando è stato acquisito da Motorola (poi Freescale), è particolarmente orientato a supportare dispositivi embedded come l'ampia gamma di microcontrollori, microcontrollori Dsp e Dsp puri di Freescale. Il compilatore C e C++ in esso incluso è ottimizzante.