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
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.
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.
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.
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.
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?
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.
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.
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.
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.
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.
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... :)
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.pdfSTM32 Bootloader USB DFU Commands:
https://www.st.com/resource/en/application_note/cd00264379.pdf
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.
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.
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.
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.