Hallo zusammen
ich schreibe gerade einen Bootloader für einen AT91SAM7S256 und benutze
die Yagarto-Toolchain (mit der Eclipse Integration). Im Bootloader
selber brauche ich jedoch keine der C-Standard-Funktionen und include
auch keine Header-Files wie stdio.h, stdlib.h oder ähnliche (die brauche
ich nur in der Hauptapplikation).
Da ich unterdessen der 16kB Grenze, die ich mir für den Bootloader
gesetzt habe, sehr nahe gekommen bin (das Binary ist nurnoch 4 Byte
kleiner ;-)) habe ich mir mal aus dem Map-File zusammengerechnet, wie
gross die einzelnen Dateien genau sind, damit ich sehen kann, wo ich
eventuell noch abspecken muss. Dabei ist mir aufgefallen, dass sehr
viele Header-Files von Yagarto included werden z.B.
welches die malloc() und free() Funktionen beinhaltet! Der ganze "Spass"
benötigt schlussendlich etwa 10.5 kB (hab einfach die Size-Angaben aus
dem Map-File addiert).
Kann mir jemand erklären, warum das genau so ist? Ich benutze weder die
malloc() noch die free() Funktion. Eigentlich benutze ich ja gar keine
Funktionen der C-Standard-Library. Dass da n'paar Funktionen vom
Compiler gebraucht werden, hatte ich schon erwartet, aber 10.5 kB????
Gibt es eine Möglichkeit diese Includes zu umgehen oder muss das so
sein, damit der Compiler überhaupt richtig kompilieren kann?
In der Hauptapplikation sind sogar über 50 kB aus der Yagarto-Lib, aber
da verwende ich auch des öfteren Funktionen aus der Standard-Library
(trotzdem scheinen mir über 50 kB doch relativ viel...).
Wäre froh wenn mich jemand "aufklären" könnte, ob das normal ist (und
wenn ja warum) oder ob man was dagegen machen kann.
Tausend Dank.
Die Includes spielen später keine Rolle.
Du solltest in der MAP-Datei oder dem ASM-Listing nachsehen, ob die
fraglichen Funktionen drin sind.
Dort siehst du generell welche Variablen und Funktionen im Endprodukt
drin sind und wieviel Platz jedes einzelne Symbol benötigt.
Ähm aus der MAP-Datei hab ich die Grösse etc ja erst ausgerechnet. Und
von dort kommt auch die oben aufgeführte Beispiel-Zeile eines Includes.
Sonst wüsste ich gar keine Möglichkeit wo ich nachsehen könnte, was
genau alles included wird. Kann man irgendwo sehen, welche Header-Files
genau von welchem File included werden? Habe bisher immer mit dem
MAP-File gearbeitet, weil da ja Grösse und Dateien in der (mehr oder
weniger) korrekten Reihenfolge drinstehen, wie sie später im Speicher
vorkommen werden.
OK ich hab den "Übeltäter" gefunden. Ich hab ne eigene crt0.S etc
geschrieben und in der steht (wie meines Wissens ungefähr üblich):
1
/* --- Now enter the C code */
2
ldr r0, =exit
3
mov lr, r0 /* Return address = exit */
4
ldr r0, =main
5
bx r0
Sobald ich die ersten beiden Zeilen, die die exit-Routine laden,
auskommentiere, sinkt die Grösse meines Binaries auf 5.5 kB. Mir ist
klar, dass die newlib Library mit libc sehr stark "verdrahtet" ist also
dass viele Dateien von anderen abhängen, aber dass das einfach mal so
mehr als 10 kB ausmacht?
Da es aber unschön ist, diese beiden Zeilen einfach wegzulassen (was
passiert, wenn main aus irgendwelchen Gründen (trotz while(1)) beendet
wird und dann da nicht zu exit gesprungen wird?) wollte ich fragen, ob
es eine Möglichkeit gibt, sowas irgendwie einfach selbst
sicherzustellen.
Dein Sprachgebrauch von included ist ungewöhnlich und kann
Diskussionen erschweren. Du meinst, was aus den Libraries gelinkt
wird.
Um den Startupcode in Ruhe zu lassen, könntest du eine eigene,
Schmalhans exit() Funktion schreiben, die zur Not nur ein while(1);
enthält bzw. beim Bootloader u.U. garnix, weil der anders den geladenen
Code anspringt. Dann wird die fette exit() aus der Library nicht mehr
dazugelinkt.
Ja sorry, ich geh mit diesen Begriffen manchmal zu "leichtsinnig" um ;-)
Natürlich meine ich gelinkt.
Eigentlich ist es auch gar nicht möglich aus der main-Funktion des
Bootloaders rauszukommen, da da am Ende sowieso ein
1
while(1);
steht, aber ich werde wohl noch eine Endlos-Schleife in die crt0.S Datei
einbauen, sicher ist sicher.
Danke für deine Hilfe.
Jetzt merke ich gerade, dass ich deinen Beitrag nicht ganz verstanden
habe.
Ja eine eigene exit() Funktion zu schreiben, wäre auch eine Alternative.
Vor allem liesse sich das dann besser anpassen, falls das mal nötig
wäre.
Den Startup-Code zu ändern ist nicht so tragisch, da ich den sowieso
selber zusammengestellt und geschrieben habe (mit Vorlage).
@Sascha M.: Kannst Du mir evtl. Informationen zur Verfügung stellen, wie
Dein Bootloader funktionieren soll?
Ich selber habe keine Vorstellungen, wie ich solch ein Code anspringen
sollte, bzw. den Stack und C-Code (Variablen, Interrupt,...) initiieren
sollte.
Im Howto von J. Lynch ist ein Startupcode vorhanden, der auf vielen ARM7
Modellen auf anhieb läuft. Afaik bezieht es sogar explizit auf den
AT91SAM7S256.
Um den Einsprung in den Startupcode muss man sich normalerweise nicht
kümmern.
Der Code ist so geschrieben, dass bei Adresse 0x0 ein Sprungbefehl zur
Adresse mit dem eigentlichen Resethandler steht. Ab 0x1 sind die
Vektoren für Interrupts etc. zu finden.
Nach dem der Resethandler ausgeführt wurde, springt dieser normalerweise
zur Funktion main. Von dort aus beginnt das Hauptprogramm
Ich kenne mich mit Assembler usw. noch nicht so gut aus, deshalb kann
ich dir zur Stackbehandlung usw. nicht so viel sagen. Ansonsten kann ich
noch auf mein Howto verweisen. Das ist zwar für einen anderen uC, der
ARM-Teil ist jedoch fast identisch.
@Heiko_S: Tilo hat ja schon einige deiner Fragen beantwortet.
Ich persönliche hatte mich am crt0.S File der newlib orientiert. Dort
findet man für viele verschiedene Plattformen die jeweiligen
Startup-Files.
Falls du weitere Fragen hast, einfach raus damit ;-)
Da ist vielleicht die Frage schlecht formuliert worden!
Den StartUpCode kenne ich zur genüge (schon einige Tage damit
verbracht). Aber ein Bootloader aus einem laufenden Programm-, oder
Bootloader grundsätzlich anspringen?
Dann, wenn kein Programm übertragen werden soll, das eigentliche
Hauptprogramm anspringen?
Wohin ist dieses dann gelinkt, da bei PowerUp immer der Flash auf
Adresse 0 zeigt.
Beim AVR ist mit einem BootLoaderFlag automatisch erst der Bootloader
aktiv, aber beim ARM?
Aus diesem Grunde (kein BootLoaderFlag beim ARM) stellen sich bei mir so
viele Fragen, wie überhaupt solch ein BootLoader funktionieren soll.
Ok, jetzt wird es etwas klarer.
Ich kann jetzt nur etws zum ADUC schreiben. In dessen Flash liegt ein
2kb großer Bootloder von ADI. Dieser wird vor dem Startup Code
ausgeführt. In diesem sind zum einen Kalibrierwerte gespeichert. Zum
anderen wird der Pegel des DWN-Pin überprüft. Ist der Pegel high, wird
über Uart neue Firmware geladen. Ist der Pegel low, wird der
Startprozess an Adresse 0x0 fortgesetzt.
Dies sollte bei anderen uCs ähnlich sein.
Also ich linke den Bootloader auf die Adresse 0x0 und die
Hauptapplikation auf 0x4000 => es bleiben 16 kB für den Bootloader
(relativ viel, wird vielleicht noch verkleinert). In den Bootloader kann
man einfach mit einem Sprung nach 0x0 kommen. Oder halt, wenn das Geräte
eingeschaltet wird.
Dadurch ist der Bootloader immer vorhanden und muss nicht erst wie bei
SAM-BA durch setzen von Jumper ins interne Flash kopiert werden. Der
Nachteil ist halt, dass dieser Speicherbereich für die Hauptapplikation
nicht zur Verfügung steht.
Ein Knackpunkt sind dann noch die Interrupt-Vektoren (sofern du die
nicht ins RAM kopierst). Ich mache es so, dass bei 0x0 die
Interrupt-Vektoren des Bootloaders sind, diese aber einfach aus einem
Branch zu den Interrupt-Vektoren der Hauptapplikation bestehen, welche
bei 0x4000 liegen. Dadurch musst du im Bootloader halt auf
Interrupt-Vektoren verzichten und des Öfteren mal auf Flag-Polling
zurückgreifen.