Der Programmcode innerhalb des Urlader-Bereiches kann in den gesamten Flash schreiben, also auch in den Urlader-Bereich. Der Urlader kann sich daher selbst modifizieren oder auch sich selbst löschen, wenn er nicht mehr benötigt wird. Die Größe des Urlader-Bereichs wird mit zwei Fuses konfiguriert. Ferner hat der Urlader zwei separate Einstellungen von Sperrbits, die unabhängig voneinander gesetzt werden können. Dies gibt dem Anwender eine einzigartige Flexibilität bei der Auswahl verschiedener Schutzstufen.
Technischer Hintergrund: Flash-Speicher, genauer: seine Adressierungseinheit, ist nicht in der Lage, beim Zugriff auf „beschäftigte“ (gerade beim Löschen oder Programmieren) Sektoren bspw. aus einem Cache zu lesen. Auch kann nur maximal ein Sektor „beschäftigt“ sein. Damit ist beim Programmieren der Speicher komplett mit sich beschäftigt. Als grober zeitlicher Richtwert gelten allgemein 10 ms. Während dieser Zeit sind keine Lesezugriffe möglich, und die CPU würde so keine Befehle lesen können. Verschiedene Mikrocontroller-Architekturen gehen verschiedene Wege, mit diesem Problem umzugehen. Häufig wird die CPU angehalten. Damit erscheint der Controller für kurze Zeit tot. Von-Neumann-Architekturen können (nur) Kode aus dem RAM ausführen. Beispielsweise ist das Verhalten undefiniert, wenn irrtümlich Flash-Speicher gelesen wird. Undefiniert heißt, dass von falschen Daten über Programmabsturz bis zum Chip-Abbrand alles drin ist. Die AVR-Lösung für größere Controller ist die Einteilung des Flash-Speichers in zwei hinreichend getrennte Bereiche, sodass einer beschäftigt sein kann und der andere (zum Befehlslesen) funktionstüchtig bleibt. Die Aufteilung und die Zugriffsmöglichkeiten ist dabei ziemlich asymmetrisch ausgelegt.Ob die CPU das Lesen während des Schreibens unterstützt oder ob die CPU angehalten wird, während die Urlader-Software updatet, ist davon abhängig, an welcher Adresse das Urlader-Programm steht. Genauso, wie der Flash wie oben beschrieben mit den BOOTSZ-Fuses in den Anwendngs- und Urlader-Bereich eingeteilt wird, wird der Flash auch in zwei feste Bereiche, nämlich den Lesen-beim-Schreiben- (RWW) und den Nicht-Lesen-beim-Schreiben-Bereich (NRWW) eingeteilt. Die Grenze zwischen dem RWW- und dem NRWW-Bereich ist der Tabelle 27-8 angegeben. Der Hauptunterschied zwischen den beiden Bereichen ist der folgende:
Welchen Bereich adressiert der Zeiger Z beim Programmieren? | Welcher Bereich kann gelesen werden? | Ist die CPU angehalten? | Gleichzeitig Lesen und Programmieren? |
---|---|---|---|
RWW-Bereich | NRWW-Bereich | Nein | Ja |
NRWW-Bereich | Keiner | Ja | Nein |
Der Anwender kann wählen:
BLB0-Modus | BLB02 | BLB01 | Schutzart |
---|---|---|---|
1 | 1 | 1 | Keine Einschränkung für SPM und LPM beim Zugriff auf den Anwender-Bereich |
2 | 1 | 0 | Schreibschutz: SPM darf nicht in den Anwender-Bereich schreiben |
3 | 0 | 0 | Vollschutz: SPM darf nicht in den Anwender-Bereich schreiben, und LPM darf nicht vom Urlader-Bereich aus den Anwender-Bereich lesen. Sofern Interruptvektoren im Urlader-Bereich platziert sind, sind Interrupts deaktiviert, wenn Kode aus dem Anwender-Bereich ausgeführt wird. |
4 | 0 | 1 | Kopierschutz: LPM darf nicht vom Urlader-Bereich aus den Anwender-Bereich lesen. Sofern Interruptvektoren im Urlader-Bereich platziert sind, sind Interrupts deaktiviert, wenn Kode aus dem Anwender-Bereich ausgeführt wird. |
BLB1-Modus | BLB12 | BLB11 | Schutzart |
---|---|---|---|
1 | 1 | 1 | Keine Einschränkung für SPM und LPM beim Zugriff auf den Urlader-Bereich |
2 | 1 | 0 | Schreibschutz: SPM darf nicht in den Urlader-Bereich schreiben |
3 | 0 | 0 | Vollschutz: SPM darf nicht in den Urlader-Bereich schreiben, und LPM darf nicht vom Anwender-Bereich aus den Urlader-Bereich lesen. Sofern Interruptvektoren im Anwender-Bereich platziert sind, sind Interrupts deaktiviert, wenn Kode aus dem Urlader-Bereich ausgeführt wird. |
4 | 0 | 1 | Kopierschutz: LPM darf nicht vom Anwender-Bereich aus den Urlader-Bereich lesen. Sofern Interruptvektoren im Anwender-Bereich platziert sind, sind Interrupts deaktiviert, wenn Kode aus dem Urlader-Bereich ausgeführt wird. |
BOOTRST | Reset-Adresse |
---|---|
1 | Reset-Vektor = Anwendungs-Reset (Adresse 0x0000) |
0 | Reset-Vektor = Urlader-Reset (Adresse siehe Tabelle 27-7) |
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
ZH(R31) | Z15 | Z14 | Z13 | Z12 | Z11 | Z10 | Z9 | Z8 |
---|---|---|---|---|---|---|---|---|
ZL(R30) | Z7 | Z6 | Z5 | Z4 | Z3 | Z2 | Z1 | Z0 |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Die einzige SPM-Operation, die nicht den Z-Zeiger verwendet, ist das Setzen von Sperrbits. In diesem Fall wird das Z-Registerpaar nicht verwendet. Für den LPM-Befehl wird Z (implizit oder explizit) zur Adressierung verwendet. Da dieser Befehl byteweise arbeitet, wird hierbei auch das LSB (Bit 0) von Z verwendet.
Variante 1: Füllen des Puffers vor dem Löschen:
Wenn der EEPROM mitten in einer Seitenlade-Operation beschrieben wird, gehen alle geladenen Daten verloren.
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
R0 | 1 | 1 | BLB12 | BLB11 | BLB02 | BLB01 | LB2 | LB1 |
---|
Werden die Bits 5:0 in R0 auf Null gesetzt, werden die entsprechenden Sperrbits gesetzt, wenn BLBSET und SPMEM im SPMCSR gesetzt wurden und innerhalb von vier Takten SPM ausgeführt wird. Der Z-Zeiger wird dabei nicht verwendet. Für künftige Kompatibilität sollte Z auf 0x0001 gesetzt werden, genauso wie zum Lesen der IOCK-Bits. Außerdem sind die Bits 7 und 6 in R0 wie oben angegeben auf „1“ gesetzt sein. Während des Programmierens der Sperrbits bleibt der gesamte Flash-Speicher ungesperrt zum Lesen.
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Rn | 1 | 1 | BLB12 | BLB11 | BLB02 | BLB01 | LB2 | LB1 |
---|
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Rn | CKDIV8 | CKOUT | SUT1 | SUT0 | CKSEL3 | CKSEL2 | CKSEL1 | CKSEL0 |
---|
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Rn | RSTDISBL | DWEN | SPIEN | WDTON | EESAVE | BODLEVEL2 BOOTSZ1 | BODLEVEL1 BOOTSZ0 | BODLEVEL0 BOOTRST | ATmega48/88/168 ATmega328 |
---|
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
Rn | - | - | - | - | - | BOOTSZ1 BODLEVEL2 | BOOTSZ0 BODLEVEL1 | BOOTRST BODLEVEL0 | ATmega48/88/168 ATmega328 |
---|
Signaturbyte | Adresse im Z-Zeiger |
---|---|
Signaturbyte 1 | 0x0000 |
Signaturbyte 2 | 0x0002 |
Signaturbyte 3 | 0x0004 |
Kalibrierbyte für RC-Oszillator | 0x0001 |
Ein falscher Flash-Speicherinhalt kann durch zwei Ursachen entstehen, in denen die Speisespannung zu gering ist. Erstens benötigt ein regulärer Schreibvorgang in den Flash eine Mindestspannung um korrekt zu arbeiten. Zweitens kann die CPU selbst Befehle falsch ausführen, wenn die Speisespannung zu gering ist.
Flash-Fehler können verhindert werden, wenn folgende Designregeln beachtet werden (eine genügt):
Aktion | Min. Programmierzeit | Max. Programmierzeit |
---|---|---|
Flash schreiben (Seite löschen, Seite schreiben, Sperrbits schreiben) | 3,7 ms | 4,5 ms |
;- Diese Routine schreibt eine Seite vom RAM zum Flash. ; Die erste RAM-Speicherzelle wird durch Y adressiert. ; Die erste Flash-Speicherzelle wird durch Z adressiert. ;- Fehlerbehandlung ist nicht dabei. ;- Verwendete Register: R0, R1, temp1 (R16), temp2 (R17), loop (R18), spmcrval (R19) ; Retten und Siederherstellen (Push+Pop) ist hier nicht eingebaut. ; Registerverbrauch kann gegen Kodegröße eingespart werden. ;- Es wird PAGESIZEB ≤ 256 für 8-Bit-Schleifenzähler vorausgesetzt. .equ PAGESIZEB = PAGESIZE*2 ;PAGESIZEB = Seitengröße in BYTE, nicht in WORD .org SMALLBOOTSTART Write_page: ; Seite löschen ldi spmcrval, (1<<PGERS) | (1<<SPMEN) rcall Do_spm ; RWW-Bereich wiederherstellen ldi spmcrval, (1<<RWWSRE) | (1<<SPMEN) rcall Do_spm ; Daten vom RAM zum Flash-Seitenpuffer schaufeln ldi loop, PAGESIZE ;Schleifenzähler initialisieren Wrloop: ld r0, Y+ ld r1, Y+ ldi spmcrval, (1<<SPMEN) rcall Do_spm subi ZL, -2 ;= +2, ohne Überlauf zum High-Teil dec loop brnz Wrloop ; execute Page Write subi ZL, PAGESIZEB ;Zeiger wiederherstellen ldi spmcrval, (1<<PGWRT) | (1<<SPMEN) rcall Do_spm ; re-enable the RWW section ldi spmcrval, (1<<RWWSRE) | (1<<SPMEN) rcall Do_spm ; read back and check, optional ldi loop, PAGESIZEB ;Schleifenzähler initialisieren subi YL, PAGESIZEB ;Zeiger wiederherstellen sbci YH, 0 Rdloop: lpm r0, Z+ ld r1, Y+ cpse r0, r1 rjmp Error ;Nicht gezeigte Fehlerbehandlung dec loop brnz Rdloop ; Rückkehr zum RWW-Bereich Return: ; Warten bis RWW-Bereich bereit zum Lesen ist in temp1, SPMCSR sbrs temp1, RWWSB ret ; RWW-Bereich wiederherstellen ldi spmcrval, (1<<RWWSRE) | (1<<SPMEN) rcall Do_spm rjmp Return Do_spm: ; Warten bis vorheriges SPM fertig Wait_spm: in temp1, SPMCSR sbrc temp1, SPMEN rjmp Wait_spm ; Warten bis evtl. EEPROM-Schreibzugriff fertig ist (Interrupts okay) Wait_ee: sbic EECR, EEPE rjmp Wait_ee ; Eingabe: spmcrval legt SPM-Aktion fest ; Interrupts sperren, falls freigegeben, Zustand retten in temp2, SREG cli ; Spezielle zeitabhängige SPM-Sequenz out SPMCSR, spmcrval spm ; SREG wiederherstellen (Interrupts freigeben wenn vorher freigegeben) out SREG, temp2 ret |
BOOTSZ1 | BOOTSZ0 | Boot Size | Seiten | Anwender-Bereich | Boot-Bereich | Ende des Anwender-Bereichs | Boot-Reset-Adresse (=Anfang Boot-Bereich) |
---|---|---|---|---|---|---|---|
1 | 1 | 128 Word | 4 | 0x000 - 0xF7F | 0xF80 - 0xFFF | 0xF7F | 0xF80 |
1 | 0 | 256 Word | 8 | 0x000 - 0xEFF | 0xF00 - 0xFFF | 0xEFF | 0xF00 |
0 | 1 | 512 Word | 16 | 0x000 - 0xDFF | 0xE00 - 0xFFF | 0xDFF | 0xE00 |
0 | 0 | 1024 Word | 32 | 0x000 - 0xBFF | 0xC00 - 0xFFF | 0xBFF | 0xC00 |
Bereich | Seiten | Adress-Bereich |
---|---|---|
Lesen-beim-Schreiben (Read-While-Write = RWW) | 96 | 0x000 - 0xBFF |
Nicht-lesen-beim-Schreiben (No Read-While-Write = NRWW) | 32 | 0xC00 - 0xFFF |
Man beachte, dass die Bereichsaufteilung nur mit der größten Bootbereichsgröße von 2 KByte korelliert.
Variable | Entsprechende Z-Bits(1) | Beschreibung | |
---|---|---|---|
PCMSB | 11 | Höchstwertiges Bit im Programmzähler. (Der Programmzähler ist 12 Bit breit = PC[11:0]) | |
PAGEMSB | 4 | Höchstwertiges Bit, welches Worte innerhalb einer Page adressiert. 32 Word per Seite erfordern 5 Bits des Programmzählers = PC[4:0]. | |
ZPCMSB | Z12 | Bit in Z-Register, welches auf PCMSB gemappt ist. Weil Z0 ungenutzt ist, ist ZPCMSB gleich PCMSB + 1. | |
ZPAGEMSB | Z5 | Bit in Z-Register, welches auf PAGEMSB gemappt ist. Weil Z0 ungenutzt ist, ist ZPAGEMSB gleich PAGEMSB + 1. | |
PCPAGE | PC[11:5] | Z12:Z6 | Programmzähler-Seitenadresse: Seitenauswahl, fürs Seiten-Löschen und Seiten-Schreiben |
PCWORD | PC[4:0] | Z5:Z1 | Programmzähler Word-Adresse: Word-Auswahl, um den Zwischenpuffer zu füllen; muss Null sein beim Seiten-Schreiben |
BOOTSZ1 | BOOTSZ0 | Boot Size | Seiten | Anwender-Bereich | Boot-Bereich | Ende des Anwender-Bereichs | Boot-Reset-Adresse (=Anfang Boot-Bereich) |
---|---|---|---|---|---|---|---|
1 | 1 | 128 Word | 2 | 0x0000 - 0x1F7F | 0x1F80 - 0x1FFF | 0x1F7F | 0x1F80 |
1 | 0 | 256 Word | 4 | 0x0000 - 0x1EFF | 0x1F00 - 0x1FFF | 0x1EFF | 0x1F00 |
0 | 1 | 512 Word | 8 | 0x0000 - 0x1DFF | 0x1E00 - 0x1FFF | 0x1DFF | 0x1E00 |
0 | 0 | 1024 Word | 16 | 0x0000 - 0x1BFF | 0x1C00 - 0x1FFF | 0x1BFF | 0x1C00 |
Bereich | Seiten | Adress-Bereich |
---|---|---|
Lesen-beim-Schreiben (Read-While-Write = RWW) | 112 | 0x0000 - 0x1BFF |
Nicht-lesen-beim-Schreiben (No Read-While-Write = NRWW) | 16 | 0x1C00 - 0x1FFF |
Man beachte, dass die Bereichsaufteilung nur mit der größten Bootbereichsgröße von 2 KByte korelliert.
Variable | Entsprechende Z-Bits(1) | Beschreibung | |
---|---|---|---|
PCMSB | 12 | Höchstwertiges Bit im Programmzähler. (Der Programmzähler ist 13 Bit breit = PC[12:0]) | |
PAGEMSB | 5 | Höchstwertiges Bit, welches Worte innerhalb einer Page adressiert. 64 Word per Seite erfordern 6 Bits des Programmzählers = PC[5:0]. | |
ZPCMSB | Z13 | Bit in Z-Register, welches auf PCMSB gemappt ist. Weil Z0 ungenutzt ist, ist ZPCMSB gleich PCMSB + 1. | |
ZPAGEMSB | Z6 | Bit in Z-Register, welches auf PAGEMSB gemappt ist. Weil Z0 ungenutzt ist, ist ZPAGEMSB gleich PAGEMSB + 1. | |
PCPAGE | PC[12:6] | Z13:Z7 | Programmzähler-Seitenadresse: Seitenauswahl, fürs Seiten-Löschen und Seiten-Schreiben |
PCWORD | PC[5:0] | Z6:Z1 | Programmzähler Word-Adresse: Word-Auswahl, um den Zwischenpuffer zu füllen; muss Null sein beim Seiten-Schreiben |
BOOTSZ1 | BOOTSZ0 | Boot Size | Seiten | Anwender-Bereich | Boot-Bereich | Ende des Anwender-Bereichs | Boot-Reset-Adresse (=Anfang Boot-Bereich) |
---|---|---|---|---|---|---|---|
1 | 1 | 256 Word | 4 | 0x0000 - 0x3EFF | 0x3F00 - 0x3FFF | 0x3EFF | 0x3F00 |
1 | 0 | 512 Word | 8 | 0x0000 - 0x3DFF | 0x3E00 - 0x3FFF | 0x3DFF | 0x3E00 |
0 | 1 | 1024 Word | 16 | 0x0000 - 0x3BFF | 0x3C00 - 0x3FFF | 0x3BFF | 0x3C00 |
0 | 0 | 2048 Word | 32 | 0x0000 - 0x37FF | 0x3800 - 0x3FFF | 0x37FF | 0x3800 |
Bereich | Seiten | Adress-Bereich |
---|---|---|
Lesen-beim-Schreiben (Read-While-Write = RWW) | 224 | 0x0000 - 0x37FF |
Nicht-lesen-beim-Schreiben (No Read-While-Write = NRWW) | 32 | 0x3800 - 0x3FFF |
Man beachte, dass die Bereichsaufteilung nur mit der größten Bootbereichsgröße von 4 KByte korelliert.
Variable | Entsprechende Z-Bits(1) | Beschreibung | |
---|---|---|---|
PCMSB | 13 | Höchstwertiges Bit im Programmzähler. (Der Programmzähler ist 14 Bit breit = PC[13:0]) | |
PAGEMSB | 5 | Höchstwertiges Bit, welches Worte innerhalb einer Page adressiert. 64 Word per Seite erfordern 6 Bits des Programmzählers = PC[5:0]. | |
ZPCMSB | Z14 | Bit in Z-Register, welches auf PCMSB gemappt ist. Weil Z0 ungenutzt ist, ist ZPCMSB gleich PCMSB + 1. | |
ZPAGEMSB | Z6 | Bit in Z-Register, welches auf PAGEMSB gemappt ist. Weil Z0 ungenutzt ist, ist ZPAGEMSB gleich PAGEMSB + 1. | |
PCPAGE | PC[13:6] | Z14:Z7 | Programmzähler-Seitenadresse: Seitenauswahl, fürs Seiten-Löschen und Seiten-Schreiben |
PCWORD | PC[5:0] | Z6:Z1 | Programmzähler Word-Adresse: Word-Auswahl, um den Zwischenpuffer zu füllen; muss Null sein beim Seiten-Schreiben |
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
(0x57) | SPMIE | RWWSB | SIGRD | RWWSRE | BLBSET | PGWRT | PGERS | SPMEN | SPMCSR |
---|---|---|---|---|---|---|---|---|---|
Zugriff | R/W | R | R/W | R/W | R/W | R/W | R/W | R/W | |
Startwert | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Ein LPM-Befehl innerhalb von drei Taktzyklen nach dem Setzen der Bits BLBSET und SPMEN liest entweder die Sperr- oder die Fuse-Bits (abhängig von Z0 des Z-Zeigers) in ein Zielregister. Siehe Lesen der Fuse- und Sperrbits.
Das Schreiben anderer Werte als 0b10001, 0b01001, 0b00101, 0b00011 oder 0b00001 in die unteren fünf Bits hat keine Auswirkungen.