Forum: Mikrocontroller und Digitale Elektronik STM32 Custom Bootloader Update


von Jay K. (deeplyembedded)


Lesenswert?

Hallo zusammen,

ich möchte gerne einen Custom Bootloader für einen STM32F4 schreiben, 
der die Möglichkeit bietet, nicht nur den Flash-Bereich der 
Applikationssoftware, sondern auch sich selbst upzudaten.
Dabei ergibt sich folgende Fragestellung:

Der Bootloader liegt am Beginn des Flash bei 0x08000000. Dort befindet 
sich auch der Reset-Vektor mit Stackpointer und Einsprungadresse.
Wenn ich nun diesen Bereich updaten will, muss ich vom "alten" 
Bootloader ein Backup erstellen, so dass ich bei einem Power-Loss 
während der Reprogrammierung auf jeden Fall einen lauffähigen Bootloader 
habe.
Nun hab ich im Manual des Controllers aber keine Option-Bytes o.ä 
gefunden, um den Controller die Vektor-Tabelle an einem anderen Ort im 
Flash suchen zu lassen.

Lediglich über die Bootpins könnte man noch den RAM auswählen etc. Was 
ist aber, wenn ich mein Backup des Bootloaders im Flash-Sektor 1 (bei 
0x08004000) anlege? Gibts eine Möglichkeit, den Core dort nach der 
Vektor Tabelle suchen zu lassen?

Alternativ wäre, den Einsprung immer bei Sektor 0 (0x08000000) zu haben 
und dort nur ein "Minimal-Programm" zu haben, das entweder an die 
Original-Adresse des Bootloaders springt (Sektor 1, 0x08004000) oder ins 
Backup bei Sektor 2, 0x08008000.
Das heißt ich würde 3 Sektoren benötigen, wobei Sektor 0 über die 
gesamte Projektlaufzeit nie überschrieben würde und dementsprechend 
schlank gehalten werden soll, um möglichst keine Bugs zu enthalten.
Damit würde man leider einen Sektor quasi "verschenken".
Gibts eine bessere Möglichkeit?

Danke und Gruß

von Gerd E. (robberknight)


Lesenswert?

Wie häufig updatest Du Deinen Bootloader?

Meist doch eher selten bis nie, man möchte sich aber für den Fall der 
Fälle die Option offenhalten.

Daher folgender Vorschlag:

Du updatest vom Bootloader aus nur das normale Hauptprogramm.

Wenn Du einmal doch den Bootloader updaten willst, dann bringst Du ein 
spezielles Hauptprogramm raus, was nichts macht außer den Bootloader mit 
einer neuen Version zu überschreiben (natürlich nicht in der Schleife, 
sondern nur wenn der bestehende Bootloader ein alter ist).

Das spielst Du über den alten Bootloader ein und startest es. Danach 
kannst Du dann mit dem neuen Bootloader wieder ein richtiges 
Hauptprogramm einspielen.

von Jay K. (deeplyembedded)


Lesenswert?

Hallo Gerd,

danke für das Feedback.
Ja in der Regel wird man den Bootloader eher selten updaten. Falls dort 
aber doch mal was im Argen liegt, wäre es schon gut, ein Update fahren 
zu können. Besonders wenn ein Zugang per JTAG nicht möglich ist.

Deine Idee ist an sich nicht schlecht, jedoch sehe ich folgendes 
Problem:
Wenn das "spezielle Hauptprogramm" den Bootloader updated, muss es den 
entsprechenden Sektor erst löschen und dann reprogrammieren.
Im Fall eines Power-Loss zwischen Löschen und Reprogrammieren, wäre die 
originale Bootloader Section zerstört und damit auch die Vektor-Tabelle. 
Ich würde also nie mehr ins Hauptprogramm springen können, um das 
Bootloader-Update neu anzustoßen...oder hab ich da jetzt einen 
Denkfehler?

Danke und Gruß

von Oszifant (Gast)


Lesenswert?

Jay K. schrieb:
> Im Fall eines Power-Loss zwischen Löschen und Reprogrammieren,


Ja dann ists leider hinüber... wie wahrscheinlich ist denn ein Power 
Loss bei Deiner Anwendung?

Das schon genannte minimal Programm ist die einzige mir bekannte Lösung 
für sowas.

von holger (Gast)


Lesenswert?

>Wenn das "spezielle Hauptprogramm" den Bootloader updated, muss es den
>entsprechenden Sektor erst löschen und dann reprogrammieren.
>Im Fall eines Power-Loss zwischen Löschen und Reprogrammieren, wäre die
>originale Bootloader Section zerstört

Das Problem hast du immer wenn du ein Bootloaderupdate machen willst.
Mach halt zwei Bootloader rein. Einer davon darf halt NIE ein Update
bekommen.

von Jay K. (deeplyembedded)


Lesenswert?

holger schrieb:
>>Wenn das "spezielle Hauptprogramm" den Bootloader updated, muss es den
>>entsprechenden Sektor erst löschen und dann reprogrammieren.
>>Im Fall eines Power-Loss zwischen Löschen und Reprogrammieren, wäre die
>>originale Bootloader Section zerstört
>
> Das Problem hast du immer wenn du ein Bootloaderupdate machen willst.
> Mach halt zwei Bootloader rein. Einer davon darf halt NIE ein Update
> bekommen.

Das meinte ich ja mit "Minimalprogramm" an 0x08000000.

Naja "immer" hat man das Problem nicht. Ich kenne es von anderen 
Controllern, dass man z.B Option-Bytes im Flash hat, die man mit der 
Adresse der Vektor-Tabelle beschreiben kann.

Die Sequenz wäre dann:
1. Backup Bootloader von original Bereich A an Bereich B
2. Option-Byte für Vektor-Tabelle mit Adresse B beschreiben
3. Löschen des originalen Bootloader Bereichs A
4. Schreiben des neuen Bootloaders an Bereich A
5. Option-Byte für Vektor-Tabelle mit Adresse A beschreiben
6. Backup Bereich B löschen

Scheinbar sind die ARM Cores hier etwas starr und bieten solch eine 
Funktion nicht.
Dann bleibt wohl wirklich nur der Weg mit den drei Sektoren.

Gruß

von Oszifant (Gast)


Lesenswert?

Du könntest natürlich versuchen den internen Bootloader der sowieso fest 
im Speicher liegt zu verwenden sofern Du die passenden Schnittstellen 
rausgeführt hast und irgendwie den BOOT0 Pin setzen kannst...

von Dr. Sommer (Gast)


Lesenswert?

Das geht ganz einfach:
* Der Bootloader kopiert sich beim Starten in den RAM, z.B. an Adresse 
0x20000000. Da der ja üblicherweise nicht allzu groß sein sollte müsste 
das passen. Am Anfang steht ganz normal der ISR-Vektor.
* Der Bootloader setzt das VTOR (Vector Table Offset Register) auf 
0x20000000, sodass der ISR-Vektor von eben da auch gelesen wird.
* Der Bootloader empfängt das Hauptprogramm+neuer Bootloader über die 
gewünschte Schnittstelle und überschreibt den Flash. Das geht sogar 
asynchron, weil der Bootloader ja aus dem RAM läuft und 
Flash-Schreibzugriffe nicht das Programm blockieren.
* Der Bootloader setzt das VTOR wieder auf 0x08000000 und führt das 
Hauptprogramm aus, dessen Startadresse sich nach dem Flashen ja an 
0x08000004 befinden sollte.

Dazu muss der Bootloader nicht einmal am Anfang des Flashes stehen, 
sondern kann sich irgendwo im Hauptprogramm befinden. Man kann so auch 
den Bootloader einfach ins Hauptprogramm integrieren, und somit beides 
auf einmal Flashen. Man braucht keine Option Bytes o.ä. anzupassen, die 
ganze Magie macht das VTOR und die Tatsache, dass der ARM Code aus dem 
RAM ausführen kann.

von Gerd E. (robberknight)


Lesenswert?

Jay K. schrieb:
> Wenn das "spezielle Hauptprogramm" den Bootloader updated, muss es den
> entsprechenden Sektor erst löschen und dann reprogrammieren.
> Im Fall eines Power-Loss zwischen Löschen und Reprogrammieren, wäre die
> originale Bootloader Section zerstört und damit auch die Vektor-Tabelle.

Das stimmt natürlich.

Ein Bootloader ist meist von der Größe her überschaubar, vielleicht 2 
oder 4KB. Der neue Bootloader ist als Datenblock im speziellen 
Update-Hauptprogramm vorhanden, liegt also schon fertig auf dem µC vor 
und muss nicht mehr mühsam und langsam seriell oder sonstwie empfangen 
werden. Im Grunde müssen die Daten nur vom einen Flash-Sektor in den 
anderen kopiert werden. Das geht in diesem Fall wirklich fix, ne Sache 
von vielleicht 1-2 Sekunden äußerstes Maximum, eher < 1 Sek.

Du bist also in einem Zeitbereich bei dem Du die Stromversorgung noch 
ohne größeren Aufwand mit normalen Elkos puffern können solltest.

von W.S. (Gast)


Lesenswert?

Gerd E. schrieb:
> Du updatest vom Bootloader aus nur das normale Hauptprogramm.

Das wird alles nichts.
Grund:
Wenn man nicht eine hardwaremäßige Umschaltung zwischen einem Bootlader 
außerhalb des gewöhnlichen Flash-Bereiches hat, dann muß der Bootlader 
eben in den gewöhnlichen Flash-Bereich. ABER: Er muß ja als allererster 
beim Booten drankommen, weswegen er zumindest beim Cortex die ersten 
zwei Vektoren der Vektortafel benötigt. Diese lassen sich aber nicht 
speziell schützen, sondern werden eben immer zusammen mit dem ersten 
Flashblock gelöscht. Das eigentliche Nutz-Programm benötigt aber alle 
anderen Vektoren, weswegen selbige bei jedem Flashen neu aufgesetzt 
werden müssen. Obendrein braucht der Bootlader in solchem Falle die 
Werte die das Nutzprogramm für die ersten 2 Vektoren vorsieht, weswegen 
er sich selbige woanders merken muß.

Man könnte einen residenten Bootlader vorsehen, der niemals erneuert 
wird und der könnte dann alle Vektoren mit einem 
Assembler-Zwischenbrenner blind weiterreichen (sowas wie ADD PC,offset). 
Das verzögert die Interrupts etwas. Obendrein muß natürlich das 
Nutzprogramm komplett ab dem zweiten Flash-Sektor gelinkt sein.

Nun, dafür ist etwas Assemblerkenntnis nötig, ebenso ein passender 
Startupcode - bloß wer von den hier Mitlesenden kann das?

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> ABER: Er muß ja als allererster
> beim Booten drankommen,
Nö, wer sagt das?

W.S. schrieb:
> weswegen er zumindest beim Cortex die ersten
> zwei Vektoren der Vektortafel benötigt
Nein.

W.S. schrieb:
> weswegen selbige bei jedem Flashen neu aufgesetzt
> werden müssen.
Nö. Man kann den ISR-Vektor des Hauptprogramms sonstwohin in den Flash 
packen und das VTOR entsprechend setzen.

W.S. schrieb:
> Man könnte einen residenten Bootlader vorsehen, der niemals erneuert
> wird und der könnte dann alle Vektoren mit einem
> Assembler-Zwischenbrenner blind weiterreichen
Unnötig dank VTOR.

W.S. schrieb:
> Nun, dafür ist etwas Assemblerkenntnis nötig, ebenso ein passender
> Startupcode - bloß wer von den hier Mitlesenden kann das?
Du brauchst auch keine P*rnos, oder? Dir geht schon einer ab wenn du 
deinen eigenen Startupcode siehst, dein vermutlich einziges 
Assembler-Programm?

Und natürlich geht das auch komplett in C, da brauchts auf dem ARM 
keinen Assembler für.

von Jay K. (deeplyembedded)


Lesenswert?

Also ganz so düster wie W.S. sie beschreibt,ist die Situation ja nicht.
Bisschen Startup-Code schreiben incl Vektortabelle und das Ganze an die 
richtige Stelle linken ist kein Hexenwerk.

Nach dem Sprung aus dem Bootloader in die Anwendungssoftware einfach das 
VTOR Register umbiegen, so wie Dr. Sommer schon sagte,dann klappts auch 
mit den Interrupts und Exceptions ?

Weitaus interessanter wirds dann den Bootloader so zu 
compilieren/linken,dass er auch von der Backup Location bzw aus dem RAM 
laufen kann.

@ Dr. Sommer: Das Umbiegrn des VTOR Registers geht zwar im laufenden 
Betrieb, aber im Fall eines Powerfails und erneutem Hochlauf des 
Controllers hat dieses Register 0 als Initialwert -> Der Chip wäre 
gebrickt bei der von dir vorgeschlagenen Programmiersequenz

Gruß

von Dr. Sommer (Gast)


Lesenswert?

Jay K. schrieb:
> Der Chip wäre
> gebrickt bei der von dir vorgeschlagenen Programmiersequenz

In dieser Fassung, ja... Man könnte statt das Programm zu löschen das 
neue Programm inkl. Bootloader hinter das erste schreiben (braucht dann 
natürlich genug Flash Speicher), und ganz an den Anfang packt man einen 
Vor-Bootloader der herausfindet welches der beiden Programme aktueller 
(Versionsnummer) und intakt (CRC) ist.

von Jay K. (deeplyembedded)


Lesenswert?

Genau, so war auch mein Verständnis.
Den "Vor-Bootloader" schreibt man an Sektor 0,dieser wird nie mehr 
gelöscht und evaluiert lediglich ob der reguläre Bootloader-Bereich oder 
der Backup Bereich gültig sind und springt dann den eigentlichen 
Bootlaoder an. Dort kann man sich dann austoben mit 
Flash-Routinen,Signaturprüfungen und weis der Geier was.

Womit wir wieder bei der Ausgangssituation wären: Es werden für dieses 
Konstrukt drei Flash-Sektoren benötigt. Ein Mechanismus wie Option-Bytes 
oder Bootheader scheint es beim Stm32 nicht zu geben

von Dr. Sommer (Gast)


Lesenswert?

Jay K. schrieb:
> Ein Mechanismus wie Option-Bytes
> oder Bootheader scheint es beim Stm32 nicht zu geben

Richtig, wozu auch? Dank VTOR und RAM-Ausführung ist das unnötig.

von Jay K. (deeplyembedded)


Lesenswert?

Nun man würde sich dadurch einen Flash-Sektor sparen,da man den 
Vor-Bootloader für das Hochlaufen und die Verzweigung nicht mehr 
braucht. Der Bootloader könnte dann an Hand des PC selbst evaluieren ob 
er gerade von der Backup-Section läuft oder nicht.

von W.S. (Gast)


Lesenswert?

Jay K. schrieb:
> Nach dem Sprung aus dem Bootloader in die Anwendungssoftware einfach das
> VTOR Register umbiegen, so wie Dr. Sommer schon sagte,dann klappts auch
> mit den Interrupts und Exceptions ?

Also da ist etwas Vorsicht geboten: soweit ich mich erinnere, haben nur 
Cortex M3, M4 und aufwärts sowas. Eventuell der M0+ aber m.W. nicht der 
M0. Sowas geht also wirklich nur bei ausgesuchten Typen.


> Weitaus interessanter wirds dann den Bootloader so zu
> compilieren/linken,dass er auch von der Backup Location bzw aus dem RAM
> laufen kann.

Na das ist kein Hexenwerk. Ich habe sowas seit Jahren für andere 
Architekturen (Fujitsu und NEC) am Laufen. Man linkt seinen Bootlader 
auf den RAM, gibt ihn als binär aus und macht dann irgendwie (skript 
o.ä.) daraus eine C-Quelle mit nem const byte lader[..] = {.....

Den ganzen Block  muß man dann bloß noch in den RAM hieven.

Aber wenn ich lese, wie vehement hier einige von Startupcodes in C 
schwärmen und nicht kapieren, daß der Bootlader (präziser: der 
Start-Teil des Bootladers) immer als allererster nach dem Reset 
drankommen muß, dann hab ich Zweifel, ob aus solchem Unterfangen was 
werden kann.

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Also da ist etwas Vorsicht geboten: soweit ich mich erinnere, haben nur
> Cortex M3, M4 und aufwärts sowas.
Richtig. Manche der ST Cortex-M0 haben dafür aber einen "Patch". Der OP 
hat aber einen Cortex-M4.

W.S. schrieb:
> Na das ist kein Hexenwerk. Ich habe sowas seit Jahren für andere
> Architekturen (Fujitsu und NEC) am Laufen. Man linkt seinen Bootlader
> auf den RAM, gibt ihn als binär aus und macht dann irgendwie (skript
> o.ä.) daraus eine C-Quelle mit nem const byte lader[..] = {.....
Viel zu kompliziert... Man linkt den Bootloader so dass er RAM-Adressen 
verwendet, packt ihn aber in den Flash, im Endeffekt genauso wie globale 
Variablen. Durch das geschickte Definieren von Linker-Symbolen braucht 
man den Code nur noch per memcpy in den RAM zu kopieren.

W.S. schrieb:
> Aber wenn ich lese, wie vehement hier einige von Startupcodes in C
> schwärmen
Vielleicht solltest du mal über den Tellerrand schauen. Es ist auf 
ARMv7M überhaupt kein Problem, den Startupcode in C zu schreiben. Ist im 
Endeffekt ja nur ein memcpy und evtl. ein paar Zuweisungen und 
Funktions-Aufrufe. Du bist derjenige, der immer von seinem eigenen 
(Assembler-)Startupcode schwärmt!

W.S. schrieb:
> und nicht kapieren, daß der Bootlader (präziser: der
> Start-Teil des Bootladers) immer als allererster nach dem Reset
> drankommen muß,
Wieso denkst du dass das so muss? Ich habe einen Bootloader für STM32F4 
und CAN geschrieben, der Teil des Hauptprogramms ist, nicht am Anfang 
des Flashes steht, und erst dann gestartet wird wenn eine entsprechende 
CAN-Nachricht ankommt. Ist überhaupt kein Problem. Das "muss" nur auf 
dem AVR so.

W.S. schrieb:
> dann hab ich Zweifel, ob aus solchem Unterfangen was
> werden kann.
Wenn du statt Rumlästern und Selbst-Beweihräuchern hilfreiche Antworten 
geben würdest, stünden die Chancen besser.

von Steffen R. (steffen_rose)


Lesenswert?

Dr. Sommer schrieb:
> W.S. schrieb:
>> und nicht kapieren, daß der Bootlader (präziser: der
>> Start-Teil des Bootladers) immer als allererster nach dem Reset
>> drankommen muß,
> Wieso denkst du dass das so muss? Ich habe einen Bootloader für STM32F4
> und CAN geschrieben, der Teil des Hauptprogramms ist, nicht am Anfang
> des Flashes steht, und erst dann gestartet wird wenn eine entsprechende
> CAN-Nachricht ankommt. Ist überhaupt kein Problem. Das "muss" nur auf
> dem AVR so.

Ja, in deiner Begriffswelt ist es der Vor-Bootloader, der als erstes 
angesprungen wird. Auf jedenfall nicht die Applikation. Die ist ja ggf. 
gelöscht. Und bei einem Power Loss auch der Teil im RAM.

Bei mir heißt das erste Programm Bootloader und der Teil, der das Update 
durchführt Updater. Ich finde es etwas irreführend letzteres mit 
"Boot.." zu bezeichnen. Scheint aber nicht nur mir so zu gehen.

Und warum benötigt ein Bootloader ein Update? Ein Teil der Gründe sollte 
genausogut für den Vor-Bootloader zutreffen. Und man ist wieder beim 
ursprünglichen Problem.

von Jay K. (deeplyembedded)


Lesenswert?

Steffen R. schrieb:
> Und warum benötigt ein Bootloader ein Update? Ein Teil der Gründe sollte
> genausogut für den Vor-Bootloader zutreffen. Und man ist wieder beim
> ursprünglichen Problem.

Naja das seh ich nicht so.
Die Aufteilung in Vor-Bootloader und Bootloader (oder Updater) macht 
schon Sinn:
Man kann den Vor-Bootloader sehr schlank halten. Im Extremfall überprüft 
der nur, in welchem Block ein gültiger Updater liegt und springt diesen 
dann an.
Im Updater kann man dann sämtliche Schweinerein treiben und es ist nicht 
so schlimm, wenn man mal nen Bock schießt und nen kritischen Bug 
einbaut.
Besonders zum tragen kommt das, wenn der JTAG Port deaktiviert wird und 
Updates generell ausschließlich über den Bootloader möglich sind

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.