Un bootloader per il PIC24 di Microchip

Il bootloader è un programma che, inserito nel microcontrollore, permette di caricare nella memoria del componente, attraverso la porta seriale Rs232 o USB, in luogo di un apposito programmatore.

Questa prerogativa permette di apportare significativi miglioramenti al processo di sviluppo dell’applicazione. Di solito un bootloader sfrutta la comunicazione seriale tra un PC e il dispositivo stesso per consentire il caricamento del software applicativo. Di sicuro una delle caratteristiche più interessanti di un microcontrollore Microchip della serie PIC, in particolare del Pic24, è la possibilità di controllare la memoria flash e la relativa facilità di implementare un proprio bootloader partendo dalla versione base in grado di rispondere a specifici requisiti. Questa possibilità rappresenta un enorme vantaggio perché permette, oltre ad aggiornare il software utente residente sul dispositivo, di controllare l’intero ciclo di vita e di sviluppo. Microchip, a questo proposito, suggerisce il suo Pic24, un processore su 16 bit, che, attraverso una semplice linea seriale, permette di realizzare un servizio di questo tipo. Il protocollo di comunicazione messo a punto dalla stessa Microchip è facilmente utilizzabile per qualsiasi applicazione rendendo estremamente flessibile la sua implementazione. Questo protocollo si basa sulla versione per PIC16 e PIC18 al fine di garantire la compatibilità tra le differenti architetture, oltre a presentare un minimo impatto applicativo. La figura 1 pone in evidenza i diversi blocchi funzionali essenziali per il design di un bootloader.

Figura 1: blocchi funzionali.

Figura 1: blocchi funzionali.

Dalla figura vediamo che i dati, una volta acquisiti attraverso la linea seriale, sono inviati al modulo di ricezione e trasmissione. Questa sezione è il vero motore del processo, definito a volte anche come bootloader engine, perché deve sfruttare i diversi componenti hardware presenti per condurre le relative acquisizioni o trasmissioni. In seguito, il data frame ricevuto, secondo uno schema definito, deve essere inviato al modulo per il relativo parsing per poi essere inseriti nella memoria del target. L’operazione di parsing è necessaria perché serve ad interpretare i comandi ricevuti al fine di adottare le differenti decisioni: ad ogni data frame ricevuto deve corrispondere un’azione predeterminata. Infatti, ad ogni comando correttamente interpretato e gestito deve essere inoltrata una conferma al modulo che ha richiesto l’operazione. Microchip, per consentire un suo facile uso in ogni situazione operativa, ha deciso di utilizzare quale mezzo di comunicazione una tradizionale linea seriale Rs-232. Grazie a questa decisione qualsiasi PC può essere utilizzato come host, basta assicurare una linea di comunicazione su 8 bit di dati, No Parità, 1 stop bit e Automatic Baud Rate Detection. Microchip, inoltre, fornisce tutto il necessario supporto per utilizzare il proprio bootloader in qualsiasi contesto operativo, da un ambiente host a tutto il software necessario. Il bootloader di Microchip basa il suo funzionamento su dei buffer in ricezione e trasmissione. In effetti, è praticamente impossibile sfruttare solo le prerogative offerte dai registri interni per implementare una feature di questo tipo, ma diventa essenziale appoggiare le acquisizioni su apposite aree di memoria con una profondità di almeno 261 byte, la massima area di memoria supportata dal protocollo messo a punto da Microchip. La figura 2, al contrario, mostra una possibile mappa di memoria che mantiene in memoria l’ultimo pacchetto ricevuto.

Figura 2: area di memoria.

Figura 2: area di memoria.

Il Bootloader di Microchip è poi estremamente flessibile perché utilizza una discreta quantità di memoria, da 2 a 3,5 Kbyte, in relazione alle sue indicazioni di configurazione e in fatto di scelte di compilazione. Il design condotto da Microchip sul suo bootloader ha permesso la possibilità di utilizzarlo in qualsiasi piattaforma differente in base alle singole esigenze. Così, nella configurazione di default, il bootloader è stato progettato in modo da poter essere posizionato in qualsiasi punto della memoria. Per inciso ricordiamo che il valore di default è un indirizzo con valore di 000400h. Occorre anche ricordare che il bootloader non deve essere collocato nella stessa pagina dei vettori di interrupt o della configuration word al fine di sfruttare la rilocabilità. Non solo, è anche opportuno inserire il bootloader in una zona protetta per evitare di essere danneggiato da eventuali azioni di sovrascritture. Interprete di comandi. L’interprete di comandi decodifica ed esegue i vari comandi previsti dal protocollo. A questo proposito la figura 3 mostra i diversi comandi supportati dal bootloader, come vediamo dalla figura è possibile svolgere operazioni di scrittura, lettura e cancellazione da tutti i tipi di memoria: da memorie di tipo volatile a quelle flash.

Figura 3: comandi disponibili.

Figura 3: comandi disponibili.

Non solo, è anche possibile accedere ai registri di configurazione o utilizzare comandi speciali, quali il reset della board e la ripetizione dell’ultimo comando. Il bootloader si avvale di un semplice protocollo che risulta robusto da usare e facile da implementare. Questo protocollo è stato originariamente definito nel documento tecnico di Microchip siglato come AN851. Rispetto al protocollo originale l’attuale versione è stato leggermente modificato per migliorare la compatibilità con l’architettura a 16 bit PIC24F aumentando, nel contempo, la dimensione massima del pacchetto. Tutti i dati sono spediti e ricevuti da un dispositivo esterno in base ad un formato come definito in figura 4: ogni campo del messaggio è costituito da un byte.

Figura 4: formato dei pacchetti.

Figura 4: formato dei pacchetti.

Ogni pacchetto è delimitato da due caratteri di controllo: uno Start of Text (STX) che delimita l’inizio del frame e End Of Text (ETX) che chiude un frame. L’ultimo byte prima del carattere ETX è sempre il checksum che cura l’integrità del frame. Ogni data field è limitato a 261 byte, questo vuol dire che se in acquisizione si riceve un data frame avente una lunghezza superiore a 261 byte ogni carattere che eccede al limite è semplicemente ignorato. Insieme a questi due caratteri di controllo esiste anche <DLE>, ossia Data Link Escape. A questo proposito la tabella 1 pone in evidenza le possibili alternative applicabili a questo controllo.

Tabella 1: caratteri di controllo.

Tabella 1: caratteri di controllo.

Il listato 2 mostra una possibile implementazione del comando di “write memory” con il relativo data frame. Il carattere <DLE> è utilizzato in modo particolare. Infatti, l’uso che il bootloader fa del carattere di controllo è quello di identificare un valore, presente nel campo dati, da utilizzare come carattere di controllo.

/*
** Linker script outline for PIC24F bootloader (for PIC24FJ64GA004 device)
*/
OUTPUT_ARCH(“24FJ64GA004”)
/*EXTERN(__resetPRI)*/ //no data init needed, only load no-init starupt
EXTERN(__resetALT)
/*
** Memory Regions
*/
MEMORY
{
data (a!xr) : ORIGIN = 0x800, LENGTH = 0x2000
reset : ORIGIN = 0x0, LENGTH = 0x4
ivt : ORIGIN = 0x4, LENGTH = 0xFC
aivt : ORIGIN = 0x104, LENGTH = 0xFC
program (xr) : ORIGIN = 0x400, LENGTH = 0x800 /*start & length of BL*/
config2 : ORIGIN = 0xABFC, LENGTH = 0x2
config1 : ORIGIN = 0xABFE, LENGTH = 0x2
}
__CONFIG2 = 0xABFC;
__CONFIG1 = 0xABFE;
__IVT_BASE = 0x4;
__AIVT_BASE = 0x104;
__DATA_BASE = 0x800;
__CODE_BASE = 0x400; /* Starting location of bootloader */
...
/*
** Interrupt Vector Table
*/
.ivt __IVT_BASE :
{
.
/* Int Vector Remap Method 1 . point to application.s ISR location */
LONG( ABSOLUTE(0xF00)); /*Location of Interrupt ISR*/
.
/* Int Vector Remap Method 2 .point to a location in a jump table */
LONG(ABSOLUTE(0x1004)); /*Location of jump table goto instruction*/
.
} >ivt
Listato 1 – Esempio di script per Bootloader

 

; IN : <STX><STX>[<0x02><DLENBLOCK><ADDRL><ADDRH>
: <ADDRU><DATA>...]<CHKSUM><ETX>
; OUT : <STX><STX>[<0x02>]<CHKSUM><ETX>
WriteProgMem
bsf STATUS,RP0
movlw b’10000100’ ; Setup writes
movwf EECON1
bcf STATUS,RP0
movlw b’11111100’ ; Force a boundry
andwf EEADR,F
movlw 0x04
movwf TEMP
Lp1
movf INDF,W
movwf EEDATA
incf FSR,F
movf INDF,W
movwf EEDATH
incf FSR,F
call StartWrite
incf EEADR,F
btfsc STATUS,Z
incf EEADRH,F
decfsz TEMP,F
goto Lp1
decfsz COUNTER,F
goto WriteProgMem ; Not finished then repeat
goto SendAcknowledge ; Send acknowledge
Listato 2 – Comando Write Program Memory

La tabella 2 pone in risalto alcuni parametri che possono essere definiti dal programmatore per ottenere una differente versione del bootloader.

Tabella 2: BOOTLOADER: alcune opzioni di configurazione

Tabella 2: BOOTLOADER: alcune opzioni di configurazione

LATENZA

Tutti i flussi di controllo sono inseriti direttamente nel protocollo. Così, per ogni comando ricevuto (eccetto il reset), esiste sempre una risposta. Può succedere che l’host non riceva nessun riscontro perché, in questo particolare caso, nel sistema target potrebbe essersi verificata un’anomalia di funzionamento: i dati, ad esempio, potrebbero essere stati corrotti e in questo caso il valore del checksum non è più congruo o il data frame, o anche il data packet, potrebbe non essere ricevuto. Non solo, il campo dei dati potrebbe anche eccedere il limite dei 261 byte. A questo punto una domanda è d’obbligo:quanto tempo dobbiamo aspettare per decidere l’azione da intraprendere? Cioè, qual è il valore della response latency? Una response latency è tipicamente relazionabile con la quantità di dati da processare e dalla frequenza di lavoro del clock. Per i comandi di lettura la latenza può essere tipicamente fissata e messa in relazione con la frequenza di clock e dalla consistenza dei pacchetti. In questo caso, per pacchetti piccoli e frequenza alta, il response time è praticamente immediato nell’ordine di alcuni microsecondi. Viceversa, per pacchetti più complessi la latenza può essere di centinaia di microsecondi.

BOOT

Il Bootloader può svolgere due distinti modalità di funzionamento. Al powerup, e una volta che il bootloader è stato caricato in memoria, è possibile scegliere due modalità operative: Boot e User Mode. Attraverso i parametri di configurazione è possibile definire alcuni comportamenti. Ad esempio, la locazione identificata come DELAY_TIME_ADDR, definito nel file config.h, contiene un valore che permette tracciare un flusso operativo. Così, con un valore pari a 0xff si intende che il bootloader divrà rimanere, al termine del boot, in Boot Mode senza la possibilità di passare in User Mode. Al contrario, con un valore differente si imposta un ritardo, espresso in secondi, prima di passare in User Mode. Viceversa, con un valore pari a zero si vuole che il passaggio in User Mode deve essere immediato. L’unica possibilità di abbandonare la modalità di Boot Mode è quello di provocare una sequenza di reset, software o hardware. Non solo, è anche possibile, in alternativa, inviare il comando di reset dall’ambiente host.

Scrivi un commento