srecord und Firmware-Bin’s

Zuletzt hatte ich folgende Aufgabenstellung zu bewältigen:

Eine Firmware sollte eine fortlaufende ansteigende Variable pro geflashten Mikrocontroller enthalten. Also eine sogenannte UID. Es durfte aber nicht irgendeine sein, sondern diese musste zwingend bei 0 beginnen und ca. 100 PCBs würden mir reichen. Irgendwelche internen UIDs des Mikrocontrollers konnte ich demnach nicht verwenden.

Es gibt dafür einen Guten und ein paar weniger gute Ansätze.

Jetzt könnte man meinen für 100 PCBs könnte ich die Variable auch manuell erhöhen und immer wieder neu kompilieren. Problematisch wird es nur, wenn eine neue Firmwareversion erstellt werden muss, weil in der Alten Bugs enthalten waren oder weitere Optimierungen hinzugekommen sind. Dann geht das Spielchen wieder von vorn los.

Also lieber gleich den richtigen Weg gehen. Mit dem Tool srec für Windows und Unix kann eine (nahezu) beliebige binäre Firmwaredatei verändert werden. Das gebräuchlichste Format ist wohl Intel-Hex. Da ich srec am Beispiel von Atmel Studio zeigen möchte, ist dies unser Zielformat. Es wird aber ebenso bei ARM GCC und allen Arduino’s eingesetzt.

Vorbereitungen im Atmel Studio

Um eine Variable gezielt in der Binärdatei zu finden und zu verändern, sollte diese natürlich immer an der selben Speicherstelle stehen. Da Compiler und Linker das ohne Vorgaben für sich entscheiden, sollte dies natürlich angepasst werden.

Die Variable muss beim gcc mit dem attribute section deklariert werden. Die Sektion kann frei gewählt werden. z.B.

const uint8_t my_id __attribute__ ((section (".my_segment"))) = 0xFA;

Ich habe als Wert absichtlich 0xFA gewählt, so ist der Wiedererkennungswert höher als mit 0x00 oder 0xFF.

Nachdem die Variable im Quellcode deklariert ist, muss dem Linker gesagt werden, welche Sektion adressiert werden soll. Da man sich beim Atmel Studio nicht mit einem Linkerscript herumschlagen muss, gibt es unter Properties-> Toolchain -> AVR/GNU Linker -> Memory Settings die passenden Optionen. Bei „FLASH segment“ kann ein neuer Eintrag vorgenommen werden. Für einen ATTINY startet der Flash bei 0x0 und wird Wortweise adressiert. Die Variable an die letzte verfügbare Flashadresse zu legen, macht am meisten Sinn. Bei einem 4 KB ATTINY45 sieht die Berechnung wie folgt aus:

4 KB = 0xFFF -> Wortadressiert -> 0xFFF / 2 = 0x7FF

Wir fügen also folgenden Eintrag hinzu:

.my_segment=0x0007FF

Das Projekt kann jetzt kompiliert und gelinkt werden.

srec_cat anwenden

Download und ausführlichste Informationen gibt es hier: http://srecord.sourceforge.net/

Trotz der ausführlich scheinenden Doku ist das Tool so umfangreich, dass ich nach einigen Funktionen lange suchen musst und mich dann mehr oder weniger mit Try-und Error durchgeschlagen habe.

Als erstes wäre es schön zu sehen, ob ein 0xFA an der erhofften Speicherstelle liegt. Sollte man dies nicht direkt aus dem Intel-Hex ablesen wollen, kann auch eine Binärdatei aus der Intel-Hex-Datei ableitet und sich im Hexeditor ansehen werden.

srec_cat firmware.hex -intel -o firmware_binary.bin -binary

Weiterhin kann die Größe der erzeugten Datei als weitere Verifikation benutzt werden. Da an die letzte Flash-Adresse geschrieben wurde, bei einem 4 KB Flash, muss die Datei nun genau 4 KB – 1 Byte groß sein, da nur 8 Bit der 16 Bit großen Speicherzelle verwendet wurden! Der Aufruf ist an dieser Stelle noch recht simpel. Die einzulesende Datei wird als Intel-hex bekannt gemacht und die Ausgabedatei wird binär erwünscht.

Nun soll das 0xFA überschrieben werden. Dazu wird der Parameter -generate verwendet. Dieser gibt erstellt mit -constan-l-e eine gewünschte Konstante an der richtigen Flash-Adresse. Achtung: Die Adresse wird nun wieder Byte-Adressiert eingegeben.

Wir versuchen einen Trockenlauf:

srec_cat -generate 0x0FFE 0x0FFF -constan-l-e 7 1 firmware.hex -intel -o output.hex -intel

Der Befehl schlägt fehl, weil die Adresse an der die 1 Byte große Variable „7“ geschrieben werden soll schon belegt ist.

srec_cat: firmware.hex: 184: multiple 0x00000FFE values (previous =
 0x07, this one = 0xFA)

Wird 0xFA angezeigt, sollte die richtige Speicherstelle gefunden sein. Das Überschreiben des Wertes gelingt mit dem Parameter -exclude:

srec_cat -generate 0x0FFE 0x0FFF -constan-l-e 7 1 firmware.hex -intel -exclude 0x0FFE 0x0FFF -o output.hex -intel -line-length=44 -address-length=2

Um die Erklärung etwas abzukürzen, habe ich direkt zwei weitere Parameter angehangen. Diese bewirken, dass der Adressraum nur mit 16 Bit angegeben wird und das auf eine Intel-hex Zeile nur eine begrenzte Anzahl an Zeichen eingefügt werden darf. In diesem Fall also 44 Zeichen pro Zeile. Einige Bootloader sind recht einfach geschrieben und benötigen (zu recht) keinen komplexen Parser. Sollte der Bootloader eurer Wahl also Probleme mit der genierten Datei bekommen, solltet ihr die Darstellung der Eingangs- und Ausgangsdatei vergleichen und evlt. die letzten Parameter anpassen.

Um einen hohen Automatisierungsgrad zu erreichen, habe ich noch eine Windows-Batchdatei erstellt, um mir 100 Hexdateien mit aufsteigender UID zu genieren. Einmal ausgeführt und schon sind 100 Dateien im Ausgabeordner:

for /l %%x in (0, 1, 100) do (
srec_cat -generate 0x0FFE 0x0FFF -constan-l-e %%x 1 Debug\firmware.hex -intel -exclude 0x0FFE 0x0FFF -o bins\output%%x.hex -intel -line-length=44 -address-length=2
)

 

 

Advertisements
Dieser Beitrag wurde unter Allgemein veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s