Teniamo sott’occhio la Complessità (Embedded)


Nel Febbraio dell’anno scorso la gamma Raspberry Pi è stata ampliata  con il nuovo Raspberry Pi Zero W (1), un personal computer da 10$ con connettività wireless. Che meravigliosa occasione questa per tutti gli hobbysti, maker, riparatori, hacker e, sì, anche alcuni di noi che stanno cercando di fare realmente il loro lavoro, cioè progettare prodotti concreti (elettronici)! Nel guardare il video di Eben Upton che annunciava il nuovo prodotto,  non ho potuto fare a meno di tornare con la mente al passato. A quell’epoca, era la metà degli anni ’80, non avrei mai potuto permettermi un computer BBC, né il lussuoso Amiga. Spesi invece tutti i miei averi per acquistare un Sinclair ZX Spectrum. Quindi gli sforzi di Eben per realizzare prodotti informatici alla portata di tutti sono veramente in sintonia con me.

Sono stupito dal nuovo minimo da record raggiunto da ciò che è, dopo tutto, un potente personal computer infilato in un piccolo circuito stampato (PCB), di 6 x 3 cm per l’esattezza. Come ulteriore riflessione, mi sono spesso chiesto se non fossero stati proprio la  frugalità dello Spectrum e i suoi tanti limiti ad avermi insegnato a scavare più a fondo, all’interno della macchina, facendomi innamorare di quel mondo strano, quella frontiera tra software e hardware, che oggi noi chiamiamo embedded.

Piccoli sistemi su Chip

Lo schema del Raspberry Pi Zero è basato su un System on Chip (BCM2835) che include un core ARM11 da 1GHz ma ha anche una Graphics Processing Unit (GPU), una interfaccia video, diverse interfacce seriali (USB, UART, SPI, I2C) ed una interfaccia per memoria esterna per gestire la grossa fetta di DDR2 RAM esterna (512MB) e l’unità di memoria di massa (SD card) necessaria per eseguire Linux OS. Queste capacità sono impressionanti per un dispositivo single chip, specialmente se paragonate alle precedenti generazioni di personal computer viste durante la mia gioventù. Si potrebbe obiettare che questo non è sproporzionato rispetto all’ultima generazione di più umili microcontroller comunemente in uso oggigiorno in tutti i tipi di applicazioni di controllo embedded. Mentre le velocità di clock, e quindi la potenza di elaborazione, sono molto inferiori (da 10 a ~100 MHz), tutti i piccoli micros sono oggi veri e propri piccoli sistemi on chip, meravigliosi a pieno titolo. Come ci si potrebbe aspettare da un microcontroller, tutta la RAM e Flash sono presenti on-chip. Le interfacce seriali (USB, UART, SPI e I2C) sono presenti, ma risultano anche  integrate tutte le regolazioni dell’alimentazione e i circuiti di supervisione della tensione. Non è inusuale trovare anche cinque o più oscillatori (di precisione) on-chip, a beneficio di una maggiore flessibilità e controllo del consumo di energia. Sono anche presenti diverse periferiche analogiche (ADC, DAC, Op Amp, Analog Comparator, ecc.) con grandi multiplexer di input/output, al posto delle fantasiose funzionalità video del Raspberry Pi, che riflettono le ovvie differenze nelle scelte progettuali che favoriscono fino in fondo l’embedded   rispetto al computing.

Non sorprende, infatti, che quando gli utenti Raspberry Pi debbano interfacciarsi con il mondo reale, al di fuori delle più delicate applicazioni di I/O come il lampeggìo del proverbiale LED, microcontroller più piccoli (spesso microcontroller 8 bit, in effetti) accorrano in salvataggio tramite “hats”, piccole daughter board che forniscono le interfacce I/O necessarie e la conversione di tensione richiesta.

Non è mia intenzione dilungarmi oltre con questo iniquo parallelo tra due mondi cosi distanti, ma devo far rilevare che entrambe le piattaforme condividono problematiche e questioni comuni: “come tenere sotto controllo la complessità?”, “come attrarre nuovi utenti”?  Ė superfluo sottolineare che le loro soluzioni sono, ancora una volta, simili ma con direzioni completamente divergenti.

Entrambe le piattaforme partono da un’offerta di strumenti software  gratuiti, tra cui IDE (Integrated Development Environments), compiler, linker, simulatori, debugger (opzionalmente disponibili in edizioni professionali ad un costo ridotto), middleware e (RT-)OS più o meno Open, ed una piccola selezione di opzioni hardware (scheda).

Le differenze tra i due ambienti, embedded e general computing, sono più sottili di quanto si possa immaginare. Entrambi finiscono per basarsi su tool chain simili, se non identiche, che sono per lo più basate su GNU. A livello di middleware le opzioni open source sono di nuovo molto simili una volta che si sia provveduto ad astrarre correttamente i livelli di driver inferiori (down to the metal). A livello OS la differenza è maggiore dato che molti microcontroller eseguiranno felicemente un RTOS, ma non saranno in grado di sostenere il peso di un kernel Linux completo. Ciò è il risultato di una reale differenza di vocazione. Il real time fa parte della “job description” del OS.

Inflazione

È quando si guarda alla documentazione che l’inflazione della complessità diventa evidente da entrambe le parti. Uno dei miei esempi preferiti è il caso di un piccolo e umile microcontroller, basato sulla popolare architettura 8-bit PIC. Il PIC16F1619 viene spesso utilizzato per controllare piccoli dispositivi e, a tal fine, contiene una piccola quantità di Flash (16 KB), in un piccolo package a 20 pin, una dozzina di interfacce periferiche digitali e quasi altrettanti moduli di supporto analogici. Tuttavia, la sua scheda tecnica si estende per 650 pagine, e cioè prima di aggiungere i dati di caratterizzazione, grafici e diagrammi (2).

Alcune delle periferiche offerte da questo piccolo SoC, come ad esempio il Signal Measurement Timer, richiedono che ben 50 di queste pagine siano documentate correttamente. Questo è quasi il doppio del numero di pagine richieste per descrivere l’attuale core PIC e il suo intero set di istruzioni.

Sul lato Raspberry Pi i problemi sono simili, se non solo ingranditi proporzionalmente (10x) dato che ci sono diversi datasheet da considerare, ciascuno con la cura di documentare solo una parte dei componenti hardware del sistema su chip (periferiche SoC, GPU, core ), con il core che da solo occupa più di 750 pagine.

Embedded Software Architecture

Come è ovvio, nessuno può aspettarsi di leggere o semplicemente di stare al passo con una quantità così vasta di informazioni. In particolare, gli sviluppatori embedded sono sempre sotto estrema pressione perché consegnino le loro applicazioni in tempi sempre più brevi, nell’eterna ricerca del time-to-market più rapido. Una soluzione comune consiste nel partizionare un’applicazione utilizzando una architettura a livelli e utilizzare librerie di periferiche standardizzate per astrarre  i dettagli dell’hardware. I layer possono essere rappresentati come a formare ordinatamente uno stack in cui “l’applicazione” sia posizionata sopra un HAL, Hardware Abstraction Layer. In effetti questa immagine può essere ulteriormente perfezionata per identificare adeguatamente l’HAL e, in aggiunta, un layer middleware  che si occuperebbe di implementare servizi/funzioni comuni come reti, file system e interfacce grafiche se presenti/richieste.

Figura 1: Stack software di una applicazione embedded

Nota: lo stack viene spesso ulteriormente rifinito separando un livello driver e un livello di supporto scheda dal HAL, ma non ci servirà quel livello di dettaglio nelle considerazioni a seguire.

Questa architettura software deriva direttamente dal mondo del “computing” e funziona bene per modellare i casi più generici. Sfortunatamente, per come si utilizza in applicazioni embedded, soffre di due difetti fondamentali:

  • L’architettura a livelli semplifica il problema dell’inflazione della documentazione purché l’attenzione si concentri sulle funzioni standard fornite dal livello middleware superiore. Nell’estremità inferiore dello spettro di applicazioni, in cui uno strato di middleware è molto sottile se presente, il risultato è in gran parte offuscamento. Uno sviluppatore deve fare affidamento sulla documentazione HAL, sotto forma di una grande Application Programming Interface (API), un corposo materiale altrettanto grande che può estendersi su diverse migliaia di pagine, ma nessuno impara mai veramente le specifiche di un dispositivo. Quando sorgono problemi, lui/lei è lasciato in un limbo o costretto ad immergersi in profondità in un territorio straniero ed in un grande volume di codice.
  • Il livello HAL fornisce un enorme aiuto per supportare i servizi middleware standard ma, a causa della sua natura molto rigida, finisce per cancellare qualsiasi caratteristica di differenziazione unica di un dispositivo specifico. Queste caratteristiche potrebbero altrimenti fornire un vantaggio tecnico ad una particolare applicazione e potrebbero essere state la ragione per selezionare un modello di dispositivo specifico.
  • All’estremità superiore dello spettro di applicazioni, in cui il livello del middleware è molto spesso, come nel caso di Raspberry Pi, il kernel del sistema operativo Linux da solo aggiunge milioni di righe di codice al problema (3). Mentre si può sostenere che questo è codice open source, c’è poco conforto in questo per lo sviluppatore medio che spera di non dover mai andare a scavare laggiù.

Lasciate che le macchine facciano ciò che sanno fare meglio!

Alla fine gli sviluppatori di Raspberry Pi potranno contare su grandi guadagni forniti dalle prestazioni “computazionali” e dalle vaste risorse offerte da quelle piccole schede. La praticità del sistema operativo Linux standard compenserà ampiamente la complessità e la vastità dell’API.

Ciò che più mi preoccupa sono i nuovi piccoli sviluppatori di SoC : i moderni utenti di microcontroller. Per loro c’è meno beneficio nel lavorare con un HAL standardizzato, in quanto vi sono delle penalizzazioni prestazionali e le caratteristiche uniche sono appiattite dall’architettura dello stack software.

Un modo intelligente per uscire da questo enigma è rappresentato da una nuova generazione di strumenti software per sviluppi rapidi. Questa è una nuova classe di generatori di codice o configuratori recentemente apparsi nel mercato dell’ Embedded Control.

Nonostante lo scetticismo iniziale, significativo e spesso giustificato, questi strumenti si dimostrano non solo efficaci ma anche necessari per qualsiasi sviluppatore embedded serio.

Tra le caratteristiche distintive troviamo:

  • Completa integrazione negli IDE più popolari, che li rende consapevoli di un contesto di progetto: selezione del modello (part number) e consapevolezza della libreria middleware.
  • Supporto per periferiche uniche e complesse. Ad esempio il Signal Measurement Timer (SMT) menzionato nell’esempio precedente, può essere presentato visivamente all’utente in una singola pagina/finestra di dialogo contenente solo una manciata di liste a scorrimento, caselle di controllo e alcune opzioni intuitive. Vedere la Figura 2 per uno screenshot da MPLAB Code Configurator (MCC) (4), lo strumento di sviluppo rapido di punta per microcontroller PIC di Microchip Technology, Inc.

Figura 2 – MPLAB Code Configurator: opzioni del Signal Measurement Timer

  • Utilizzo di un motore di templating, avendo cura di tradurre le scelte di configurazione fatte in un piccolo insieme di funzioni completamente personalizzate. Ciò significa che viene generata solo un’API minima con poche funzioni da apprendere, con convenzioni di naming coerenti e intuitive. La personalizzazione della funzione garantisce che la maggior parte dell’astrazione dell’hardware venga eseguita staticamente in fase di compilazione (in realtà prima). Ciò riduce l’elenco dei parametri richiesti/approvati per ciascuna funzione e quindi aumenta le prestazioni e la densità del codice. Vedere l’elenco 1 per un esempio della frugalità tipica del MPLAB Code Configurator.
  • L’output è composto da file sorgenti molto brevi (in linguaggio C) che possono essere completamente ispezionati dall’utente offrendo una opportunità di apprendimento ma possono essere anche ulteriormente ottimizzati manualmente  da esperti. I moderni generatori di codice mescolano il loro codice con il codice utente con facilità preservandone l’integrità e consentendo di sfruttare appieno le preziose funzionalità hardware avanzate.

void SMT1_Initialize(void) {
    // CPOL rising edge; EN enabled; SPOL high/rising edge enabled; SMT1PS 1:1 Prescaler; …
    SMT1CON0 = 0x80;
    // SMT1MODE Counter; SMT1GO disabled; SMT1REPEAT Single Acquisition mode;
    SMT1CON1 = 0x08;
    // SMT1CPRUP SMT1PR1 update complete; SMT1TS not incrementing; RST SMT1TMR1 update complete …
    SMT1STAT = 0x00;
    SMT1CLK = 0x00;            // SMT1CSEL FOSC;
    SMT1WIN = 0x00;           // SMT1WSEL SMTWINx;
    SMT1SIG = 0x00;            // SMT1SSEL SMTxSIG;
    SMT1PRU = 0x00;           // SMT1PR16 0x0; 
    SMT1PRH = 0x00;            // SMT1PR8 0x0; 
    SMT1PRL = 0x00;            // SMT1PR0 0x0;
}

void SMT1_DataAcquisitionEnable(void) {   
    SMT1CON1bits.SMT1GO = 1;    // Start the SMT module by writing to SMTxGO bit

}

void SMT1_SetPeriod ­­(uint32_t periodVal) {
    // Write to the SMT1 Period registers
    SMT1PRU = (periodVal >> 16);
    SMT1PRH = (periodVal >> 8);
    SMT1PRL = periodVal;

}

Listato 1 – Sezione del file sorgente (smt1.c) generato da MCC per configurare la periferica SMT

In pratica i configuratori/generatori di codice fanno ciò che le “macchine” fanno meglio.  La fase ripetitiva e soggetta ad errori di configurazione delle periferiche hardware, la creazione di un HAL, che spesso si risolve in molte ore noiose perlustrando i datasheet, è stata notevolmente ridotta ad alcuni minuti molto più divertenti e intellettualmente stimolanti sia di scoperta che di creazione.

Infatti, gli utenti possono apprendere riguardo specifiche funzionalità di periferiche hardware dalla stessa interfaccia utente, sostanzialmente eliminando del tutto (o almeno riducendo notevolmente) la necessità del datasheet.

Il livello di astrazione hardware diventa una parte flessibile del progetto e viene infatti rigenerato spesso e rapidamente, se necessario, per ottimizzare le prestazioni dell’applicazione.

In Dieci Linee di Codice (binario)

Una volta che la configurazione (delle periferiche) è stata curata, la mente è libera di concentrarsi immediatamente sull’applicazione, la parte più intelligente del progetto a livello applicativo, vale a dire ciò che si trova all’interno del “main loop”, in contrapposizione a ciò che viene prima di questo.

Alla fine, grazie ai generatori di codice, anche nel mondo embedded il classico esempio di “Hello World”, che è invariabilmente tradotto in un LED lampeggiante, diventa un rinfrescante due linee di esercizio del codice! 

LED_Toggle();
__delay_ms(500);

Listato 2 – Le uniche due righe di codice che dovrai digitare per creare il tuo primo “Hello World” embedded

Sarai in grado di trovare (20) esempi più pratici dello stesso uso efficace degli strumenti di sviluppo rapido nel libro che ho recentemente pubblicato: “In 10 Lines of Code”5.

Combattiamo la Complessità

Man mano che i piccoli microcontroller crescono diventando SoC, e che i personal computer si riducono in Raspberry Pis, non è solo il tempo sprecato o il carico cognitivo, ma anche la vulnerabilità ad essere introdotti quando lavoriamo sui sistemi e che non possiamo comprendere/padroneggiare appieno.

La complessità non è una conseguenza inevitabile del progresso tecnologico. I moderni configuratori/generatori di codice possono aiutarci incrementando il nostro processo di sviluppo del software, automatizzando ed alla fine ripristinando il nostro comando nel numero in rapida crescita di funzionalità/opzioni disponibili. 

Riferimenti

  1. Annunci riguardo RaspberryPi ZeroW. https://www.raspberrypi.org/blog/raspberry-pi-zero-w-joins-family/
  2. PIC16F1619 datasheet. http://microchip.com/pic16f1619
  3. Linee di codice nel kernel di Linux. https://arstechnica.com/business/2012/04/linux-kernel-in-2011-15-million-total-lines-of-code-and-microsoft-is-a-top-contributor/
  4. MPLAB Code Configurator. http://microchip.com/mcc
  5. In Solo 10 Linee di Codice. http://blog.flyingpic24.com/10lines

BIO

Lucio Di Jasio è EMEA Business Development Manager di Microchip Technology Inc. Ha ricoperto vari ruoli tecnici e di marketing all’interno delle divisioni aziendali 8, 16 e 32 bit negli ultimi 18 anni. Quale autore tecnico prolifico e stimato, Lucio ha pubblicato numerosi articoli e svariati libri sulla programmazione per applicazioni di Embedded Control. Inseguendo la sua passione per il volo, ha ottenuto anche le certificazioni FAA e EASA per la licenza di pilota privato. Puoi leggere di più sugli ultimi libri e progetti di Lucio sul suo blog all’indirizzo: http://blog.flyingpic24.com 

 

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *