Bootloader: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
Zeile 100: Zeile 100:
   // Rückkehr zur Adresse von CALL.
   // Rückkehr zur Adresse von CALL.
}
}
Achtung, falls innerhalb der Interrupt-Service-Routine in den Flash geschrieben wird, darf in dieser Routine nicht mit GIEH=... oder GIEL=... die Interrupt-Sperre freigegeben werden. Neue Interrupts sind in der ISR gesperrt und werden nachher automatisch entsperrt. Zusätzliche Freigabe führt zu Störung des Stacks für Funktionsrücksprünge.


==== In den Flash schreiben ====
==== In den Flash schreiben ====

Version vom 30. November 2014, 17:48 Uhr

Der ursprüngliche Weg, ein Programm in einen Microcomputer zu bringen, war, ein EPROM mit dem Programmcode zu brennen und es mit dem Bus des Controllers zu verbinden. Controller mit internem PROM, EPROM oder Flash-ROM besitzen meist eine dem EPROM ähnliche parallele Programmiermöglichkeit.

Moderne Controller besitzen einen Bootloader. Dies ist ein im Controller befindliches Programm, dessen Aufgabe es ist, das eigentliche Programm in den Speicher zu laden. Bootloader gibt es in vielfältiger Ausprägung. Zumeist ist es ein fest im Controller integriertes Programm wie beim C166 oder 68HC11. Dieses ermöglicht das Laden des Programms über die serielle Schnittstelle. Speziell bei diesen beiden Controllern muss sich der zu programmierende Speicher nicht im Controller selber befinden. Neu ist die Möglichkeit, auch den Bootloader im Flash selbst zu programmieren. Diese Möglichkeit bieten z. B. die ATmegas.

Beispiel

PIC18, C-Implementierung, Stichpunkte

Überblick

Für gewöhnlich wird das Binary (es kann einem Intelhexfile oder einem Motorola S-Recordfile entnommen werden) über eine physische Schnittstelle vom PC auf den µC übertragen. Als physische Schnittstelle kommen z.B. RS232 (alias UART, USART, EUSART auch COM), USB oder I2C in Betracht. Diese Schnittstellen sind als Module direkt auf dem µC-Chip implementiert. Das Flashtool (PIC-Programmer) wird nur zum Aufspielen des Bootloaders selbst verwendet. Das Nutzprogramm (Firmware) spielt später der Benutzer auf, der kein Flashtool besitzt. Bei der Übertragung wird das Nutzprogramm in kleinen Stücken übertragen, da es nicht als Ganzes in den RAM des µC passt. Auf dem µC wird es blockweise im Flash-Speicher abgespeichert. Grund ist, dass der Flash das Speichern nur blockweise, das Lesen aber byteweise ermöglicht. Der Bootloader sollte auch das Rücklesen des Flash-Inhaltes ermöglichen um Übertragungsfehler auszuschließen. Der Bootloader benötigt zwei Modi: 1) Programmieren, 2) Nutzprogramm. Zum Starten wird der Programmzähler auf den Start im Flash-Speicher gesetzt wo sich der Interrruptvektor befindet.

Literatur

  • [1] µC Handbuch eines spezifischen PIC18
  • [2] Application Note AN851 für Assembler

Übertragungsprotokoll

Der Nachteil einer C-Implementierung ist ihr hoher Flash-Speichererbrauch. Vorschlag: Für Kommandos nur ein ASCII-Zeichen verwenden. Das Nutzprogramm sollte als Roh-Bytes in 64-Byte-Blöcken übertragen werden, was der nutzbaren Flash-Block-Größe entspricht. Der halbe Block am Ende des Programmes wird vom PC mit 0xFF-Bytes aufgefüllt. Nach dem Senden von 64Byte Nutzdaten wartet der PC bis der Bootloader ein Antwortsbyte gesendet hat um sicherzustellen dass vorher der zeitverbrauchende Speichervorgang abgeschlossen ist.

Speicherorganisation

Beginnend auf Adresse 0x0000 des Flash befindet sich ein schützbarer Speicherbereich, in dem man den Bootloader ablegen kann. Er hat eine Größe von 500 Byte bis 2 kByte. Auf der Adresse 0x0000 befindet sich der Interruptvektor mit den Startadressen:

0x0000 _entry_scn ... Init des Programmes und Aufruf von main() aus der Datei c:\Programme\Microchip\...\c018i.c

0x0008 High priority Interrupt.

0x0018 Low priority Interrupt.

Zunächst befinden sich Bootloader und Nutzprogramm auf Adresse 0x0000. Das Nutzprogramm wird im Linkerfile (z.B. auf Adresse 0x0800) verschoben. Somit beginnt der Interruptvektor des Nutzprogrammes im Beispiel auf 0x0800. Der µC legt den Programmzähler bei jedem Interrupt auf 0x0000 und folgende nachdem er die Rücksprungadresse auf dem Hardwarestack abgelegt hat. Der Interruptvektor auf Adresse 0x0000 ist im µC fest verdrahtet und kann nicht überschrieben werden. 0x0800 ist für Interrupts im µC nicht vorgesehen. Somit müssen Interrupts von 0x0008 auf 0x0808 und von 0x0018 auf 0x0818 bei jedem Aufruf weitergeitet werden. Man spricht von Remapping. Einmalig bei Booten des µC muss entschieden werden, ob das Flash-Programm oder das Nutzprogramm ausgeführt wird. Das Flash-Programm kann defaultmäßig starten und beim Vorliegen eines Bootloader-Flags den Programcounter auf 0x0800 setzen.

Das Bootloader-Flag

Das Bootloader-Flag entscheidet ob das Flash-Programm oder das Nutzprogramm gestartet wird. Es wird während der Laufzeit des Bootloaders sowie zur Laufzeit des Nutzprogrammes dann abgefragt, wenn der Bootloader Interrupts benötigt und damit eine Interruptverzweigung notwendig wird. Datenübertragung über RS232 benötigt Interrupts, Datenübertragung über USB kommt mit Polling aus. Das Bootloaderflag ist im Code des Bootloaders definiert. Wenn es abgefragt wird, so wird in diesem Moment Bootloadercode ausgeführt.

Ein Bootloader-Flag im RAM muss mit Hilfe des Linkerfiles vor Zugriffen aus dem Nutzprogramm geschützt werden. Falls überhaupt möglich so muss man beachten, dass die beiden Linkerfiles von Flashprogramm und Nutzprogramm konkurierend Orte im RAM für globale Variablen definieren. Es werden auch Speicherorte für Variablen innerhalb von Funktionen dort definiert wobei diese Variablen nicht statich deklariert worden sein müssen. Weiter legt der Linker Speicherorte für Funktionsübergabeparameter im RAM fest.

Ein Bootloader-Flag im EEPROM kann auf einem definierten Speicherort abgelegt werden. Somit treten keine ungewollten Schreibvorgänge auf. Es kann bei jeder Interruptverzweigung gelesen werden, sollte nur beim Flashen geändert werden nicht aber bei jedem Booten. Beim Flashen des Bootloaders selbst wird der EEPROM mit dem Byte 0xff initialisiert. Dieses Zeichen sollte den Bootloadermodus darstellen. Ohne weitere Schutzmaßnahmen kann es vorkommen, dass das Nutzprogramm nicht in den Bootmodus springt weil es kaputt ist. Somit wäre der Bootloader ohne Flashtool nicht erreichbar. Um das zu vermeiden sollte das Nutzprogramm vor dem Rückschalten auf den Nutzermodus zur Kontrolle rückgelesen werden.

Weitere Möglichkeiten um die beiden Modi zu unterscheiden wären das Auswerten der Rücksprungadresse auf dem HW-Stack sowie ein HW-Bootloader-Jumper.

Die Interrupt-Weiterleitung mit Interruptverzweigung

Sprunganweisungen sind:

a) _asm goto INTHANDLER _endasm // Ohne Rücksprung.

b) _asm call INTHANDLER, 0 _endasm // Mit Rücksprung, entweder return oder RETFIE

Das Pragma

#pragma interrupt INTHANDLER

void INTHANLDER(void) {

   // Impl.

}

fügt zu der Funktion INTHANLDER zwei Dinge hinzu: Zum einen das Sichern eines Kontextes und zum anderen die Rückkehr mittels RETFIE, einem return das die während des Interrupts gesperrten Interruptsprünge wieder entsperrt. Während Interrupt-Ausführung sind neue Interrupts gesperrt um Störungen zu vermeiden. Diese Routine darf nicht mit "goto" sondern nur mit "call" verlassen werden. Sie darf nur einmal unmittelbar aus dem Interruptvektor aufgerufen werden: Es darf eine Routine mit diesem Pragma keine zweite Routine mit ebenfalls diesem Pragma aufrufen. Sonst würde die Interruptfreigabe vor dem Verwerfen des Kontextes der äußeren Routine erfolgen.

Somit sollte folgende Aufrufkette existieren (hier nur INTHIGH, das selbe wird für INTLOW benötigt):

// Bootloader

#pragma code INTVEC_HIGH=0x0008

void INTVEC_HIGH(void) {

 _asm goto INTHANDLER_HIGH _endasm

} ...

#pragma interrupt INTHANDLER_HIGH

void INTHANDLER_HIGH(void) {

 if(BOOTFLAG) // Vorsicht bei jeglichen Zwischenvariablen.
 {
   HandleRS232();
 }
 else
 {
   _asm call 0x0808, 0 _endasm  // Hier kein goto verwenden.
 }

} ...

// User

#pragma code INTVEC_HIGH_REMAP=0x0808

void INTVEC_HIGH_REMAP(void) {

 _asm goto INTHANDLER_HIGH_REMAP _endasm

} ...

// Kein #pragma interrupt, entfernen falls Nutzerprogramm hierher verschoben wurde.

void INTHANDLER_HIGH_REMAP(void) {

 // Hier Interruptbehandlung des Nutzerprogrammes.
 // Rückkehr zur Adresse von CALL.

}

Achtung, falls innerhalb der Interrupt-Service-Routine in den Flash geschrieben wird, darf in dieser Routine nicht mit GIEH=... oder GIEL=... die Interrupt-Sperre freigegeben werden. Neue Interrupts sind in der ISR gesperrt und werden nachher automatisch entsperrt. Zusätzliche Freigabe führt zu Störung des Stacks für Funktionsrücksprünge.

In den Flash schreiben

Es werden zunächst Bytes in 64 Zwischenspeicherplätze geschrieben, die sogenannten Holding-Register. Danach erfolgt die Übernahme in den Flash mittels einer Instruktion. Dabei hält der µC die Programmausführung an. Die Vorgehensweise ist mit Beispielen im µC-Handbuch beschrieben. Mir ist unklar, warum PREINCREMENT verwendet wird beim Beschreiben der Holdingregister, eigentlich sollte es POSTINCREMENT sein.

Siehe auch

Artikelsammlung

AVR Bootloader in C - eine einfache Anleitung

Forum

http://www.mikrocontroller.net/topic/132026#1196880

Weblinks

AVR

ARM

MSP430

Manche µC von TI aus der MSP430-er Familie haben einen sog. Boot Strap Loader. TI hat die Beschreibung des BSL und Beispielcode offen gelegt. Damit kann man eigene Bootloader schreiben.

PIC

R8C

Die R8C enthalten ab Werk bereits einen proprietären, unveränderlichen(?) Bootloader. Zur Flash-Programmierung mit Hilfe dieses Bootloaders gibt es folgende Referenzen:

ZNEO

Sonstige

  • Der U-Boot (Universal Bootloader): "The 'U-Boot' Universal Bootloader project provides firmware with full source code under GPL. Many CPU architectures are supported: PowerPC(MPC5xx, MPC8xx, MPC82xx, MPC7xx, MPC74xx, 4xx), ARM(ARM7, ARM9, StrongARM, Xscale), MIPS(4Kc,5Kc), x86, Blackfin." -- U-Boot Homepage