Forum: Compiler & IDEs Hardware initlaisieren vor main() - C++


von DerAlbi (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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"

von DerAlbi (Gast)


Lesenswert?

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?

von DerAlbi (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von DerAlbi (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von DerAlbi (Gast)


Lesenswert?

>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

von (prx) A. K. (prx)


Lesenswert?

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.

von DerAlbi (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

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...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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
Noch kein Account? Hier anmelden.