Hallo!
Ich versuche mich aktuell daran auf einem RP2040 (Pi Pico) ein FW-Update
hinzubekommen.
Ziel ist es später einmal ein Firmwareupdate für Endanwener anzubieten,
und zwar ein FW-Update über die vorhandene Feldbusanbindung, also ohne
USB, SWD etc...
Ich hab erstmal ein "Proof-Of-Concept" aufgesetzt und die ganze
Feldbusgeschichte ausgeklammert.
In der Testumgebung wird ein UF2-File (identisch mit der
Updatemöglichkeit über den integrierten USB UF2 Bootloader) über
USB-Serial hochgeladen und im Flash (weit hinter dem Programmbereich +
0x100000) abgelegt.
Ein Vergleich des Flashinhaltes an XIP_BASE + 0x000000 (Flashadresse 0,
laufendes Programm über USB BL geladen) und XIP_BASE + 0x100000 (UF2
über Serial geladen) zeigt keine Unterschiede.
Jetzt gehts an Eingemachte. Ich kann ja nicht den Ast absägen, auf dem
ich sitze. Also wird die "UpdateFlash" Methode als
void __not_in_flash_func(UpdateFlash)() definiert und liegt daher im
RAM.
Das funktioniert auch, im MAP-File liest man 0x200000c0 (0x20000000 =
Start SRAM Adressbereich).
Dann rufe ich 2 Methoden aus dem SDK, und zwar flash_range_erase
(https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_flash/flash.c#L63)
und flash_range_reprogramm
(https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_flash/flash.c#L87).
Die ersten Versuche gingen schief und ich hab gecheckt dass zwar die
Funktionen im RAM liegen aber nicht alle von diesen Funktionen
aufgerufenen Funktionen.
Auch das Sektorweise kopieren war quatsch.
Also kopiere ich jetzt alles am Stück und zwar auch noch mit einer neuen
Funktion erase_and_program (im Endeffekt bei beiden SDK Funktionen
zusammengeführt).
Die Funktionen verwenden übrigend wieder unterlagerte Funktionen aus dem
Boot-ROM für die Flash-Operationen.
flash_flush_cache();// Note this is needed to remove CSn IO force as well as cache flushing
24
flash_enable_xip_via_boot2();
25
}
Und so wird das dann gerufen. Die Adressen hab ich mit einer
"Dry-Run"-Verion mal ausgegeben und da sieht alles gut aus:
flash_range_erase_and_program 0x0 0x10100000 0x11000
1
void__not_in_flash_func(UpdateFlash)()
2
{
3
noInterrupts();
4
rp2040.idleOtherCore();
5
watchdog_enable(10000,false);
6
7
uint32_tnew_binary_start_ram=m_firstTargetAdr+m_flashoffset;// in RAM address space, so XIP_BASE ist not substracted
Trotzdem haut es am Ende nicht hin. Nach dem Reset bleibt der UF-2
Bootloader stehen.
Einen Debugger hab ich aktuell nicht (ist unterwegs) und printf oder gar
gpio debugging funktioniert auch nicht, weil die methoden ja alle im
überschriebenen Flash liegen.
Jemand eine Idee?
Achja, auf einen "Second-Stage" BL wollte ich verzichten, um die
Komplexität zu reduzieren und das einfach adaptierbar zu machen.
Ich stehe demnächst vor einem ähnlichem Problem mit dem RP2040.
Was mir an Lösungen mit einem einzigen Image absolut nicht gefällt, ist
daß es im Fall von einem Fehler keinen Weg zurück gibt. Ich möchte daher
2 Images verwenden und dann wird zwischen denen umgeschaltet. Flash ist
dafür ja normal genug vorhanden.
Also genauer: ich teile den Flash auf in 3 Blöcke: Mini-Bootloader +
"Partitionstabelle", Image 1, Image2.
Beim Boot lädt der integrierte Bootloader immer meinen Mini-Bootloader.
Der entscheidet anhand der Metadaten in der "Partitionstabelle" und
anhand eines Tasters mit dem man im Notfall auf das vorige Image
zurückgehen kann, welches Image jetzt gestartet werden soll. Dann
startet er dies.
Innerhalb des Images gibt es dann eine Update-Funktion die ein neues
Image empfangen kann. Damit wird dann das aktuell nicht laufende Image
überschrieben. Wenn Prüfsumme etc. ok sind, werden als letztes die Daten
in der "Partitionstabelle" aktualisiert und ein Reboot ausgelöst.
Das einzige Problem an der Sache ist, daß der XIP-Block leider zu den
Flash-Adressen kein Offset addieren kann. Die Speicheradressen im Flash
sind also absolut und daher unterschiedlich, je nachdem ob ich gerade
Image 1 oder Image 2 verwende.
Ich werde daher wohl jede Version 2x bauen, mit 2 unterschiedlichen
Linker-Skripts mit den unterschiedlichen Flash-Adressen. Ein Update
enthält dann beide Images hintereinander und der Code zum Empfangen des
Updates schreibt nur den einen Teil davon der für die aktuell benötigte
Image-Adresse passt.
Ich mag gerade die Einfachheit der Lösung, dass eine Funktion im RAM das
aktuelle Program im Flash überschreibt.
Ich brauch keinen 2ten Bootloader, keine Tabelle, keine zwei
Linkerscripts... Einfach das UF2, das auch beim Laden mit dem UF2 USB
Bootlader geladen wird, und fertig.
Schief gehen kann immer was - jedoch wird es nur problematisch, wenn
etwas in der Funktion schief geht, die aus dem "hinteren" Flashbereich
dann auf 0x00 kopiert. Eventuelle Checks (Prüfsumme etc..) kann man
vorher machen.
Aber - der USB UF2 Bootloader ist ja als ROM fest eingebrannt. Der wird
immer verfügbar sein, egal was beim Update schief geht. Das reicht mir
als Fallback.
Ich konnte das Problem übrigens zwischenzeitlich lösen:
Die flash_range_ Funktionen sind zwar als __no_inline_not_in_flash
definiert, rufen jedoch Funktonen, die das nicht sind. Und dann kracht
es, weil an der Stelle im Flash dann ggf. was anderes steht... kriegt
man aber hin - hab nun eine eigene flash_range funktion die zu 100% im
Ram läuft.
@sirsydom
Danke für deine Infos.
Ich versuche ebenfalls ein "einfaches" OTA mit dem pico zu realisieren.
Kannst du Bitte deine fertigen Funktionen "flash_range_" nochmals
posten.
Am besten wäre natürlich alle beteiligten Funktionen (flash_range_,
flash_range_erase_and_program & UpdateFlash).
Du würdest mir dadurch sehr helfen.
Danke
Tobias
das SDK wurde in diesen beiden Funktoinen überarbeitet und
wahrscheinlich ist mein Workaround gar nicht nötig.
Hast du mit dem aktuellen SDK-Funktionen getestet?
Gerd E. schrieb:> Das einzige Problem an der Sache ist, daß der XIP-Block leider zu den> Flash-Adressen kein Offset addieren kann. Die Speicheradressen im Flash> sind also absolut und daher unterschiedlich, je nachdem ob ich gerade> Image 1 oder Image 2 verwende.
Schon länger her, aber es ist ein allgemeines Problem und Du bist hier
ja wohl noch aktiv.
Vielleicht wäre es eine Lösung, die Programme immer auf 0x20000100 zu
linken und im RAM laufen zu lassen: passender Bootloader vorausgesetzt.
Dann muß nur beim Erstellen der .uf2-Datei der Ablageort im FLASH
angepaßt werden.