Hallo. Ich habe hier ein C++ Projekt auf einem Mikrocontroller! Dieser hat externen ram und der Heap ist dorthin ausgelagert. Nun ist das Problem: Entweder ich nutze den C-Startupcode, den ich als ASM habe - dort kann ich editieren, dass zuerst mein Speicher initilaisiert wird, bevor der Heap (.bss und .data) initialisiert wird. Nachteil ist, dass nun die Konstruktoren der statischen Variablen nicht mehr aufgerufen werden. Ich würde gerne C++ dazu bringen direkt nach dem Reset aber VOR der Initialisierung von .bss und .data einige Hardweareinitialisierungs-Funktionen aufzurufen. Ich hab nen AVR32 AP7000 (GCC) Es MUSS irgendwie gehen. MFG
Haben die Linkerscripts beim AVR32 denn keine .initN-Sections? (N = Zahl von 1 bis 9) In diesen werden solche Initialisierungs- aufgaben normalerweise erledigt, die C++-Konstruktoren belegen auch eine davon. Du müsstest also im Linkerscript mal nachsehen, welche sie belegen und dann deinen Initialisierungs-Code in einer Section davor platzieren. Aber Vorsicht, da ist u. U. der Stack noch nicht initialisiert, normalerweise baut man dort nur ,,flache'' Codestücke ein, keine Funktionen mit CALL/RET. In C kann man derartige Funktionen mit dem Attribut `naked' erzeugen, was aber gelegentlich auch Fallstricke mit der Optimierung bringen kann, siehe Beitrag "GCC Startet nicht main() auf Mega168"
Also so wie es aussieht hat das Linkerscript keine InitN-Sections. ich hab schon versucht damit zumzuspielen. Hier der entsprechende Ausschnitt:
1 | ... |
2 | .rela.plt : { *(.rela.plt) } >PROG_MEM AT>PROG_MEM |
3 | .init : |
4 | { |
5 | KEEP (*(.init)) |
6 | } >PROG_MEM AT>PROG_MEM =0xd703d703 |
7 | .plt : { *(.plt) } >PROG_MEM AT>PROG_MEM |
8 | .text : |
9 | ... |
Du meint, wenn ich vor .init eine Section einfüge und dort meinen Init-Code rein schreibe, klappt das? Muss die Funktion wirgendwie besonderns heißen?
so. ich habs! Hab eine neue section angleget.. noch vor .reset:
1 | SECTIONS |
2 | { |
3 | /* Read-only sections, merged into text segment: */ |
4 | PROVIDE (__executable_start = 0x00000000); . = 0x00000000; |
5 | .init0 : { KEEP( *(.init0)) } >PROG_MEM AT>PROG_MEM |
6 | .interp : { *(.interp) } >PROG_MEM AT>PROG_MEM |
7 | .reset : { *(.reset) } >PROG_MEM AT>PROG_MEM |
8 | ... |
(wichtig ist das KEEP bei .init0, sonst wird die Funktion weggeschmissen.. die ruft ja niemand auf.) Dann hab ich einen Code geschreiben, der während einer normalen C-initilaisierung genommen würde:
1 | extern "C" __attribute__((naked, used, __section__(".init0"))) |
2 | void __init() |
3 | {
|
4 | asm volatile ("lda.w sp, _estack"); |
5 | asm volatile ("rcall InitGPIOs"); |
6 | asm volatile ("rcall InitRam"); |
7 | asm volatile ("csrf %0" : : "n" (AVR32_SR_EM_OFFSET)); |
8 | asm volatile ("nop"); |
9 | asm volatile ("nop"); |
10 | //Weiter mit normalen StartCode -> return
|
11 | }
|
Dieser Code wird jetzt sofrot am Anfang eingefügt. Der Stackpointer ist initlaisiert - das erlaubt zumindest Funktionsaufrufe. Hier der Fall <InitGPIOs> und <InitRam>. Prinzipiell kann man dann dort alles reinschreiben was man will, wie es scheint. Der normale .reste-Code wird dann einfach hinten rangehängt. Geht alle sproblemlos bis jetzt. :-) Danke, war eine Hilfe, auch wenn ichs zum schluss selbst geschafft habe.
Die Nummer mit den Init-Sections wie in der avr-libc geht wie wie mittlerweile wissen schon bei den AVRs u.U. in die Hose, weil Funktionen nicht zwingend dort aufhören wo man es gerne hätte. Drum ist ist mir diese Methode im Startup von beispielsweise ARMs auch noch nicht begegnet. Kann ich nicht wirklich empfehlen. Man kann aber jederzeit in den Startup-Code generell einen bestimmten Funktionsaufruf (z.B. __initHardware) einfügen. Will man diese Funktion nicht immer verwenden, dann definiert man weiter unten in Startup eine "weak" Funktion dieses Namens die nur aus einem Return besteht. Die greift, wenn sich sonst niemand gleichen Namens findet.
Naja das problem: wie fügt man denn einen bestimmten Funktoinsaufruf so ein, dass er nicht u.U in die Hose geht?? Man bemerke: der Startupcode liegt also lib vor. Also im Code rumschreiben is nicht. Ich bin eigentlich ganz zu frieden damit, muss ich sagen.. ich wüsste jetzt keinen Grund warum das jemals nicht funktionieren sollte, in meiner Konfiguration :-) MFG
DerAlbi schrieb: > Man bemerke: der Startupcode liegt also lib vor. Ok, das war mit so nicht bekannt, oben hast du geschrieben, er läge als ASM vor, das las ich als Assembler-Quelltext. > Ich bin eigentlich ganz zu frieden damit, muss ich sagen.. ich wüsste > jetzt keinen Grund warum das jemals nicht funktionieren sollte, In der von dir benutzten Variante wird das wohl funktionieren, also mit bekanntem Assembler-Code, der seinerseits nur Funktionen aufruft. Die Initialisierungsfunktionen dort direkt rein zu pinnen, wie Jörg das im Sinn hatte, ist jedoch hoch riskant. Auf manchen ARM Plattformen, darunter m.W. auch denen von Atmel, können sich aber Nebenwirklungen in Bezug auf das Debugging per JTAG ergeben. Ob bei AVR32 ähnlich wir bei vielen ARMs der Debugger einen ab Reset ungebremst loslaufenden Core erst einfangen muss ist mir aber nicht bekannt.
>Ok, das war mit so nicht bekannt, oben hast du geschrieben, er läge als >ASM vor, das las ich als Assembler-Quelltext. Sry, Ich hab den C-StartupCode als ASM vor mir.. aber C++ StartupCode als Lib. Deswegen hab ich erst den C-StartupCode genommen. Nun war ich aber mit der Einschränkung unzufrieden, dass der die Konstruktoren der statischen variablen nicht aufruft. Deswegen war ich nun auf der Suche nach einer richtigen C++Vairante. MFG
Komische Methode, zwischen C und C++ Startup zu unterscheiden. Steckt da irgendwie Geld drin, nach dem Schema: wer mehr will muss ordentlich Geld rausrücken? Klingt abschreckend. Startup von sowas will ich als Quellcode, sonst droht Ablage P.
Vielleicht gibts den auch ;-) Aber ich hab davon ehrlichgesagt eher weniger Ahnung. Naja.. ich sag mal so: für C++ ist halt auch recht wenig Support.. Da steckt sicher kein Geld dahinter und abschreckend sollte es nicht sein... passt schon. Hab ja auch sonderanforderungen. Normalerweise läuft auf dem AP7000 ein Linux. Da braucht man soetwas nicht. Und wenn überhaupt scheinen die meißten leute - warum auch immer - C zu bevorzugen... also braucht man das noch viel weniger ;-) MFG
A. K. schrieb: > Die Nummer mit den Init-Sections wie in der avr-libc geht wie wie > mittlerweile wissen schon bei den AVRs u.U. in die Hose, weil Funktionen > nicht zwingend dort aufhören wo man es gerne hätte. Das stimmt so nicht. Das stimmt nur für in C geschriebene und als "naked" deklarierte Funktionen. Die Methode funktioniert trotzdem noch einwandfrei für reine Assembler-Schnipsel, und das Stückchen da oben hätte ich selbst wohl auch als Assemblerdatei dazu gelinkt, statt mich mit dem Inline-Assembler zu verrenken. Der inline- Assembler ist in der hier gezeigten Form aber auch kein Kandidat für irgendwelche Code-Umstellungen durch den Compiler, insofern auch sicher.
Das ist richtig, aber wenn man sowas zukunftsträchtig einrichten will, dann sind "naked" Funktionen in einer Init-Section nun einmal problematisch. Wenn man das auf Assembler-Stücke beschränkt, dann wird das funktionieren, ist aber nicht jedermanns Sache. Eine sicherere Variante scheint mir darin zu bestehen, analog zu den Konstruktur-Aufrufen eine oder mehrere Sektionen zu schaffen, die nicht aus dem nackten Code selbst bestehen, egal ob Assembler oder C, sondern aus Zeigern auf den Code. Das kommt ohne Assembler aus, die Funktionen benötigen keinen speziellen Frame und nur die Vektoren werden der Sektion zugeordnet.
Statische Instanzen in C++ ist keine gute Idee, da man praktisch keine Kontrolle ueber die Initialisierungsreihenfolge hat. Das ist oft nur am Anfang eines Projekts eine coole Idee und raecht sich spaeter bitter.
Korrekt. Man kann sie sehr wohl verwenden, muss dabei unbedingt daran denken, ausschliesslich die eigenen Resourcen, d.h. den eigenen Inhalt, zu initialisieren. Abhängigkeiten von anderen solchen Daten oder vom Zustand der Hardware sind sehr problematisch. Andersum neige ich dazu, bei vollintegrierten Controllern dynamische Allokation zu vermeiden, egal ob C oder C++, weil bei rein statischer Allokation und restriktiver Allokation von Daten auf dem Stack der Speicherbedarf weit besser übersehbar ist.
A. K. schrieb: > Andersum neige ich dazu, bei vollintegrierten Controllern dynamische > Allokation zu vermeiden, egal ob C oder C++, weil bei rein statischer > Allokation und restriktiver Allokation von Daten auf dem Stack der > Speicherbedarf weit besser übersehbar ist. Deswegen habe ich eine Main-Klasse als einzige statische Instanz oder als lokale Instanz innnerhalb von main() und alles andere, was global gebraucht wird, ist Member von Main. Alle Initialisierung, die ueber einfaches Aufsetzen von Membern geht, machen die Klassen in einer extra Methode, die von Main in definierter Reihenfolge aufgerufen wird. Das erspart einem uebrigens auch sonst vielen lustigen Aerger bei Klassenhierarchien, weil man dann im Konstruktor nicht auf einem halb initialisierten Objekt rumwerkelt...
Peter Stegemann schrieb: > Deswegen habe ich eine Main-Klasse als einzige statische Instanz oder > als lokale Instanz innnerhalb von main() und alles andere, was global > gebraucht wird, ist Member von Main. Das alloziert allerdings den Speicher auf dem Stack, was die Sache sehr unübersichtlich macht. Controller-Benutzer sind Freunde voll statischer Allokation, weil man diese prima zur Compilezeit bereits sieht und die Unwägbarkeit eines Stacküberlaufs unwahrscheinlicher wird.
Jörg Wunsch schrieb: > Peter Stegemann schrieb: > >> Deswegen habe ich eine Main-Klasse als einzige statische Instanz oder >> als lokale Instanz innnerhalb von main() und alles andere, was global >> gebraucht wird, ist Member von Main. > Das alloziert allerdings den Speicher auf dem Stack, Nur beim "oder..."-Fall. > was die Sache > sehr unübersichtlich macht. Controller-Benutzer sind Freunde voll > statischer Allokation, weil man diese prima zur Compilezeit bereits > sieht und die Unwägbarkeit eines Stacküberlaufs unwahrscheinlicher > wird. Bezueglich des Stackueberlaufs sehe ich da keinen grossen Unterschied. Der Gesamtverbrauch ist nur um einen statischen Umfang groesser, dafuer der Restspeicherverbrauch um den gleichen Umfang kleiner.
Peter Stegemann schrieb: > Der Gesamtverbrauch ist nur um einen statischen Umfang groesser, dafuer > der Restspeicherverbrauch um den gleichen Umfang kleiner. Yep, aber den einen Verbrauch sehe ich direkt, den anderen nicht. > Alle Initialisierung, die ueber einfaches Aufsetzen von Membern > geht, machen die Klassen in einer extra Methode, die von Main in > definierter Reihenfolge aufgerufen wird. Ebenso. Nur eben auf statischer Basis.
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.