Hallo, ich suche schon seit Wochen eine vernünftige Grundlage, um zu verstehen, wie ARM Startup Code in GNU Assembler oder mit dem GCC geschrieben wird. Ich finde zwar diverse Beispiele und Tutorials, aber leider wird mir daraus nicht klarer, warum der Code funktioniert und welche Anpassungen ich für z.B. einen STM32 vornehmen muss. Prinzipiell bin mir zwar darüber bewusst, was alles zu tun ist. Allerdings ist mein Problem, dass die richtigen Anleitungen von ARM und ST selbst, sich komplett auf ARM Assembler beziehen. Und scheinbar kann man das auch nicht 1:1 portieren, das hab ich nämlich schon versucht. Obwohl lt. dem Mapping der Stack und Einsprungpunkt an der richtigen Stelle zu liegen scheinen, läuft ein mit GNU übersetztes Discovery-Board Beispiel für Keil, bei dem außer dem Startcode und Linkerscript nichts geändert wurde, bei meinen Versuchen leider nicht. Darin wollte ich erstmal ein paar grundlegende Sachen ausprobieren, bevor hier Sachen in Hardware gegossen werden. Bei meiner Suche bin ich bisher natürlich gestolpert über * http://www.embedded.com/design/mcus-processors-and-socs/4026075/Building-Bare-Metal-ARM-Systems-with-GNU-Part-2 * den STM32-Artikel (Abschnitt Startup code & Linker Script) * den Beitrag "ARM startup Code und Linkerscripte" im Forum, wegen dem ich u.a. auch lieber den Code selber schreiben möchte. Das hat mich alles schon weiter gebracht, allerdings gibt es halt einen Unterschied, zwischen "Codeblöcke zusammenklauben" und verstehen, was man da eigentlich macht. Und ich würde es wirklich gern verstehen, schon um den Fehler in meinem Startup Code finden zu können. Also nochmal konkret die Frage: Gibt es irgendwo ein/e Sachbuch/Anleitung, welches das schreiben von Startcode in GNU Assembler oder via GCC so erklärt, dass es jemand, der schon ein paar Jahre C/C++ programmiert hat und bisher nur für AVR hardwarenah programmiert hat? Zu welcher Plattform diese Anleitung ist, wäre egal (x86 oder so), sofern die ARM spezifischen Anweisungsblöcke auch noch irgendwo anders behandelt werden. Schönen Gruß und danke für Anworten.
Holger J. schrieb: > Gibt es irgendwo ein/e > Sachbuch/Anleitung, welches das schreiben von Startcode in GNU Assembler > oder via GCC so erklärt, dass es jemand, der schon ein paar Jahre C/C++ > programmiert hat und bisher nur für AVR hardwarenah programmiert hat? Das wäre ein dünnes Buch... Der startupcode von ST ist aus irgendwelchen Gründen in Assembler, kann aber auch genauso gut wunderbar in C geschrieben werden:
1 | extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss; |
2 | |
3 | void __attribute__((naked)) Reset_Handler () { |
4 | memcpy (&_sdata, &_sidata, &_edata-&_sdata); // Globale Variablen initialisieren |
5 | memset (&_sbss, &_ebss-&_sbss, 0); // Globale 0-Variablen mit 0 initialisieren |
6 | __libc_init_array (); // Konstruktoren globaler Objekte aufrufen |
7 | main (); // Programm starten. |
8 | }
|
Verstanden? Die lustigen Symbole kommen aus dem Linkerscript, so wie in dem von ST.
Erlkönig, das ist ja mal ein kurzer Code. In den Beispielen der ST Library sind noch FPU initialisierung und der Aufruf von SystemInit(); my 2ct. Adib. --
Adib schrieb: > In den Beispielen der ST Library sind noch FPU initialisierung und der > Aufruf von SystemInit(); Stimmt, das gehört aber meiner Meinung nach nicht zum Startupcode sondern zur main()... Aber ein Funktionsaufruf mehr und die FPU-Initialisierung (siehe ARM GCC) machen den Braten jetzt auch nicht fett. PS: Oben im Code ist noch was falsch, man muss die Pointer erst nach char* casten und dann subtrahieren, sonst ist das Ergebnis nicht in bytes sondern Words... Allgemein ist der ARMv7M so gebaut dass man direkt mit C Code starten kann und keinen Assembler braucht; man muss eben nur den Programmstatus (globale Variablen, Konstruktoren) initialisieren.
Niklas Gürtler schrieb: > Holger J. schrieb: >> Gibt es irgendwo ein/e Sachbuch/Anleitung > Das wäre ein dünnes Buch... Das wär mir egal, solange es verständlich ist und sich nicht irgendwelchen fertigen Bibliotheken bedient. Denn dann hab ich wieder nix gewonnen. > > Der startupcode von ST ist aus irgendwelchen Gründen in Assembler, kann > aber auch genauso gut wunderbar in C geschrieben werden: > [...] > Verstanden? Nur bedingt. Die Zusammenarbeit von Linkerscript und deinem Code ist mir im Grunde klar, aber mir geht es ja gerade darum, beides anhand der Prozessordokumentation selbst schreiben zu können. Egal ob in Assembler oder C. Aber eben unabhängig von etwaigen fertigen Toolchains. Zur Not möchte ich es mit make selbst zusammengebaut bekommen und als einziges notweniges Übel auf einen USB Treiber zum Flashen zurückgreifen müssen, der nicht von mir stammt. Das spielt nämlich keine Rolle, da sich die Schnittstelle bei eigener Hardware sowieso zu JTAG ändern wird und dafür gibt es hier schon eine Lösung. > Die lustigen Symbole kommen aus dem Linkerscript, so wie in dem von ST. Was ist "das ST Linkerscript"? Ich habe mir die Standard Peripherals Library von ST heruntergeladen und dort finde ich zwar diverse Linkerscripts für IAR, RealView, Ride und das im STM32 Artikel erwähnte TrueStudio, aber nichts für den GNU ld/cc selbst. So bringt mir das auch wieder nichts, weil es teilweise nicht frei verwendet werden darf (oder nur eingeschränkt), s. oben, und plattformunabhängig bin ich damit auch nicht. Ohne auf irgendwas fertiges zurückzugreifen, steh ich momentan wie vor einer Wand. Das ist ziemlich frustrierend.
> Das wär mir egal, solange es verständlich ist und sich nicht > irgendwelchen fertigen Bibliotheken bedient. Denn dann hab ich wieder > nix gewonnen. Mein Code benutzt nur die Standard C library, ich denke die kann man als bekannt vorrausetzen. Wenn du memcpy und memset nicht kennst, kannst du danach googlen und eine einfache Beispiel-Implementation finden. __libc_init_array () ist eine Funktion der C Library die durch ein globales Array aus Funktionenpointern geht und diese Aufruft; dieses Array wird vom Compiler & Linker gefüllt und enthält globale Konstruktoren. Du kannst dir im newlib Source die Implementation davon ansehen. > Nur bedingt. Die Zusammenarbeit von Linkerscript und deinem Code ist mir > im Grunde klar, aber mir geht es ja gerade darum, beides anhand der > Prozessordokumentation selbst schreiben zu können. Die Prozessordokumentation bringt dir hier praktisch gar nichts, sondern eher die von ld, binutils und GCC. > Egal ob in Assembler > oder C. Aber eben unabhängig von etwaigen fertigen Toolchains. Ohne Compiler und Linker machen Dinge wie "C Source" und "Linkerscript" keinen Sinn, also kannst du die auch so nicht verfassen. Ohne toolchain kannst du nur im Hexeditor das Flash-Image von Hand schreiben, dann brauchst du auch keinen Startup Code und kein Linkerscript. > Was ist "das ST Linkerscript"? ST's Beispiel-Linkerscript für den GCC. > Ich habe mir die Standard Peripherals > Library von ST heruntergeladen und dort finde ich zwar diverse > Linkerscripts für IAR, RealView, Ride und das im STM32 Artikel erwähnte > TrueStudio, aber nichts für den GNU ld/cc selbst. Das TrueStudio ist nur eine GUI die den GCC aufruft. Das Linkerscript dafür ist also das was du brauchst. > So bringt mir das auch > wieder nichts, weil es teilweise nicht frei verwendet werden darf (oder > nur eingeschränkt), Musst halt danach selber nochmal schreiben... > s. oben, und plattformunabhängig bin ich damit auch > nicht. Ohne auf irgendwas fertiges zurückzugreifen, steh ich momentan > wie vor einer Wand. Das ist ziemlich frustrierend. Wenn du nichts fertiges willst, musst du GNU ld's Doku über Linkerscripte lesen. ST's Linkerscript für den GCC ist von der Zielplattform abhängig, aber die Compiler-Plattform ist egal. > Ohne auf irgendwas fertiges zurückzugreifen, steh ich momentan > wie vor einer Wand. Das ist ziemlich frustrierend. Wenn du unbedingt dein eigenes Linkerscript schreiben willst (aber die Doku dazu nicht findest) stell dich auf noch viel mehr Frust ein. Weniger Frust ist es, das fertige zu verwenden.
:
Bearbeitet durch User
Niklas Gürtler schrieb: > __libc_init_array () ist eine Funktion der C Library die durch ein > globales Array aus Funktionenpointern geht und diese Aufruft; dieses > Array wird vom Compiler & Linker gefüllt und enthält globale > Konstruktoren. Du kannst dir im newlib Source die Implementation davon > ansehen. Ok, die war mir tatsächlich neu und bisher in den Startup-Code-Beispielen nicht bewusst über den Weg gelaufen. > Die Prozessordokumentation bringt dir hier praktisch gar nichts, sondern > eher die von ld, binutils und GCC. Genau das hab ich auch getan. Und damit auch irgendwas zusammengebaut, dass dann aber nicht läuft. Aber gut, dann werde ich mich halt mehr mit diesem Thema auseinander setzen müssen und hoffen, dass ich es doch noch hinbekomme. > Das TrueStudio ist nur eine GUI die den GCC aufruft. Das Linkerscript > dafür ist also das was du brauchst. Ich hab mir das Ding oberflächlich angesehen gehabt. Und zuerst dachte ich wegen der Beschreibung, dass es genau wie beim Keil seinen eigenen Compiler, etc. mitbringt. Beim Keil kann man zwar auch den GCC als andere Toolchain auswählen, aber mit dieser laufen die mitgelieferten Beispiele ohne die Anpassungen nicht, nach denen ich bisher erfolglos gesucht habe. Ich werf also mal dort einen Blick rein. Danke.
Holger J. schrieb: > Ok, die war mir tatsächlich neu und bisher in den > Startup-Code-Beispielen nicht bewusst über den Weg gelaufen. Hm? Das ist sogar im Startupcode von ST für Atollic drin. > Genau das hab ich auch getan. Und damit auch irgendwas zusammengebaut, > dass dann aber nicht läuft. Aber gut, dann werde ich mich halt mehr mit > diesem Thema auseinander setzen müssen und hoffen, dass ich es doch noch > hinbekomme. Das von 0 auf anzufangen ist wohl ziemlich schwierig; einfacher ist es die vorhandenen Linkerscripte & Startupcode mithilfe der ld/GCC Doku zu analysieren und zu sehen wie sie arbeiten. > Ich hab mir das Ding oberflächlich angesehen gehabt. Und zuerst dachte > ich wegen der Beschreibung, dass es genau wie beim Keil seinen eigenen > Compiler, etc. mitbringt. Heh, das machen nur Keil und mikroE, alle anderen sind nur GUI's für eben diese Compiler... PS: Linkerscripte & Startupcode schreiben ist halt nicht das was dem gewöhnlichen Anwendungs-Programmierer zugemutet wird - für die gibts "Installiere Keil" - deswegen gibts da keine/wenig einfache Dokumentation/Literatur. Da muss man halt in der LD doku etc. graben.
:
Bearbeitet durch User
Holger J. schrieb: > Prinzipiell bin mir zwar darüber bewusst, was alles zu tun ist. Bist du dir sicher? Ich bin eher vom Gegenteil überzeugt. Schau einfach mal, was sich so in einem Startup-Code für einen ARM7TDMI und in einem für einen Cortex so befindet. Beim ARM7TDMI gibt es dort das Aufsetzen de verschiedenen Stacks, den Starter für "main" und die eigentlichen Interrupt-Programme, beim Cortex den Starter für "main" und die als "weak" gekennzeichneten Default-Adressen für die Interrupts. Eigentlich ist ein Startup-Code ne interessante Sache, aber man sollte wirklich wenigstens ein bissel von der Hardware verstehen. W.S.
W.S. schrieb: > beim Cortex > den Starter für "main" und die als "weak" gekennzeichneten > Default-Adressen für die Interrupts. Unsinn, das "Aufsetzen der Interrupt-Adressen" geschieht durchs Flashen, d.h. dadurch, dass man die Tabelle der ISR's an eine bestimmte Stelle in den Flash packt (bei STM32 z.B. ab 0x4, aber allgemein Implementations-spezifisch). Damit hat der Startupcode gar nichts zu tun. Für die main() muss er ebenfalls nichts aufsetzen, sondern die lediglich aufrufen. > Eigentlich ist ein Startup-Code ne interessante Sache, aber man sollte > wirklich wenigstens ein bissel von der Hardware verstehen. Wenn die Hardware da bestimmte Bedürfnisse hat ja; gerade bei den ARMv7M (Cortex-M3,4) brauchts quasi nur Wissen über den Compiler & Linker, da der Prozessor sich selbst initialisiert, man muss nur das eigene High-Level C(++) Anwendungs-Gedöns initialisieren.
Niklas G. schrieb: > Holger J. schrieb: >> Gibt es irgendwo ein/e >> Sachbuch/Anleitung, welches das schreiben von Startcode in GNU Assembler >> oder via GCC so erklärt, dass es jemand, der schon ein paar Jahre C/C++ >> programmiert hat und bisher nur für AVR hardwarenah programmiert hat? > Das wäre ein dünnes Buch... > > Der startupcode von ST ist aus irgendwelchen Gründen in Assembler, kann > aber auch genauso gut wunderbar in C geschrieben werden: >
1 | extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss; |
2 | >
|
3 | > void __attribute__((naked)) Reset_Handler () { |
4 | > memcpy (&_sdata, &_sidata, &_edata-&_sdata); // Globale Variablen |
5 | > initialisieren |
6 | > memset (&_sbss, &_ebss-&_sbss, 0); // Globale 0-Variablen mit 0 |
7 | > initialisieren |
8 | > __libc_init_array (); // Konstruktoren globaler Objekte aufrufen |
9 | > main (); // Programm starten. |
10 | > } |
> Verstanden? > > Die lustigen Symbole kommen aus dem Linkerscript, so wie in dem von ST. Im obigen Code sind ein paar Fehler, die, da der Thread prominent in den Suchergebnissen zu STM32 startup-code auftaucht, besser korrigiert werden.
1 | extern unsigned char _sidata, _sdata, _edata, _sbss, _ebss, _estack; |
2 | |
3 | void __attribute__((naked)) __libc_init_array (); |
4 | |
5 | void __attribute__((naked)) Reset_Handler () { |
6 | memcpy (&_sdata, &_sidata, &_edata - &_sdata); |
7 | memset (&_sbss, 0, &_ebss - &_sbss); |
8 | __libc_init_array (); |
9 | main (); |
10 | }
|
Die vertauschten Parameter bei memset() sind leicht zu bemerken. Aber der Typ von _sidata, etc verursacht subtilere Fehler, da das erste Viertel der Variablen noch richtig initialisiert wird. Philipp
Das allein reicht aber noch nicht, es fehlt auch noch eine Vektortabelle, mindestens eine mit den ersten 8 Bytes (Stack und Reset) damit er loslaufen kann. Hier möch ich auch noch ein ballastfreies aber vollständiges Beispiel von mir beitragen: https://github.com/prof7bit/bare_metal_stm32f401xe
Philipp Klaus K. schrieb: > &_edata - &_sdata Ist das korrektes C? Standard sagt: "When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object" was im Code nicht der Fall ist. Also besser auch die Differenz im ld-Script berechnen und als Symbol verfügbar machen.
Johann L. schrieb: > Philipp Klaus K. schrieb: >> &_edata - &_sdata > > Ist das korrektes C? So lange es funktioniert würde ich diese Frage für diesen Fall nicht als so wichtig erachten. So etwas wie startup-code ist im C-Standard eh nicht bedacht, genausowenig wie ein __attribute__((naked)), oder eine Funktion namens __libc_init_array () aufzurufen. All das ist bestenfalls von der Implementierung definiertes Verhalten, schlimmstenfalls ein "funktioniert halt irgendwie". Aus Sicht des C-Standards beginnt die Ausführung des Programms bei main(). Philipp
Wenn man kein C++ machen will kann man den Aufruf von __libc_init_array() getrost weglassen. Das ruft nämlich nur die Konstruktoren von statischen C++ Objekten auf damit die alle initialisiert sind bevor main() beginnt. In C existiert sowas nicht.
Bernd K. schrieb: > In C existiert sowas nicht. Ist aber auch in GNU-C nutzbar per __attribute__((constructor))
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.