Forum: Mikrocontroller und Digitale Elektronik STM32 - Bootloader (A/B) - Wie Flash Startadresse dynamisch halten?


von Thomas K. (thomas47058)


Lesenswert?

Hallo zusammen,

ich beschäftige mich damit einen Bootloader mit einem STM32L4 zu 
programmieren. Der Speichebereich ist wie folgt aufgebaut: 0x800000 
Bootloader, ab 0x800600 Firmware A, ab 0x8040000 Firmware B. Es soll in 
Zukunft mögliche sein, eine bereits bestehende Firmware inaktiv zu 
setzen, und dafür eine neue im zweiten Speicherbereich zu flashen. 
Sollten Bugs auftreten, gibt es ein Fallback auf die frühere Version.

Ich weiß, wie man eine Firmware per LinkerScript so einrichten muss, 
dass sie von einem festgelegten Speicherbereich aus angsprungen werden 
kann:
1
MEMORY
2
{
3
RAM (xrw)      : ORIGIN = 0x20000100, LENGTH = 96K - 256
4
RAM2 (xrw)      : ORIGIN = 0x10000000, LENGTH = 32K
5
FLASH (rx)      : ORIGIN = 0x8006000, LENGTH = 1024K - 0x6000
6
}

Aber genau das will ich vermeiden. Um konkreter zu werden: Die Firmware 
selbst soll nicht wissen (müssen), wo sie später lebt, in welchem 
Speicherbereich (A oder B), ich möchte also absolute Adressen (siehe 
oben) vermeiden, damit eine neue Firmware entweder im Bereich A oder B 
geflasht werden kann. Das soll der Bootloader später entscheiden, durch 
einen separaten bereich, wo Informationen über Versionen gespeichert 
werden (auch schon implementiert).

Was mich wundert: Ich flashe den Bootloader bei FLASH_BASE (0x800000), 
danach flashe ich "per Hand" die Firmware A ab Bereich 0x8006000. 
Anwelcher Stelle macht meine Firmware von der Info aus dem Linker Script 
gebraucht? Das müsste ich abfangen...

Hat jemand eine Idee dazu? Habe ich etwas vergessen zu erwähnen? Über 
Gedankenanstöße wäre ich sehr sehr dankbar!

Viele Grüße,
Thomas

von Dr. Sommer (Gast)


Lesenswert?

Thomas K. schrieb:
> danach flashe ich "per Hand" die Firmware A ab Bereich 0x8006000.
> Anwelcher Stelle macht meine Firmware von der Info aus dem Linker Script
> gebraucht? Das müsste ich abfangen...

Der Compiler/Linker kodiert die Adressen fix in den Maschinencode 
hinein. Mit -fPIC wird eine relative Adressierung benutzt, welche aber 
den Code ineffizienter macht und eine globale Datenstruktur mit Adressen 
erfordert. Außerdem hast du das Problem, dass ja die RAM-Adressen fix 
bleiben sollen, das musst du da einbauen.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Beim kompilieren brauch die Software schon die absoluten Adressen für zB 
Funktionsaufrufe.

Disasembliere doch mal etwas von deinem Code und dann wirstes sehen.

Was du brauchst ist "position independend code".
Das macht man im embeddet Bereich aber eher selten.

Zudem müsstest du dir deine IVT zur Laufzeit des Programs aufbauen, weil 
die NVIC IRQ Hardware absolute Adressen braucht.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Ich hatte mal den Code mit 2 unterschiedlichen Linkerscripten übersetzt, 
dann gibt es 2 Hex Dateien, je Speicherbereich eine.
Die Flasher Routine muss dann so klug sein und die richtig Datei 
auswählen.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Die Methode die ich jedoch bevorzugen würde ist ein externes Data Flash 
mit einigen MB. Da kann man sich gemütlich Backups erstellen und neues 
Flaschen. Der Bootloader braucht dann für den FW Update nur noch den 
Data Flash lesen und das Flash schreiben.

von Thomas K. (thomas47058)


Lesenswert?

Hallo zusammen,

vielen Dank für die wirklich zügigen Rückmeldungen. Ich dachte mir 
schon, dass das nicht einfach wird. Mir ist währenddessen eine Idee 
gekommen:

Der Bootloader müsste einige Funktionen übernehmen, die ich eigentlich 
für die Firmware vorgesehen hatte (Konnektivität zum Internet, 
Flashoperationen, etc.), aber gut: Die aktive Firmware läuft immer an 
der gleichen Stelle, z.B. Bereich A (immer die gleiche Adresse im Linker 
Script). Kommt nun ein Update, so wird vom Bootloader die aktive 
Firmware in den Bereich B kopiert, als inaktiv markiert, dann wird die 
neue Firmware in den Bereich A heruntergeladen, gespeichert, als aktiv 
markiert und gestartet. Falls das nicht klappt, wird der Vorgang 
rückkängig gemacht: Bereich A löschen, FW aus Bereich B wieder zu A 
kopieren.

Sollte gehen, oder? Erkennbare Nachteile?

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Sowas kann der L4 eigentlich schon in Hardware.
Guck dir mal das Flash banking an.

https://www.st.com/resource/en/application_note/dm00230416.pdf

von Dr. Sommer (Gast)


Lesenswert?

Thomas K. schrieb:
> Sollte gehen, oder? Erkennbare Nachteile?

Zu umständlich. Speichere im Firmware-Image die Version und eine 
Prüfsumme. Der Bootloader überschreibt immer das image mit der älteren 
Version, und startet immer das intakte (->Prüfsumme) mit der höchsten 
Version.

von Thomas K. (thomas47058)


Lesenswert?

Dr. Sommer schrieb:
> Zu umständlich. Speichere im Firmware-Image die Version und eine
> Prüfsumme. Der Bootloader überschreibt immer das image mit der älteren
> Version, und startet immer das intakte (->Prüfsumme) mit der höchsten
> Version.

Genau so hatte ich es ursprünglich vor, aber das geht ja nicht, bzw. nur 
mit viel Aufwand. Das neuer Image weiß dann nicht, wo es im 
Flashspeicher landet. Genau das muss ich aber im Linkerskript mitgeben.

Mw E. schrieb:
> Sowas kann der L4 eigentlich schon in Hardware.

Danke für den Hinweis. Das sehe ich mir morgen mal genauer an.

von Jim M. (turboj)


Lesenswert?

Thomas K. schrieb:
> Um konkreter zu werden: Die Firmware
> selbst soll nicht wissen (müssen), wo sie später lebt, in welchem
> Speicherbereich (A oder B), ich möchte also absolute Adressen (siehe
> oben) vermeiden

Wird mindestens aufwändig, denn die mitgelieferten C-Libs gängiger 
Compiler sind AFAIK ohne Support für PIE (position independent 
executable) gebaut.

Man muss also mindestens die LibC (newlib) selber bauen, und muss sich 
selber um relocation beim Startup kümmern.


Falls man genug SRAM hat: Lass den Bootloader den Code komplett ins RAM 
laden und von dort ausführen. So sind die Addressen konstant.

von Jim M. (turboj)


Lesenswert?

Andere Alternative: Man linkt die Firmware 2x, einmal für Addressbereich 
A und einmal für Addressbereich B. Braucht eigentlich nur 2 Linker 
Skripte.

Der Bootloader entscheidet dann ob Firmware A oder Firmware B geflasht 
werden soll.

Custom Bootloader baucht der OP ja sowieso schon.

von Gerd E. (robberknight)


Lesenswert?

Ich finde die Variante mit einem externen SPI-NOR-Flash und einem 
Bootloader, der dann das passende Programm in den Flash des µCs flasht 
eleganter:

- Programm muss nur 1x und ohne irgendwelche Besonderheiten compiliert 
und gelinkt werden

- Fast die volle Flash-Größe des µCs steht zur Verfügung (nur der 
Bootloader kommt hinzu)

- Man kann leicht mehr als 2 verschiedene Programmsektionen im externen 
Flash ablegen: Ursprüngliches Basisprogramm, bei dem zumindest der 
Empfang neuer Programmsektionen gesichert funktioniert, Programm A, 
Programm B. Wenn ich jetzt ein neues Programm empfange, kann ich einen 
der Programmplätze überschreiben. Sollte das aktuell laufende Programm 
jetzt durch einen Bug z.B. Probleme mit dem Empfang neuer Firmware 
haben, kann ich immer noch auf die ursprüngliche Version zurück.

- Man braucht natürlich das externe Flash zusätzlich. Das kostet aber 
keine 30 Cent. Das kann schnell billiger werden als einen STM32 mit 
größerem internem Flash zu nehmen.

von Thomas K. (thomas47058)


Lesenswert?

Danke nochmal für die tollen Tipps. Der L4 unterstützt einen Dual Bank 
Mode. Das umgeht einen Bootloader komplett, was ich nicht so prickelnd 
finde. Der Bootloader sollte auch ein Interface für die Außenwelt sein. 
Man sollte also auch von außen (über Python oder C++ GUI <-> UART via 
virtCOMPort/USB <-> uC) flashen können inkl. einiger Informationen über 
Versionsnummern und Zustand der FW und ohne unbedingt einen ST-Link 
Programmer zur Hand haben zu müssen.

@Gerd, Die Sache mit dem externen Flash ist wirklich eine Überlegung 
wert... Linker Script müsste nur einmalig angepasst werden auf eine 
Positionen im uC Flash. Bleibt noch der Platzverbrauch auf der PCB

Jim M. schrieb:
> Man muss also mindestens die LibC (newlib) selber bauen, und muss sich
> selber um relocation beim Startup kümmern.

DAS übersteigt dann doch meine Kenntnisse... :)

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Ich wüsste jetzt nicht wieso dualbank deinen Bootloader umgeht.
Der muss eben auf beiden Bänken sein und schreibt dann dein Program in 
den Flashspeicherbereich dahinter.
(also bis hierher keine Änderung zum eigentlichen Vorhaben)
Danach schaltet er die Bank um (im SYSCFG Register).
Stellt "der andere" Bootloader fest, dass sein Programm Murks ist so 
schaltet er die Bank zurück.

Selbst wenn, die STM32 haben ein eingebauten Bootrom mit Bootloader.
Dann muss zwar nicht per SWD geupdatet werden.
Der kann aber trotzdem weniger als dein Bootloader.
(Version deiner FW auslesen würde gehen indem du die an eine feste 
FlashAdresse packst und mit "Read Memory" liesßt*)

*muss dann aber im unprotectet Flash liegen

STM32 Bootloader UART Commands:
https://www.st.com/resource/en/application_note/cd00264342.pdf

STM32 Bootloader USB DFU Commands:
https://www.st.com/resource/en/application_note/cd00264379.pdf

von W.S. (Gast)


Lesenswert?

Thomas K. schrieb:
> ich beschäftige mich damit einen Bootloader mit einem STM32L4 zu
> programmieren.

Das mag zwar ne nette Beschäftigung sein, aber sinnvoll ist sie nicht.

Mal abgesehen davon, daß die STM32 bereits einen eigenen fest 
installierbaren Bootlader haben, wo du bloß ein passendes Gegenstück für 
den PC schreiben müßtest (oder ein fertiges solches benutzen müßtest) - 
solltest du in ganz anderer Richtung denken: Zerteilen deiner Firmware 
in eine Art Betriebssystem und eine oder mehrere Applikationen.

Eine davon könnte sowas sein wie ein spezieller Programmlader. Die 
übrigen Applikationen müßtest du dann aber positionsunabhängig 
übersetzen. Das dürfte dann auch nicht so viel ausmachen, weil du ja 
alle grundlegenden Treiber und Middleware-Komponenten ins Betriebssystem 
verlagert hast und die Apps nur den blanken Hi-Level-Anwendungscode 
beisteuern.

W.S.

von Jim M. (turboj)


Lesenswert?

W.S. schrieb:
> Das dürfte dann auch nicht so viel ausmachen, weil du ja
> alle grundlegenden Treiber und Middleware-Komponenten ins Betriebssystem
> verlagert hast und die Apps nur den blanken Hi-Level-Anwendungscode
> beisteuern.

Blöd nur das ucLinux reichlich externes SRAM oder PSRAM braucht (ein 
paar MB).

Was anderes gibt es für Cortex-M3 oder Cortex-M4 meiner Meinung nach 
nicht, wenn Anwendungen als PIE laufen sollen.

von Gerd E. (robberknight)


Lesenswert?

W.S. schrieb:
> solltest du in ganz anderer Richtung denken: Zerteilen deiner Firmware
> in eine Art Betriebssystem und eine oder mehrere Applikationen.
>
> Eine davon könnte sowas sein wie ein spezieller Programmlader. Die
> übrigen Applikationen müßtest du dann aber positionsunabhängig
> übersetzen. Das dürfte dann auch nicht so viel ausmachen, weil du ja
> alle grundlegenden Treiber und Middleware-Komponenten ins Betriebssystem
> verlagert hast und die Apps nur den blanken Hi-Level-Anwendungscode
> beisteuern.

Das halte ich bei einem µC nicht für sinnvoll. Sagen wir mal Du findest 
einen Bug in dem "Betriebssystem"-Teil und Du willst den jetzt durch 
eine gefixte Version ersetzen. Wie willst Du das machen wenn die 
"Programmlader"-Applikation läuft und den "Betriebssystem"-Teil 
verwendet? Du müsstest dann ein Page Erase für den Speicherbereich des 
laufenden "Betriebssystems" aufrufen, das wird nicht funktionieren.

Ein eigenständiger Bootloader, der nicht irgendwie mit dem Rest der 
Firmware verkuppelt ist, macht also ganz klar Sinn.

Und die von ST mit ausgelieferten Bootloader sind ja schön und gut, 
haben aber auch ganz klar ihre Grenzen. Sowas wie signierte 
Firmwareimages oder A/B-Images, wie es der TO haben möchte, können die 
einfach nicht. Außerdem sind die auf einige wenige 
Übertragungsprotokolle beschränkt, z.B. Ethernet mit TCP/IP, CAN mit 
ISO-TP, RS485,... können die nicht.

von W.S. (Gast)


Lesenswert?

Jim M. schrieb:
> Blöd nur das ucLinux reichlich externes SRAM oder PSRAM braucht (ein
> paar MB).

Noch blöder, daß du nur an sowas wie ucLinux denken kannst.

Ich hatte sowas bereits vor langer Zeit bei der Lernbetty vorgeturnt: 
Eine Art Betriebssystem als Grundlage und ladbare Applikationen dazu.

Das eigentliche Betriebssystem war ziemlich winzig, es kam über die 32K 
Grenze nur dadurch, daß es einige relativ dicke Grafik-Fonts enthielt. 
Aber auch die hätte man schlußendlich abtrennen können. Was nicht dabei 
war, war ein Relokator/Binder, der sowas wie .elf auf konkrete Adressen 
bindet.

Gerd E. schrieb:
> Sagen wir mal Du findest einen Bug in dem "Betriebssystem"-Teil

Dann wäre das Flashen des "Betriebssystem"-Teiles über den eingebauten 
Bootlader fällig. Aber da dieser Teil ja keine anwendungsspezifischen 
Algorithmen enthält, sollte er ab Version 1.0 fehlerfrei machbar sein. 
Obendrein kann er sogar offengelegt werden, da er ja das eigentliche 
KnowHow garnicht enthält.

Meine Erfahrung ist jedenfalls, daß Änderungen in fertigen Geräten NICHT 
wegen etwaiger Bugs, sondern wegen geänderter Ansprüche der Kunden oder 
geänderter Rechtsgrundlagen nötig werden.

W.S.

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.