HEF ?
Nei microcontroller, la presenza di EEPROM è molto utile per salvare impostazioni
utente, dati di calibrazione e simili. Quanto scritto in EEPROM permane anche
se il chip non è alimentato.
Nei PIC più recenti, Microchip ha sostituito EEPROM con HEF, acronimo di High Endurance
Flash, ovvero Flash a Lunga Durata.
Tuttavia, HEF non è uguale a EEPROM e l'unica documentazione specifica su High Endurance Flash
(HEF) è l' AN1673
e quanto scritto sui fogli dati dei vari componenti. Anche perchè c'è ancora
qualche confusione dovuta al fatto dal fatto che nelle specifiche di alcuni chip si indica EEPROM, mentre
poi si tratta di HEF.
Dai fogli dati si rileva che l' area HEF, che corrisponde alle ultime
locazioni (typ. 128) della Flash, ha una durata minima garantita di 100K cicli di
scrittura, rispetto ai 10K del resto della Flash. Questo porta le
caratteristiche di durata (endurance) della HEF ad essere comparabili con quelle della
EEPROM.
La differenza essenziale è che la HEF fa parte della memoria programma FLASH:
si tratta quindi di scrivere e leggere dati da un'area della Flash, che, negli Enhanced Midrange e nei Midrange, è della stessa ampiezza del
bus istruzione, ovvero 14 bit.
Dato che il bus dati è a 8 bit, i fogli dati parrebbero affermare che solamente
gli 8 bit più bassi delle locazioni
HEF sono validamente utilizzabili. Da questo si dovrebbe affermare che le ultime locazioni della Flash siano
composte da memoria di programma in grado di contenere 14 bit (così come il resto della memoria
Flash), ma dei quali gli 8 bit LSB sono HEF Flash e i 6 bit MSB sono di flash normale.
Ne deriva che ogni scrittura dovrà agire in HEF flash sugli 8 bit bassi,
scartando i 6 bit alti.
Sia l'area HEF che la Flash normale differiscono da un modulo dati EEPROM
in due modi. Nella HEF:
- I dati devono essere cancellati manualmente prima di una scrittura e questo può essere
eseguito solo
in blocchi (denominati righe) di una dimensione fissa (tipicamente 32
locazioni)
- Scrivendo la Flash si manda in stallo la CPU per alcuni millisecondi (param.
D123, tipicamente 2-2.5ms)
Per contro, in una EEPROM reale i dati possono essere scritti byte per
byte e non c'è stallo nella CPU durante la scrittura, anche se , per ragioni di semplicità
costruttiva, la maggior parte delle applicazioni includeranno un ciclo di ritardo per assicurare
che la scrittura è stata completata prima del successivo passo.
Le differenze indicate implicano che ci sono differenze nel trattamento
dei dati in HEF rispetto a quanto finora visto per EEPROM e che sia necessaria
una corretta comprensione dei meccanismi quando si sostituisce HEF a EEPROM.
Vediamo alcuni dettagli.
Per tutti gli esempi seguenti facciamo riferimento a Enhanced Midrange del
gruppo 16F150x, ma quanto detto vale anche per gli altri chip dotati di HEF,
con l'obbligo di verificare la posizione nella mappa di memoria dell'area HEF,
la sua estensione e l' ampiezza della riga, parametri che possono variare a
seconda del chip.
Operazioni sulla HEF.
HEF è Flash e quindi le operazioni di lettura e scrittura impiegano le
stesse procedure che sono impiegate per la Flash "normale".
L' unica differenza consiste nel fatto che, dei 14 bytes letti, solo i primi 8
(LSB) vanno considerati, mentre i rimaneti 6 sono scartati.
Vediamo nei particolari le varie operazioni.
Lettura della HEF.
Per quanto riguarda la lettura di dati nella HEF, non esistono limitazioni
nella dimensione, nè tanto meno è richiesto uno stallo della MPU.
Il problema essenziale è che la lettura della Flash non è la stessa cosa
della lettura della RAM, in quanto richiede, analogamente a quanto accede per
la EEPROM, una procedura specifica, con l'
uso di registri dedicati.
Per gli Enhanced Midrange si tratta dei registri PMADRL
e PMADRH per l' indirizzo e dei registri PMDATL
e PMDATH per
il dato. Inoltre, entra in gioco il registro PMCON1.
Importante considerare che questi chip hanno tipicamente l'area RAM suddivisa
in ben 32 banchi e che i registri di gestione delle operazioni in Flash si
trovano tipicamente nel banco 3, il che richiede l' uso dello switch dei
banchi.
Se gli Enhanced Midrange semplificano questa operazione con l' istruzione bsr,
l' uso del classico BANKSEL è la
soluzione più pratica, dato che è valida per qualsiasi famiglia di PIC.
|
L'operazione non è complessa ed è divisibile in tre fasi:
- piazzare nei registri PMADRL e PMADRH
l' indirizzo voluto
- selezionare nel registro la modalità di lettura dell' area Flash
- recuperare il dato nei registri PMDATL
e PMDATH
L'indirizzo può essere uno qualsiasi all' interno dell' area HEF.
Se l' uso di una coppia di registri per l' indirizzo è evidente, diventa
meno immediata la necessità di una coppia di registri per il dato in lettura.
Però, se ricordiamo come la Flash sia una memoria programma a 14 bit, ne
deriva che l'uso di questi come dati ne prevede la suddivisione in un byte
basso a 8 bit ed in uno alto a 6 bit.
In effetti, vediamo nella routine più sotto, analoga a quella della lettura di
un dato in una locazione generica della Flash, come il byte alto venga scartato,
anche perchè, come indicato inizialmente, non è HEF e quindi inadatto a
sostenere con sicurezza un numero elevato di cicli di scrittura.
Osservare la necessità di introdurre due nop
una volta iniziata l' operazione di lettura: questo tempo extra è
necessario in quanto il processore ha due fetch di istruzione che
ignorerà; inserendo due nop
si risolve il problema. |
Ecco un esempio di come si può trasformare la cosa in istruzioni:
; Legge 1
byte dall'indirizzo HEF_ADDR
Read_HEF
BANKSEL PMADRL ; Select Bank for PMCON registers
movlw low(HEF_ADDR) ; Load initial address
movwf PMADRL
movlw high(HEF_ADDR)
movwf PMADRH
bcf
PMCON1,CFGS ; select flash Space
bsf PMCON1,RD
; Initiate read
nop
nop
; get only LSB - 8 bit HEF
movf PMDATL,W
; Get LSB of word
return |
Il trasferimento del dato dalla cella Flash al bus dati è immediato, ma l'
operazione per arrivare a questo dato, come si vede, richiede numerose
istruzioni. Ne deriva che, necessitando di leggere dati in HEF la procedura
più sensata è quella effettuare, ad esempio ad inizio programma, il
trasferimento del contenuto di blocco e copiarlo in una delle aree
RAM dati disponibili, dalla quale i dati saranno accessibili immediatamente.
La routine seguente utilizza l' indirizzamento indiretto post indicizzato FSR0++
e l' istruzione movwi
specifica del set degli Enhanced Midrange per spostare una
riga di HEF una una area RAM dati.
L' indirizzo HEF_ADDR dovrà essere quello
di inizio della riga che si vuole copiare.
; Copia 32 bytes
dall'indirizzo HEF_ADDR alla RAM all'indirizzzo hefdata
; Scarta MSB delle word
Read_HEF32:
BANKSEL PMADRL
; Select Bank for PMCON registers
movlw
low(HEF_ADDR) ; Load initial HEF address
movwf
PMADRL
movlw
high(HEF_ADDR)
movwf
PMADRH
movlw
LOW hefdata ; Load initial data address
movwf
FSR0L
movlw
HIGH hefdata
movwf
FSR0H
RdH_lp bcf
PMCON1,CFGS ; select flash Space
bsf
PMCON1,RD ; Initiate read
nop
nop
; get only LSB - 8 bit HEF
movf
PMDATL,W ; Get LSB of word
movwi
FSR0++ ; Store in user location
; i byte alti vengono scartati
;movf PMDATH,W
;movwf data_hi
movf
PMADRL,W ; Check lower bits of address
xorlw
0x1F ; Check if we're on the last of 32 addresses
andlw
0x1F
skpnz
goto
RdH_end
incf
PMADRL,f
bra
RdH_lp
RdH_end:
return |
L'indirizzo della HEF.
Abbiamo accennato al fatto che la HEF occupa l' ultima parte della Flash e
quindi il suo indirizzo di partenza dipende dal processore considerato.
Sfortunatamente i fogli dati sono oscuri e confusi sull'argomento, in modo molto
irritante.
Occorre ricercare con certosina pazienza quanto serve per definire l' area HEF.
Troviamo parte delle informazioni nella sezione e parte dove si
descrivono le operazioni in Flash, ma possiamo benissimo trovarci davanti a
fogli dati reticenti o del tutto sballati.
Ad esempio, per 10F320 il foglio dati DS40001585C-page 2 indica una Program
Memory Flash da 512 words, il che equivarrebbe ad una zona tra 000 e 1FFh.
Purtroppo lo stesso documento, a pagina 9 riporta la TABLE 2.1:
che è evidentemente contiene un errore nell' Address Range di entrambe le
aree HEF, che il Silicon Errata and Data Sheet Clarification DS80000529F
non corregge, nè tanto meno "chiarifica".
Non è secondario, poichè il foglio dati non specifica la
dimensione della HEF, che invece troviamo indicata chiaramente solo nella descrizione
generale del componente , dove vengono dichiarati 128 bytes.
Ora, se la Flash di 10F320 è di 256, inizia a 00 e termina a FFh. Se 128 sono
HEF, questa occupa metà della Flash: inizia a 80h e finisce a FFh!
In compenso, abbiamo l' indicazione "spot" che solo il low byte è
HEF.
Bisogna poi passare al capitolo relativo alla Flash, pag.91, per arrivare a
sapere che la riga ha una ampiezza di 16 word.
Abbastanza analoga la situazione delle informazioni per altri chip;
piuttosto desolante per quella che dovrebbe essere una documentazione tecnica
di un componente complesso.
Comunque, ecco le caratteristiche di alcuni:
PIC |
Flash |
Inizio HEF |
Dimensione |
Riga |
16F1507 |
2k |
0780h |
128 |
32 |
16F1508 |
4k |
0F80h |
128 |
32 |
16F1509 |
8k |
1F80h |
128 |
32 |
16F1516/7 |
8k |
1F80h |
128 |
32 |
16F1518/9 |
16k |
3F80h |
128 |
32 |
10F320 |
256 |
80h |
128 |
16 |
10F322 |
512 |
180h |
128 |
16 |
Questi elementi serviranno per le definizioni nel sorgente. Ad esempio,
volendo pre caricare una riga:
; HEF memory preload
#ifdef __16F1508
HEFDATA CODE 0F80h
#endif
#ifdef __16F1509
HEFDATA CODE 1F80h
#endif
#ifdef __16F1507
HEFDATA CODE 0780h
#endif
DA 60h, 0, 99h, 9, 40h, 03h, 0, 0 ;8 bytes
DA
"MICROCONTROLLER.IT@ 2015"
;24 bytes - tot 32 bytes |
Stallo della MCU
In contrasto con il comportamento di una EEPROM , la MCU non è in grado di recuperare nuove istruzioni dalla Flash durante un ciclo di auto-scrittura ed è,
pertanto, in fase di stallo fino a che l' operazione di scrittura (o
cancellazione) non è
conclusa: dobbiamo aver ben chiaro che la scrittura nella Flash implica il
fatto che la Flash stessa è l' area che contiene i codici delle istruzioni:
se stiamo scrivendoci dentro, non possiamo contemporaneamente leggere opcodes....
Questo significa che nessuna altra azione può essere eseguita durante una cancellazione o scrittura, né
è possibile rispondere ad interrupt, che va disabilitato.
Al termine della scrittura, l' esecuzione del codice riprende automaticamente. Il progettista deve, quindi, tener
conto del jitter aggiuntivo introdotto o del congelamento momentaneo dell'applicazione
durante gli aggiornamenti di memoria non volatile.
Va notato che la durata dello stallo è indipendente dalla quantità di dati scritti in una
riga, operazione eseguita in una sola azione.
Il problema dello stallo della MCU ,nonostante quello che si possa temere,
normalmente non è per nulla un evento critico. Basta semplicemente
considerare la presenza di questo tempo ed effettuare gli aggiornamenti della
Flash in momenti del programma in cui esso non è causa di problemi. In
pratica, trattandosi di un tempo limitato (max. 2.5ms), non è percepibile
dall' utente (ad esempio, non disturba neppure il multiplex di un gruppo di
display a sette segmenti, anche se sospende l' interrupt). Solo nel caso in
cui la gestione dell' interrupt sia critica, occorrerà definire con
precisione il momento delle operazioni sulla HEF, ad esempio all' accensione e
allo spegnimento.
Cancellazione di una riga
Se non esistono limitazioni sulla lettura dei dati dall'area HEF, quando si
scrive o cancella un dato occorre garantirne l' allineamento corretto nella
matrice, ovvero all' interno di un blocco, o riga.
Questo vuol dire che non è possibile scrivere (cancellare) un singolo byte, ma occorre
riscrivere o cancellare l' intera riga.
Pertanto, una operazione di aggiornamento della HEF, anche per un solo
elemento, deve considerare le seguenti azioni:
- copia in RAM della riga che contiene il dato
- modifica del dato
- cancellazione della riga in HEF
- scrittura della riga
Da osservare che la cancellazione è necessaria, di per se, solamente per i
bit che si trovano a livello 0: la Flash "cancellata" ha i bit a
livello 1. Comunque, se anche un solo bit della riga contiene uno 0, la cancellazione
riguarda l' intera riga.
Fortunatamente vedremo subito che Microchip ha implementato un sistema
alquanto efficiente per cui la cancellazione o la scrittura della riga è
effettuata in una sola operazione.
|
Ecco il flow chart delle operazioni necessarie alla cancellazione di
una intera riga.
Per prima cosa, se l'interrupt è abilitato, occorre disabilitarlo.
Come per la lettura, ci si appoggia ai registri speciali PMADRL e PMADRH
che, ricordiamo, non sono in banco 0.
La cancellazione avviene a partire dall' indirizzo di inizio della
riga, ma, in effetti, basta fornire un indirizzo della riga e la
cancellazione avviene per tutta la riga corrispondente.
Il registro dovrà essere inizializzato selezionando il bit
di memoria CFGS.
Come per le EEPROM, occorre una "chiave" di accesso alle
operazioni di cancellazione e scrittura e questa è costituita da una
ben precisa sequenza di istruzioni.
Dopo aver programmato i bit FREE e WREN per abilitare la
cancellazione, occorreà scrivere la "chiave" AAh e 55h.
A questo punto si avvia la cancellazione e la MPU va in stallo per
2-2.5ms. In questo periodo il processore è l'I/O sono congelati.
Esaurito il tempo di stallo, il programma riprende automaticamente
dall'opcode successivo.
Occorrerà ora disabilitare la scrittura con il bit WREN e
riabilitare, se richiesto, l' interrupt.
Come si vede, una procedura del tutto simile a quella usata per la
EEPROM.
|
Ed ecco le istruzioni:
; Cancella una riga di HEF (32 word)
Erase_HEF
bcf INTCON,GIE
; Disable int
BANKSEL PMADRL
movlw low(HEF_ADDR) ; lower
address
movwf PMADRL
movlw high(HEF_ADDR) ; upper
address
movwf PMADRH
bcf PMCON1,CFGS ; Not configuration space
bsf PMCON1,FREE ; Specify an erase operation
bsf PMCON1,WREN ; Enable writes
movlw 055h
; Start erase sequence
movwf PMCON2
movlw 0AAh
movwf PMCON2
bsf PMCON1,WR
; Set WR bit to begin erase
nop
nop
; The processor stalls until the erase process is complete
; after erase processor continues with 3rd instruction
bcf PMCON1,WREN ; Disable writes
bsf INTCON,GIE
; Enable interrupts
return
|
Osserviamo che non servono "dati" per la cancellazione; cancellare
una locazione significa semplicemente portare il livello elettrico dei bit a
1. Di conseguenza una locazione cancella a 14-bit conterrà 3FFh.
|
Per chiarezza, questa è struttura della "chiave":
Viene utilizzato uno specifico SFR, PMCON2,
nel quale occorre scrivere, nella sequenza indicata, le chiavi AAh e
55h.
Da osservare la necessità dei due nop
già visti per la lettura.
Se non si utilizza questo algoritmo, non è possibile cancellare o
scrivere nella Flash.
La sua necessità è evidente, in quanto la Flash contiene il
firmware, che non deve rischiare di essere danneggiato da eventi
casuali, ma che può essere modificato solamente a seguito di una
azione volontaria del programma. |
Scrittura di una riga
Analogamente alla
cancellazione, la scrittura richiede una "chiave", ma, questa volta,
anche dei dati.
Dovendo scrive una intera riga per volta, occorre un sistema per evitare il
ripetersi di 16 o 32 stalli alla scrittura di ogni byte. Il meccanismo
implementato dal costruttore è quello di un buffer, ampio come la riga, che
viene scritto in Flash in una unica azione, riducendo lo stallo ai già visti
2-2.5ms.
Ovviamente il buffer deve essere caricato con word a 14
bit e questo richiede un certo numero di istruzioni: ecco il flowchart
consigliato dal foglio dati
Osserviamo che nella scrittura dei latches non occorre
alcun ritardo.
In istruzioni. Copiamo una riga da 32 bytes dalla RAM
alla HEF.
Osserviamo che la Flash è composta da due "bytes",
uno di 8 e uno di 6 bit. A noi interessa, trattandosi di HEF, solo la parte a
8bit e si scarterà quella a 6, caricandola con un valore dummy.
; Copia 32 bytes da RAM dall' indirizzo hefdata all'indirizzo
; HEF_ADDR della HEF
; Dati immagazzinati in formato little endian
; L'MSB delle word è il valore dummy 3Fh
Write_HEF
bcf
INTCON,GIE ; Disable ints
BANKSEL PMADRH
; Bank 3
movlw low(HEF_ADDR)
; Load initial address
movwf PMADRH
movlw high(HEF_ADDR)
movwf PMADRL
movlw LOW hefdata
; Load initial data address
movwf FSR0L
movlw HIGH hefdata
movwf FSR0H
bcf
PMCON1,CFGS ; Not configuration space
bsf
PMCON1,WREN ; Enable writes
bsf
PMCON1,LWLO ; Only Load Write Latches
WHlp moviw FSR0++
; Load first data byte into lower
movwf PMDATL
;moviw FSR0++ ; discard second data byte
movlw 3Fh
; and use dummy value for MSB
movwf PMDATH
movf
PMADRL,W ; Check if lower
address
xorlw 0x1F
andlw 0x1F
skpnz
; Exit if last of 32 words
bra
WHstart ;
otherwise re loop
movlw 055h
; write sequence
movwf PMCON2
movlw 0AAh
movwf PMCON2
bsf
PMCON1,WR ; Set WR bit to begin write
nop
nop
incf PMADRL,F
; latches Increment address
bra
WHlp ; Write next latches
WHstart
bcf
PMCON1,LWLO ; memory write
movlw 055h
; write sequence
movwf PMCON2
movlw 0AAh
movwf PMCON2
bsf
PMCON1,WR ; Set WR bit to begin write
nop
; all latches simultaneously
nop
; to program memory.
; the processor stalls until the self-write process complete. ; After write processor continues
bcf
PMCON1,WREN ; Disable writes
bsf
INTCON,GIE ; Enable interrupts
return
|
Osserviamo come l' uso dell'indirizzamento indiretto post
incrementato e l'opcode
moviw
facilitino l'operazione.
L'MSB (i 6 bit non HEF) è caricato con un valore che ha tutti i bit a 1 ed
evita una in utile scrittura.
Modifica della HEF.
|
La modifica del contenuto della HEF è realizzabile semplicemente
organizzando quanto detto finora.
Dobbiamo partire sempre dal concetto base che è l' operazione su
una intera riga, sia che si debba modificare un solo byte o più bytes.
Il contenuto della riga ha posto come immagine in RAM: in una area
adeguata posso copiare il contenuto della riga.
Le modifiche verranno effettuate su questa immagine in RAM.
Quindi si provvederà a cancellare la riga e a riscriverla con il
contenuto desiderato.
Le operazioni di blocchi di dati sono facilmente realizzabili con
l' uso dell'indirizzamento indiretto e delle nuove istruzioni
relative. |
Ecco un esempio di macro per movimentare un c erto numero di byte da
un'area RAM ad un'altra.
; Copy n bytes da source a target
COPYMEM MACRO source, target, nbyte
local c_0
movlw nbyte
movwf temp
movlw low(source)
movwf FSR0L
movlw high(source)
movwf FSR0H
movlw low(target)
movwf FSR1L
movlw high(target)
movwf FSR1H
c_0 moviw FSR0++
movwi FSR1++
decfsz temp,f
bra
c_0
ENDM |
Osserviamo l'impiego di due puntatori indiretti e della coppia di istruzion
moviw - movwi
.
Sorgente e destinazione possono essere in qualsiasi banco, dato che,
attraverso gli FSR possiamo raggiungere
qualsiasi punto della RAM. Il post incremento riduce il numero delle
istruzioni necessarie al loop.
Verifica.
|
Una volta effettuata una scrittura, può essere utile verificare che i dati
siano stati correttamente copiati in HEF.
Il procedimento è semplice.
Consideriamo come riferimento la serie di dati che abbiamo copiato
dalla RAM nei buffer per la scrittura della Flash.
Leggiamo uno alla volta gli elementi della riga appena scritta e li
compariamo con la copia in RAM. Se ci sono differenze, la scrittura
non è riuscita e si dovranno prendere le conseguenti decisioni.
Se abbiamo avuto cura di realizzare le varie funzioni come subroutines o macro, il loro riutilizzo è immediato e consente un sensibile risparmio di tempo e risorse. |
E con il C?
L' AN1673
tratta il problema dal punto di vista del compilatore C, per il quale esiste
una libreria specifica per la scrittura della Flash.
|