Self-Programming con ATMEL megaAVR®

Atmel AVR mega

I più recenti microcontrollori ATMEL della serie megaAVR® incorporano avanzate tecnologie che consentono il “Self-Programming” della memoria. Ecco cosa si può fare e come farlo.

Esistono diversi approcci alternativi per la realizzazione di microcontrollori  con la possibilità di eseguire l’auto-scrittura delle memoria programma e le differenza stanno sostanzialmente nella dimensione dei blocchi della memoria codice e di boot, la dimensione del settore che può essere cancellato e riprogrammato, la gestione e la configurazione del settore di boot  e l’implementazione  di  tecniche di  sicurezza per la protezione del codice contro le programmazioni accidentali. La scelta della dimensione del settore che può essere riprogrammato, è un compromesso tra tempo di programmazione e flessibilità: un settore più ampio consente di accorciare sensibilmente il tempo di programmazione e cancellazione ma risulta una soluzione poco flessibile qualora sia necessario aggiornare solo piccole parti della memoria. Oltre a questo non è da trascurare il fatto che se il singolo settore è grande sarà necessario disporre di una sufficiente quantità di memoria RAM per contenere i dati da scrivere. Una RAM più ampia comporta una più ampia superficie del die con conseguente incremento del costo del microcontrollore.

La soluzione ATMEL

Per la gestione del settore di boot  della memoria esistono tipicamente due approcci:
Flash e ROM. Nel caso della tecnologia ROM, il settore di boot viene scritto in fase di fabbricazione del wafer e può essere indirizzato attraverso dei pin esterni. Questa tecnologia è particolarmente sicura in quanto evita la possibilità di riprogrammare accidentalmente il settore di boot,  ma ha l’evidente svantaggio che il contenuto del settore di  boot non  può  essere modificato  in  alcun modo. A questo inconveniente si ovvia utilizzando  la tecnologia  Flash. In questo caso si deve prevedere un algoritmo  di sicurezza in modo da evitare la scrittura accidentale del boot sector visto che questo è ora modificabile. Le soluzioni si basano generalmente sull’uso di un lock bit ed eventualmente sull’implementazione di  algoritmi di encription/decription che innalzano ulteriormente il grado di protezione. La soluzione di ATMEL implementata  sui microcontrollori della famiglia megaAVR® prevede un blocco di boot programmabile le cui dimensioni vanno da 256 byte fino a 4Kbytes a seconda del dispositivo. Usando micro con settore di boot piuttosto  ampio  consente di  implementare  l’algoritmo  DES in 2Kbytes. La struttura è quella di figura 1.

Figura 1. La soluzione ATMEL per il self-programming

Figura 1. La soluzione ATMEL per il self-programming

Tutti  i dispositivi della famiglia megaAVR® offrono all’utilizzatore la possibilità di eseguire le istruzioni nel settore di boot durante la cancellazione/scrittura dei blocchi della memoria programma. Il vettore di interruzione può essere trasferito dalla memoria programma al settore di boot consentendo al microcontrollore di rispondere ad interruzioni critiche anche in fase di scrittura della memoria programma.

Read-While-Write

Come mostra la figura 1, la memoria Flash è suddivisa in due sezioni: la sezione programma  e la sezione si boot. La dimensione delle sezioni dipende dalla configurazione dei  fuses BOOTSZ come  mostrato  nella figura 2.

Figura 2. Struttura della memoria nella famiglia megaAVR®

Figura 2. Struttura della memoria nella famiglia megaAVR®

La sezione programma  è dedicata ad accogliere il codice dell’applicazione vera e propria ed il livello di protezione per questa sezione può essere impostato mediate i bit lock in accordo alla tabella 1.

BLB0 MODE BLB02 BLB01 PROTEZIONE
1 1 1 Nessuna
2 1 0 SPM non può scrivere nella sezione programma
3 0 0 SPM non può scrivere nella sezione programma e LPM, eseguita dalla sezione
di boot, non può leggere dalla sezione programma. Se il vettore di interruzione
è mappato nella sezione di boot, le interruzioni vengono disabilitate durante
l’esecuzione del codice dalla sezione programma.
4 0 1 L’esecuzione dell’istruzione LPM dalla sezione di boot non può leggere dalla
sezione programma. Se il vettore di interruzione è mappato nella sezione di
boot, le interruzioni vengono disabilitate durante l’esecuzione del codice
dalla sezione programma.
▲ Tabella 1.  I livelli di protezione per la sezione codice

Le restrizioni coinvolgono  ovviamente le istruzioni SPM (Store Program Memomry)  ed LPM (Load Program Memory). L’eventuale bootloader dovrà essere necessariamente collocato nella sezione di boot in modo che mediante  l’istruzione SPM sia possibile iniziare la programmazione della sezione programma. I livelli di protezione per la sezione di boot sono impostabili mediante i flag BLB11 e BLB12 in accordo alla tabella 2.

BLB1 MODE BLB12 BLB11 PROTEZIONE
1 1 1 Nessuna
2 1 0 SPM non può scrivere nella sezione di boot
3 0 0 SPM non può scrivere nella sezione di boot e LPM, eseguita dalla sezione
programma, non può leggere dalla sezione di boot. Se il vettore di interruzione
è mappato nella sezione programma, le interruzioni vengono disabilitate
durante l’esecuzione del codice dalla sezione di boot.
4 0 1 L’esecuzione dell’istruzione LPM dalla sezione programma non può leggere
dalla sezione di boot. Se il vettore di interruzione è mappato nella sezione
programma, le interruzioni vengono disabilitate durante l’esecuzione del codice
dalla sezione di boot.
▲ Tabella 2.  I livelli di protezione per la sezione boot

Oltre alla suddivisione appena vista, la sezione programma è a sua volta suddivisa in due zone: la zona Read-While-Write (RWW) e la zona No-Read-While-Write (NRWW). Mentre è possibile leggere dalla zona NRWW mentre si effettua una scrittura/cancellazione di dati dalla zona RWW, la CPU risulta bloccata durante la scrittura/cancellazione di dati all’interno della zona NRWW.

Il Bootloader

L’entrata nella sezione del bootloader  avviene a seguito di un salto o di una chiamata dal programma principale o a seguito del verificarsi di  un  evento esterno come ad esempio un codice ricevuto via UART o via SPI. Come alternativa si possono configurare i fuses in modo  da far puntare il reset vector all’indirizzo della sezione boot  avviando in questo modo il bootloader al reset del microcontrollore.  Le operazioni del bootloader vengono configurate attraverso il registro SPMCSR (Store Program Memory Control and Status Register) la cui struttura è riportata in figura 3.

Figura 3. Il registro SPMCSR

Figura 3. Il registro SPMCSR

Ecco in dettaglio la funzione di ogni singolo bit:

SPMIE (SPM Interrupt Enable):  se ad 1 viene generata una interruzione al termine dell’esecuzione della istruzione SPM.

RWWSB (Read-Wile-Write  Section Busy): viene messo ad 1 ogni volta che viene eseguita una operazione (scrittura/cancellazione) nella sezione RWW. Quando è ad 1 non è possibile accedere all’area RWW.

RWWSRE (Read-While-Write    Sectione Read Enable): Agendo sulla sezione RWW tale sezione viene bloccata automaticamente tramite il flag RWWSB e viene riabilitata solo al termie dell’operazione in corso. Se RWWSRE  è ad 1 l’istruzione SPM riabilita  la zona RWW entro quattro cicli istruzione.

BLBSET (Boot Lock Bit Set): una volta messo ad 1 la prima istruzione (entro quattro cicli di clock) SPM aggiorna i Lock bit in accordo a quanto scritto nel registro R0.
La prima istruzione (entro tre cicli di clock) LPM leggerà il valore dei Lock bit.

PGWRT (Page Write): una volta messo ad 1, la prima istruzione SPM entro  quattro  cicli clock, scrive in memoria il contenuto del buffer. Al termine  della scrittura o se non viene eseguita alcuna istruzione SPM antro i quattro cicli clock, tale bit viene posto automaticamente a zero. Durante la scrittura dei dati la CPU risulta bloccata.

PGRES (Page Erase): una volta messo ad 1, la prima istruzione SPM entro quattro  cicli clock provoca la cancellazione di un blocco di memoria.

SPMEN (Store Program Memory Eanble): abilita l’istruzione SPM alla scrittura in memoria.

Prevenire la perdita di dati

Se la tensione di alimentazione scende al di sotto dei valori nominali, la memoria flash viene trovarsi a lavorare in condizioni non ottimali con la conseguente perdita dei dati memorizzati. La perdita dei dati a causa della bassa tensione di alimentazione può avvenire per due effetti: l’operazione di scrittura richiede specifici valori di alimentazione per cui con  tensioni troppo  basse i dati possono essere scritti in modo errato (un “1” logico  può  essere erroneamente  interpretato come uno “0” logico); l’istruzione di scrittura non viene eseguita correttamente  in  quanto  la CPU non opera nelle condizioni nominali. Per evitare questi problemi è necessario attenersi a semplici regole di progettazione quali:

  1. Se nell’applicazione non è necessario un bootloader è consigliato proteggere la sezione di boot per evitare aggiornamenti non voluti;
  2. Mantenere attivo il reset fintanto che la tensione di alimentazione non raggiunge i valori nominali;
  3. Mantenere in power-down  il core del microcontrollore per tutto il tempo in cui la tensione di alimentazione si mantiene a livello basso.

Un esempio di bootloader

Nel listato 1 è riportato un esempio di codice Assembler per  un  bootloader  scritto  per ATmega128. La routine scrive una pagina di  dati dalla memoria RAM alla memoria Flash. I dati sono memorizzati in RAM a partire dalla locazione indicata dal puntatore Y e scritti in Flash a partire dall’indirizzo indicato nel puntatore Z. La routine dovrà essere collocata nella sezione di boot.

Boot
.equ PAGESIZEB = PAGESIZE*2 ;dimensione della pagina in byte
.org SMALLBOOTSTART
Scrivi_pagina:
;cancellazione della pagina
ldi spmcsrval, (1<<PGERS) | (1<<SPMEN)
call Do_spm
; abilitazione della sezione RWW
ldi spmcsrval, (1<<RWWSRE) | (1<<SPMEN)
call Do_spm
; trasferimento dati dalla RAM alla Flash
ldi looplo, low(PAGESIZEB)
ldi loophi, high(PAGESIZEB);non necessario se PAGESIZEB<=256
Ciclo_Wr:
ld r0, Y+
ld r1, Y+
ldi spmcsrval, (1<<SPMEN)
call Do_spm
adiw ZH:ZL, 2
sbiw loophi:looplo, 2 ;usare subi se PAGESIZEB<=256
brne Ciclo_Wr
; scrittura della pagina
subi ZL, low(PAGESIZEB) ;ripristino del puntatore
sbci ZH, high(PAGESIZEB) ;non necessario se PAGESIZEB<=256
ldi spmcsrval, (1<<PGWRT) | (1<<SPMEN)
call Do_spm
; abilitazione della sezione RWW
ldi spmcsrval, (1<<RWWSRE) | (1<<SPMEN)
call Do_spm
; Rilettura e controllo (opzionale)
ldi looplo, low(PAGESIZEB)
ldi loophi, high(PAGESIZEB);non necessario se PAGESIZEB<=256
subi YL, low(PAGESIZEB) ;ripristino del puntatore
sbci YH, high(PAGESIZEB)
Coclo_Rd:
lpm r0, Z+
ld r1, Y+
cpse r0, r1
jmp Errore
sbiw loophi:looplo, 1 ;usare subi se PAGESIZEB<=256
brne Ciclo_Rd
; ritorno alla sezione RWW
; verifica che la sezione RWW sia pronta per la lettura
Ritorna:
lds temp1, SPMCSR
sbrs temp1, RWWSB ; Se RWWSB=1, la sezione RWW non è pronta
ret
; abilitazione della sezione RWW
ldi spmcsrval, (1<<RWWSRE) | (1<<SPMEN)
call esegui_spm
rjmp Ritorna
esegui_spm:
; attesa del completamento dell’istruzione SPM
Attendi:
lds temp1, SPMCSR
sbrc temp1, SPMEN
rjmp Attendi
; disabilitazioni interrupt e salvataggio di stato
in temp2, SREG
cli
; verifica che non vi siano accessi in EEPROM
Attendi_eeprom:
sbic EECR, EEWE
rjmp Attendi_eeprom
sts SPMCSR, spmcsrval
spm
; rispristino di SREG
out SREG, temp2
ret

Listato 1

Indirizzamento della memoria

Poiché la memoria è organizzata in pagine, il Program Counter può essere considerato suddiviso in due sezioni: gli 8 bit meno significativi indirizzano la word all’interno di una pagina di memoria, mentre gli 8 bit più significativi indirizzano la pagina. Le istruzioni di cancellazione e scrittura di un cella di memoria, vengono indirizzate in maniera indipendente pertanto è necessario che il bootloader si riferisca alla stessa pagina di memoria durante le suddette operazioni. La figura 4 mostra l’indirizzamento della memoria.

 

Figura 4. L’indirizzamento della memoria

Figura 4. L’indirizzamento della memoria

Una volta iniziata l’operazione di programmazione, i registri Z e RAMPZ possono essere utilizzati nuovamente in quanto  l’indirizzo  coinvolto  nella fase di programmazione viene bufferizzato. L’operazione SPM è l’unica che non usa i registri Z e RAMPZ, ma si avvale dei Boot Loader Lock Bits. Nell’operazione di Self-Programming, la memoria viene aggiornata pagina per pagina e, prima di iniziare la programmazione, la pagina deve essere cancellata. Il buffer contenente i nuovi dati da scrivere nella pagina, viene invece aggiornato per singole word e  l’aggiornamento del  buffer  può  avvenire sia prima della fase di cancellazione che prima della fase di scrittura della pagina. Qualora sia necessario  aggiornare solo parte del contenuto  della pagina, sarà quindi  necessario copiare l’intera pagina nel buffer, aggiornare le singole word all’interno del buffer, quindi trasferire il  contenuto  del  buffer  così aggiornato  nella relativa pagina di memoria. È  ovvio che in questo caso è strettamente necessario procedere con la cancellazione della pagina solo prima di eseguire l’operazione di scrittura.

Riferimenti

Informazioni generali su AVR:  http://www.atmel.com/products/avr

AVR230: DES Bootloader – Application note  http://www.atmel.com/dyn/resources/prod_documents/doc2541.pdf

AVR109 Self-Programming:  http://www.atmel.com/dyn/resources/prod_docu- ments/doc1644.pdf

Scarica subito una copia gratis

Una risposta

  1. Maurizio Di Paolo Emilio Maurizio 29 dicembre 2015

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend