La Program Memory e la tecnologia Flash nei Microcontrollori PIC Microchip

Microcontrollori PIC

Le diverse tecnologie di produzione della program memory dei microcontrollori Flash di Microchip: le differenze tra le famiglie di microcontrollori PIC16 e PIC18, i metodi di scrittura e la gestione di un bootloader.

La continua ricerca di migliori performance ed abbassamento dei costi dei dispositivi, ha portato allo sviluppo di diverse tecnologie di costruzione della Program memory. Ad oggi si possono pensare a quattro diverse tecnologie costruttive per realizzare la cella di memoria, ed in particolare si parla di:

  • ROM o Read Only Memory. In questi microcontrollori, il codice di fatto viene univocamente definito in fase costruttiva, definendo una maschera della program memory a livello di silicio (si parla infatti di Micro “mascherati”). Soluzioni del genere sono ad oggi ancora utilizzate laddove il codice sia univocamente definito e le quantità sono molto alte, garantendo un basso costo del singolo microcontrollore. Anche Microchip propone tali soluzioni che vengono identificate con una lettera “R” nel codice del dispositivo (ad esempio PIC16CR72).
  • EPROM o Erasable Programmable Read Only Memory. In questi micro il die viene incluso in un package ceramico ed in prossimità del die viene aperta una finestra in quarzo, in modo da poter consentire la cancellazione della program memory a seguito della esposizione a raggi Ultravioletti e la successiva riprogrammazione.  Tale soluzione ad oggi è solamente utilizzata per la parte di sviluppo e prototipazione di soluzione ROM: infatti il package ceramico risulta essere costoso ed inoltre, in funzione della lunghezza d’onda   e della intensità di luce, la fase di cancellazione può durare fino ad una decina di minuti,  rallentando notevolmente lo sviluppo del firmware. Microchip identifica tali dispositivi con un suffisso “JW” alla fine del part number (es: PIC16C72/JW)
  • OTP o One-Time-Programmable. Di fatto  questi microcontrollori  hanno la stessa program memory degli EPROM, ma il package plastico privo di finestra non permette la cancellazione del codice e la successiva riprogrammazione. Il dispositivo allora risulta essere più economico ed è adatto anche a produzione di serie più limitate rispetto alla soluzione ROM (es: PIC16C72-I/SP).
  • FLASH. Tale tipologia di memoria può essere cancellata elettricamente in pochi secondi da un programmatore e il dispositivo può essere riprogrammato con un nuovo codice. I micro Flash vengono identificati con una “F” all’interno del part number (es: PIC16F72-I/SP).

Per la loro riprogrammabilità,  questi microcontrollori di fatto stanno soppiantando la produzione di dispositivo OTP, EPROM e E2PROM.
La possibilità di riscrivere il dispositivo mediante degli opportuni segnali elettrici e l’introduzione di particolari strutture all’interno che potessero gestire un protocollo seriale di comunicazione, ha reso possibile la programmazione del chip direttamente montato sulla scheda, snellendo le fasi di montaggio delle schede (che possono quindi  essere completamente sviluppate in SMD). Esistono diversi “protocolli” che permettono la programmazione del dispositivo montato sulle scheda, tra i quali i più noti sono il JTAG e ICSP™ (in-circuit serial programming),  quest’ultimo adottato ormai dalla quasi totalità dei dispositivi Microchip.  L’introduzione poi di  altre strutture in grado di gestire un “monitor di debug”  (cioè una particolare porzione di programma per monitorare lo stato dei registri del microcontrollore e per gestire eventuali Break-point) ha reso possibile l’introduzione di sistemi di debug low cost, noti con il nome di ICD (in-circuit debug). Questi ICD di fatto riprogrammano il dispositivo ad ogni compilazione del codice (da qui l’assoluta necessità di avere un dispositivo flash) e permettono di trasferire lo stato dei registri all’ambiente di sviluppo, in modo da avere il completo controllo dello stato del microcontrollore. Ad oggi, le tecnologie di celle flash per la costruzione della memoria programma per un microcontrollore si possono essenzialmente suddividere in due classi. La prima è la tecnologia flash che permette la scrittura e la cancellazione della memoria attraverso dei segnali elettrici che vengono forniti sui pin del microcontrollore mediante un programmatore esterno: tale tecnologia risulta essere la più economica. La seconda, grazie anche all’introduzione all’interno del dispositivo di alcune strutture in grado di generare i corretti valori di tensione per la scrittura della cella (tipicamente  dei Charge Pump), permette di riscrivere la memoria flash direttamente dal programma che il microcontrollore sta eseguendo. Questo risulta particolarmente utile sia per la memorizzazione di parametri, sia per l’aggiornamento  del  firmware  stesso. Microchip  distingue queste due tipologie di flash rispettivamente con il nome di Standard Flash (StdFl) e Enhanched Flash (EnhFl): tipicamente i dispositivi sviluppati con questa tecnologia hanno all’interno anche della memoria E2PROM, per la memorizzazione dei dati.

Figura 1. Program memory nella famiglia PIC16

Figura 1. Program memory nella famiglia PIC16

La program memory nel PIC16 e la scrittura in Flash

I microcontrollori  della famiglia PIC16 hanno un program counter a 13 bit, in grado di indirizzare sino a 8 Kword, dove la word dell’istruzione è a 14 bit. La program memory è mappata in banchi da 2Kword con indirizzi che vanno rispettivamente da 0h a 7FFh, da 800h a FFFh, da 1000h a 17FFh, e da 1800h a 1FFFh come mostrato nella figura 1. In funzione della tipologia di microcontrollore (e quindi in funzione delle dimensioni della program memory) questi banchi possono essere implementati tutti o in parte. Per passare da un banco all’altro si deve allora agire su un registro particolare denominato PCLATH (Program Counter Latch High), che contiene la parte alta del program counter. Quando si chiama una routine che viene mappata su un altro banco, si deve porre attenzione a settare correttamente il PCLATH in modo che il program counter punti alla locazione di memoria dove la routine è mappata: infatti  l’assembler è in grado di gestire solamente il salto all’interno del banco. Dall’analisi della program memory si vede che il reset vector è mappato in posizione 0x00, il quale, in fase di reset conterrà una istruzione del tipo “Goto main” dove “main” sarà l’indirizzo della cella contenente la prima istruzione da eseguire. In posizione 0x04, invece c’è l’interrupt vector, alla quale la CPU salta quando viene generato un interrupt. In questa locazione di memoria, ci sarà un’istruzione del tipo “Goto ISR” dove “ISR” è l’indirizzo della routine di gestione dell’interrupt. Poichè il registro PCLATH non viene modificato durante la chiamata alla routine di interrupt,  si deve dapprima salvare il contenuto  di questo registro (in modo  da ripristinare la condizione al momento  del ritorno dall’interrupt) e forzarne il contenuto in modo da puntare correttamente nel banco in cui la routine dell’interrupt è effettivamente mappata. Le istruzioni di salto (tipo CALL) possono manipolare indirizzi con un salto massimo di 11 bit, (che permettono di indirizzare sino a 2Kword): se il salto supera questo range, si devono forzare i bit della paginazione (PCLATH<4:3>), mentre al ritorno dalla chiamata l’intero Program counter (cioè tutti i 13 bit) viene “recuperato”  dallo stack, non richiedendo la scrittura manuale dei bit PCLATH<4:3>.
La program memory nei dispositivi Enhanched flash (es PIC16F877A) può essere accessibile, sia in lettura che in scrittura, mediante una procedura particolare, implementata forzando il contenuto dei registri:

  • EEDATA e EEDATH: costituiscono una word a 2 byte che contengono la parola a 14 bits che deve essere scritta o che è stata letta dalla program memory.
  • EEADR and EEADRH: contengono i 13 bit che rappresentano l’indirizzo della cella di program memory a cui puntare.
  • EECON1: è il registro di controllo  in cui vengono configurati i bit per l’accesso in EEPROM o in Flash (mediante il bit EEPGD), l’inizializzazione della fase di scrittura, lettura o cancellazione della program memory (RD e WR).
  • EECON2: non è un registro fisicamente implementato nel MCU, ma accedendo a questo registro mediante degli opportuni valori si fa partire la scrittura nella cella (le stesse sequenze possono essere utilizzate per accedere in lettura e scrittura alla memory EEPROM, in quanto l’unica differenza è definita dal bit EEPGD).
Figura 2. I buffer register per la scrittura in flash nella famiglia PIC16

Figura 2.  I buffer register per la scrittura in flash nella famiglia PIC16

L’accesso in program memory è permesso solamente se la cella a cui si punta si trova in una porzione di memoria che non è protetta in scrittura, protezione definita mediante i bit WRT1:WRT0 della configuration word. Inoltre le celle di memoria vengono sempre scritte  a blocchi di 4 word che vengono dapprima bufferizzate all’interno di speciali registri (come evidenziato nella figura 2).
La sequenza fondamentale per la scrittura in flash è allora dettata da prefissati passi, come evidenziato qui sotto:

; Si punta alla program memory
BSF EECON1,EEPGD
; Si abilita la scrittura
BSF EECON1,WREN
; si Disabilitano gli interrupt
BCF INTCON,GIE
; Si fa partire la scrittura
MOVLW 55h
; forzando il valore 55h in EECON2
MOVWF EECON2
MOVLW AAh;
; Si scrive 0xAA in EECON2
MOVWF EECON2
; Si setta il Bit WR
BSF EECON1,WR
; Il processore di fatto si blocca
; per poter far avvenire la scrittura
NOP
; si disabilita la scrittura
BCF EECON1,WREN
; si riabilitano gli interrupt
BSF INTCON,GIE

Si vedrà più avanti come la scrittura in flash sia un elemento essenziale per la gestione di un bootloader.

La program memory nel PIC18 e la scrittura in Flash

I dispositivi della famiglia PIC18 sono per la maggior parte realizzati in tecnologia flash: esistono comunque dei part number OTP (PIC18C), ma la compatibilità pin-to-pin  e la possibilità di avere gli stessi part number in tecnologia flash, hanno sicuramente focalizzato l’interesse da parte degli  sviluppatori  verso questa tecnologia. Per i PIC18F, tuttavia, non esiste solamente la distinzione tra memoria flash standard o enhanched: infatti  è stata da poco introdotta  una famiglia di prodotti intermedi, che hanno la possibilità della auto-programmazione, ma che non hanno la E2PROM e che sono identificati da una lettera “J” nel part number (es.:  PIC18F45J10). Tali dispositivi hanno inoltre una massima tensione di funzionamento limitata da 2,0 V a 3,6 V (completo  range nel quale possono lavorare a 40 MHz), non hanno il Brown out programmabile e il numero di cicli di riscrittura della flash è limitata a 1000 volte: tuttavia, quelle che possono rappresentare delle limitazioni, si traducono in una sensibile riduzione del prezzo del dispositivo. Analizzando in dettaglio la famiglia PIC18 si vede che, essendo l’instruction word a 16 bit, la program memory è a 16 bit. In particolare i dispositivi con memoria Flash Enhanced o della famiglia “J”, permettono la scrittura e la lettura della flash mediante delle opportune  istruzioni. Per poter spostare i dati tra la program memory e la data memory, sono state introdotte due funzioni particolari

  • Table Read (TBLRD)
  • Table Write (TBLWT)

che appoggiano il loro funzionamento su un registro a 8 bit (TABLAT). La prima permette di leggere e spostare i dati dalla program memory alla data memory, mentre la seconda permette di caricare nella program memory i dati presenti nella data memory. Nella figura 3 vengono schematizzate queste operazioni. Tali funzioni operano sempre su dati a 8 bit e possono essere strutturate in blocchi, la cui dimensione dipende dal dispositivo, ma che viene organizzata come sequenze di 8 byte o 4 word. Per permettere la programmazione del dispositivo tali dati vengono dapprima copiati in particolari registri (holding register) quindi viene forzata la reale scrittura in Flash (come mostrato nella figura 4). È possibile ripetere sino a 8 caricamenti successivi degli 8 holding register, in modo da scrivere blocchi di 64 byte. Il tempo di scrittura di questo blocco di memoria è di circa 18 msec e in questo intervallo di tempo di fatto la CPU è bloccata a causa dell’impossibilità di accesso alla Program memory stessa, che è in fase di aggiornamento. Nel caso di scrittura nella Flash di dati organizzati in byte (ad esempio il salvataggio di lookup table per il setup di parametri di funzionamento, o per valori di calibrazione della scheda su cui il micro è montato) i blocchi possono partire da qualsiasi indirizzo, indipendentemente dall’allineamento con la program memory: ciò significa che il dato ad 8 bit può essere scritto sia nella parte bassa che nella parte alta della word, che viene quindi vista come l’unione di 2 byte indipendenti. Nella fase di puntamento alla program memory si deve allora gestire correttamente un incremento unitario, in modo da poter creare un indice pari o dispari e poter puntare nella esatta parte della word. Se si sta gestendo invece una riprogrammazione del dispositivo mediante un codice di bootloader, si deve assolutamente allineare il dato all’instruction word, in modo che la struttura interna alla macchina sia in grado di gestire correttamente l’istruzione scritta nella program memory: l’incremento del puntatore allora sarà a salto doppio (cioè il PC viene incrementato di 2) in modo da puntare sempre nella parte corretta. In questo caso è possibile accedere a tutta la memoria del dispositivo, compresa la configuration memory.

Figura 3. Schematizzazione dell’accesso in program memory mediante TBLRD e TBLWR

Figura 3. Schematizzazione dell’accesso in program memory mediante TBLRD e TBLWR

Figura 4. Gli holding register nella fase di Table Write

Figura 4. Gli holding register nella fase di Table Write

L’elemento fondamentale per entrambe le operazioni rimane la scrittura nella flash, il cui corretto funzionamento dipende dalla configurazione dei seguenti registri:

  • RCON register: in questo registro c’è un bit (LWRT) che abilita la scrittura in program memory mediante le istruzioni definite. Questo bit non può essere modificato durante il normale funzionamento del codice, ma può essere modificato solamente dopo un Power-On Reset (POR) oppure MCLR reset.
  • MEMCON register: questo registro non è presente in tutti  i microcontrollori,  bensì solamente in quelli dotati  di  EMA (External Memory  Addressing). In questi dispositivi (tipicamente ad 80 pin, quali ad esempio i PIC18F8722), parte degli  I/O  possono essere configurati come una struttura per generare gli indirizzi e gestire i dati da leggere o scrivere su una memoria esterna, come se questi pin fossero un’estensione del Program Counter e il data bus della memoria stessa. In questo registro due bit risultano essere molto importanti: il primo è il EBDIS (External Bus  DISable bit) che permette di gestire i bit come dei semplici I/O oppure come BUS esterno, e il bit PGRM (Program RAM enable bit) che permette di discriminare se i GPR (General Purpouse Register) sono quelli interni al dispositivi o vengono mappati esternamente.
  • TBLPTR - Table Pointer Register: Questa struttura serve a costituire un puntatore  per poter accedere alla cella di memoria della program memory. Essendo i registri tutti a 8 bit, tale struttura è costituita da tre diversi registri (TBLPTRU:TBLPTRH:TBLPTRL) che vanno a costituire i 22 bit dell’indirizzo della cella a cui si vuole accedere: in particolare i 21 bit più bassi permettono di accedere alla program memory con una dimensione massima di 1 MWord (2 Mbyte), mentre  il  22-simo  bit  permette  di  accedere al Device ID, allo User ID e ai Configuration  bits. Il Table Pointer Register, può essere modificato (incrementato o decrementato) a seguito di una delle istruzioni riportate nella tabella 1, da cui si vede che è possibile incrementare e decrementare il  Table Pointer immediatamente prima o dopo l’esecuzione della istruzione stessa: questa modalità permette di essere  molto rapido nella gestione dell’accesso in memoria, ottimizzando anche la dimensione del firmware che si sta scrivendo.
ISTRUZIONE OPERAZIONE SUL TABLE POINTER
TBLRD*
TBLWT*
TBLPTR non viene modificato
TBLRD*+
TBLWT*+
TBLPTR viene incrementato dopo il read/write
TBLRD*-
TBLWT*-
TBLPTR viene decrementato dopo il read/write
TBLRD+*
TBLWT+*
TBLPTR viene incrementato prima del read/write
▲  Tabella 1
  • TABLAT - Table Latch Register: un registro a 8-bit mappato nella parte degli special function register (SFR)  e contiene  il  dato  da trasferire tra  le due memorie (data e program).
  • EECON1 control register for memory accesses. In questo registro il bit EEPGD discrimina l’accesso in program memory o data EEPROM. Il bit CGFS determina se l’accesso  è verso i registri della configuration word / calibration oppure alla program / data EEPROM memory.
  • EECON2 – non è un registro fisicamente implementato nella macchina, ma accedendovi con una sequenza opportuna si attiva  la  scrittura, come riportato di seguito.
; Si punta alla FLASH program memory
BSF EECON1,EEPGD
; Si accede FLASH program memory
BCF EECON1,CFGS
; si abilita la scrittura
BSF EECON1,WREN
; si disabilita interrupts
BCF INTCON,GIE
MOVLW 55h
; Si forza il valore 0x55 nel registro
MOVWF EECON2
MOVLW AAh
; Si forza il valore 0xAA nel registro
MOVWF EECON2
; Inizia la programmazione
; e la CPU si blocca
BSF EECON1,WR
NOP

Durante la fase di scrittura o cancellazione della program memory è consigliabile disabilitare gli interrupt. Inoltre in funzione della criticità dell’applicazione, può essere consigliabile, una volta terminata la procedura di scrittura di un blocco, farne una verifica. La cancellazione della memoria avviene a blocchi di 64 byte e la sequenza agisce su un bit FREE.

; Si punta alla FLASH program memory
BSF EECON1,EEPGD
; Si accede FLASH program memory
BCF EECON1,CFGS
; si abilita la scrittura
BSF EECON1,WREN
; si abilita la cancellazione
BSF EECON1,FREE
; si disabilita interrupts
BCF INTCON,GIE
MOVLW 55h
; Si forza il valore 0x55 nel registro
MOVWF EECON2
MOVLW AAh
; Si forza il valore 0xAA nel registro
MOVWF EECON2
; Inizia la cancellazione
; e la CPU si blocca
BSF EECON1,WR
NOP
; si riabilita interrupts
BSF INTCON,GIE

Essendo la scrittura e la cancellazione della flash organizzata in blocchi a 64 byte, si capisce che per modificare anche un solo dato, si deve trasferire il blocco in RAM, modificare il dato che si vuole scrivere e poi riscrivere tutto il blocco.

Il Bootloader e l’aggiornamento firmware

In molti  sistemi è sempre più importante  fornire la possibilità  di aggiornare il firmware da remoto, cioè senza disconnettere  la  scheda dall’applicazione  e senza dover fisicamente connettersi ad un programmatore: si pensi ad esempio all’aggiornamento  del firmware di sistemi installati in luoghi lontani e che possono essere connessi ad esempio attraverso un modem o via GSM. Il bootloader è quella porzione di codice che, quando eseguita, permette di riscrivere la program memory del dispositivo. Al termine di questa fase, mediante una procedura di reset, il microcontrollore eseguirà il nuovo codice, esattamente come se il programma fosse stato caricato tramite un programmatore esterno. Unico accorgimento da tenere in considerazione è il fatto che durante la fase di bootloading non si deve accedere alla porzione di memoria che contiene il bootloader stesso, al fine di non  inficiarne  il  funzionamento. Questa erronea modalità di funzionamento, nella famiglia PIC18, viene di fatto evitata grazie ad una protezione di Self-writing impostabile dalla configuration bit, che va a proteggere la porzione di memoria in cui risiede il bootloader, mappata nella parte bassa della program memory (si veda la figura 5).

Figura 5. La program memory del PIC18F452 prima e dopo la definizione della sezione di bootloader

Figura 5. La program memory del PIC18F452 prima e dopo la definizione della sezione di bootloader

Esistono tre elementi fondamentali che vengono evidenziati dall’analisi della schematizzazione della program memory, ed in particolare si tratta di:

  • Il codice di bootloader viene mappato nei primi 512 byte ( o 256 word). Tale blocco è protetto  in scrittura e quindi anche i vettori di reset e interrupt non sono modificabili.
  • lo spostamento virtuale del reset vector dalla posizione 0x0000 alla posizione 0x0200. Il microcontrollore infatti al momento del reset ha un PC azzerato e quindi punta sempre alla prima istruzione: in questa cella tipicamente viene scritta un’istruzione del tipo “GOTO main”,  dove “main” di fatto è l’indirizzo della program memory dove viene fisicamente mappato il codice che deve essere eseguito. Se si gestisce un bootloader, si deve fare in modo che che nel reset vector ci sia l’indicazione di puntare a questo nuovo indirizzo (0x0200), il quale risulta essere fuori dalla porzione di memoria non riscrivibile e quindi può essere aggiornato con l’indirizzo delle nuove routine di start-up. La posizione di queste routine viene poi gestita in automatico dall’assemblatore, dal compilatore e dal linker al momento della compilazione del codice.
  • lo spostamento virtuale dei vettori di interrupt di alta e bassa priorità, dall’indirizzo 0x0008 e 0x0018 (e quindi nella porzione on riscrivibile) agli indirizzi 0x0208 e 0x0218 rispettivamente. Al momento della generazione di un interrupt, il PC punterà al valore originario del interrupt vector (0x0008 o 0x0018), i quali poi conterranno l’informazione per far saltare il programma ai nuovi indirizzi dei reset vector. In queste celle di memoria (ora nella sezione riscrivibile) allora ci sarà l’istruzione del tipo “goto ISR_xx” , dove “ISR_xx” sarà l’indirzzo della program memory in cui vengono mappate le routine di gestione dell’interrupt. Anche la Data Memory deve essere opportunamente gestita, in quanto si ha la necessità di avere un buffer che vada a caricare i dati che devono essere trasferiti in flash. Tale buffer risulta essere sempre un compromesso tra una dimensione più larga possibile (per aumentare il numero di byte da trasferire in un solo comando) e l’occupazione della memoria: infatti è bene tenere questo blocco di data memory solo per la parte di bootloader, per evitare che durante l’esecuzione del codice possano essere allocate delle variabili in questa zona di memoria, che potrebbero corrompere i dati da scrivere. Lo stesso ragionamento può essere fatto per  la riscrittura in un  PIC16F877A, andando a rimappare il vettore di reset all’indirizzo 0x100 e il vettore di interrupt all’indirizzo 0x104, essendo anche in questo caso il codice di boot mappato nella parte bassa della program memory. Al fine di ottimizzare anche la sicurezza della fase di aggiornamento del codice,  si consiglia di:
    • Introdurre dei particolari accorgimenti nella gestione del protocollo. Tale obiettivo può essere raggiunto introducendo dei caratteri di controllo, quali lo “Start of Text” e “End of Text” (che delimitano il blocco di dati che si vogliono scrivere) e il Checksum (ad esempio facendo il complemento a 2 del Least significant byte della somma dei caratteri ricevuti). Una struttura di protocollo applicabile allora può essere quella riportata in figura 6.
    • Introdurre alcuni flag che identifichino la modalità di boot, in modo da discriminare il fase di reset se la memoria è stata aggiornata correttamente. Si supponga infatti che durante la scrittura della program memory avvenga un reset: se non ci fosse un flag a notificare la fase di scrittura della memoria, si rischierebbe di partire con l’esecuzione del codice che in realtà risulta parzialmente aggiornato. Per evitare allora questa problematica è consigliabile scrivere un flag nella data memory che non venga “sporcato”  dal reset: ne è un esempio una cella di EEPROM che identifica con il proprio valore, la modalità di scrittura della flash oppure il normale funzionamento. Il firmware allora in fase di start-up, andrà a puntare a questa cella, ne leggerà il contenuto e sarà in grado di discriminare se il codice nella sua program memory è corretto (e quindi parte con l’esecuzione del codice) oppure attendersi un comando per la terminazione della scrittura in flash). Microchip  fornisce un esempio di  Bootloader in una application note (AN851) scaricabile dal sito Microchip.
<STX><STX><DATA><CHKSUM><ETX>
; _________/ \____________________________
;
; <COMMAND><DLEN><ADDRL><ADDRH><ADDRU><DATA>...
;
; ed in particolare:
;
; STX – Inizio del pacchetto
; ETX - Fine del pacchetto
; LEN – Lunghezza del pacchetto
; DATA – Dati da scrivere
; CHKSUM – Complemento a 2 della soma di di LEN e DATA
; COMMAND – Comando da eseguire
; DLEN – Lunghezza dei dati associate al commando da eseguire
; ADDR – Indirizzo della cella dove scrivere i dati
; DATA – Dati da scrivere

Figura 6.  Una possibile struttura del protocollo

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend