Forum: Mikrocontroller und Digitale Elektronik AVR: Speicherverwaltung in Mischprojekt C++ & ASM


von Markus R. (gegenwartsanalyst)


Lesenswert?

Hallo und frohe Ostern,

Ich habe ein AVR Projekt aus C++ und Assembler Code. Zum Kompilieren und 
Linken verwende ich avr-gcc (via ArduinoMakefile). Die `main()` liegt im 
C++ Code. Kompiliert wird das für einen Atmega328p (old bootloader) auf 
einem Arduino.

Das alles funktionierte auch wunderbar, solange der SRAM einigermaßen 
leer blieb. Mittlerweile brauche ich 75% und beobachte, dass der Stack 
in meine Variablen hineinwächst. Die weitere Untersuchung am Simulator 
ergab, dass in der assemblierten `main()` der Stackpointer umgesetzt 
wird. Während er Anfangs bei 0x08ff liegt, wird er auf signifikant 
weniger herabgesetzt.

Ich frage mich jetzt, wo die Anweisung dafür herkommt. Von mir ist sie 
nicht, und ich verstehe auch nicht, wie es sein kann, dass das im Namen 
meiner `main()` geschieht.

Ich habe gelesen, dass man das über eine Umgebungsvariable(?) `RAMEND` 
kontrollieren könne, aber ich weiß nicht wo, d.h. an welcher Stelle sie 
existieren muss. Vor `avr-gcc main.cpp ...` ändert das nichts.

Ich bin noch recht neu in der Materie, würde den Sachverhalt gerne 
verstehen, und bin für jegliche Hilfe dankbar.

von Oliver S. (oliverso)


Lesenswert?

Markus R. schrieb:
> Die weitere Untersuchung am Simulator
> ergab, dass in der assemblierten `main()` der Stackpointer umgesetzt
> wird. Während er Anfangs bei 0x08ff liegt, wird er auf signifikant
> weniger herabgesetzt.
>
> Ich frage mich jetzt, wo die Anweisung dafür herkommt. Von mir ist sie
> nicht,

Tja, von jemandem muß sie aber sein. Irgend jemand wird da ein großes 
Array o.ä. auf dem Stack anlegen. Wenn nicht du, wer dann?

Oliver

von sourcen gucker (Gast)


Lesenswert?

Markus R. schrieb:
> und bin für jegliche Hilfe dankbar.

Ohne deine Sourcen zu sehen wird das wohl schwierig werden.

von EAF (Gast)


Lesenswert?

Oliver S. schrieb:
> Tja, von jemandem muß sie aber sein. Irgend jemand wird da ein großes
> Array o.ä. auf dem Stack anlegen. Wenn nicht du, wer dann?

Eine Rekursion?!?!

von Lotta  . (mercedes)


Lesenswert?

Wenn Du in C++ deine Objekte dynamisch anlegst, brauchst Du
viel Platz im RAM, auch wenn du Klassen hast, die viel
kopieren, also nen Kopierkonstruktor besitzen.

Also, statt

Medusa *Lotta = new Medusa(roter_rock,silberne_highheels);
lieber
Medusa.Lotta(roter_rock,silberne_highheels);

verwenden. :-P

mfg

von Lotta  . (mercedes)


Lesenswert?

Naja, es war gestern ein bissel spät, ich muß die Zeile
 Medusa.Lotta(roter_rock,silberne_highheels);

berichtigen, da muß es natürlich heißen:
 Medusa Lotta(roter_rock,silberne_highheels);

also ohne den Punkt.

Wenn Du Klassen dynamisch zuweist, vergiß
bitte nach Gebrauch der Klasse das rausschmeißen aus dem
Speicher nicht, also

 Medusa *Lotta = new Medusa(roter_rock,silberne_highheels);
 Lotta -> Tanze("tanzvorlage.med");
   ...
                      // Nun ist Lotta fertig und möchte raus
 if(Lotta)
 {
  delete Lotta;
  Lotta = nullptr;    // oder Lotta = NULL;
 }

Jetzt erst räumt Lotta ihren Speicher, und Du kannst ihn neu verwenden.

Dasselbe gilt auch für asm.
Gehe deine Quellen pedantisch durch, ob auch alle dyn. 
Speicheranforderungen zurückgenommen werden!

mfg

von Peter D. (peda)


Lesenswert?

Markus R. schrieb:
> Ich frage mich jetzt, wo die Anweisung dafür herkommt. Von mir ist sie
> nicht

Muß sie aber.
Default geht der Stack immer bis RAMEND, siehe hier:
https://www.nongnu.org/avr-libc/user-manual/malloc.html

von Peter D. (peda)


Lesenswert?

Für Mixprogramme *.C, *.S ist es immer das beste, für das *.S erstmal 
ein *.C zu schreiben, was alle Parameter und Variablen enthält und es 
nach *.S zu compilieren. Dann kann man darin bequem den Assemblercode 
einfügen.

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Markus R. schrieb:
> Ich frage mich jetzt, wo die Anweisung dafür herkommt.

Der avr-gcc führt ein Stückchen Assembler Code aus, bevor er die 
main() Funktion aufruft. Ich nehme an, dass du diesen Code meinst.

Der Code wird vom avr-gcc generiert. Beim arm-gcc würde er als *.s Datei 
vorliegen. Du kannst dir das Assembler-Listing generieren lassen, dann 
siehst du ganz genau, was der Compiler aus deinem Programm gemacht hat.

avr-objdump -h -S programm.elf

Ich habe mal ein Beispiel angehängt. Beim Reset wird dort zur Adresse 
00000074 gesprungen, dort ist der interessante Teil, und zwar bis zur 
Zeile

>   a6:  0e 94 86 00   call  0x10c  ; 0x10c <main>

wo die main Funktion aufgerufen wird. Du siehst, dass da einiges vorher 
passiert. Man kann da übrigens sogar C Funktionen noch vor main() 
aufrufen. Zum Beispiel so:
1
void my_init() __attribute__ ((naked))  __attribute__ ((section (".init5")));
2
3
void my_init() {
4
   ...
5
}
6
7
int main() {
8
   ...
9
}

Aber Achtung, es gibt Einschränkungen, was man in solchen Funktionen tun 
darf. Das ist in der Doku der avr-libc beschrieben.

von EAF (Gast)


Lesenswert?

Lotta  . schrieb:
> Wenn Du Klassen dynamisch zuweist, vergiß
> bitte nach Gebrauch der Klasse das rausschmeißen aus dem
> Speicher nicht, also

Selbst wenn du recht hast, hat es doch nichts mit dem Problem zu tun.
Denn new lässt zwar den Heap sich füllen, aber nicht den Stack in den 
Heap wachsen.

Also: Deine Aussage ist richtig, aber Thema verfehlt.

von Oliver S. (oliverso)


Lesenswert?

Stefan ⛄ F. schrieb:
> Markus R. schrieb:
>> Ich frage mich jetzt, wo die Anweisung dafür herkommt.
>
> Der avr-gcc führt ein Stückchen Assembler Code aus, bevor er die
> main() Funktion aufruft. Ich nehme an, dass du diesen Code meinst.

Was mit dem Problem des TO überhaupt nichts zutun hat.

Der Stack beginnt beim AVR-GCC mit avrlibc am Ende des SRams. Immer. 
Auch bei Arduino. Was ja der TO auch bestätigt, denn der Stackpointer 
ist zu Beginn von main genau da, wo er hingehört. Was davor passiert, 
ist für das Problem uninteressant.

Alles, was nach dem Start von Main passiert, ist Anwenderprogramm.  Da 
legt entweder eine Arduino-Lib oder der TO selber Daten auf den Stack.

Da der Sourcecode aber geheim ist, muß der TO halt selber rausfinden, 
was er da eigentlich tut.

Oliver

von Markus R. (gegenwartsanalyst)


Lesenswert?

Vielen Dank schonmal für die Antworten!

Ich habe mir ein minimales Beispiel erstellt, bei dem die Sache mit dem 
Stackpointer ebenfalls auftaucht.

Mittlerweile habe ich herausgefunden, warum der Speicherverbrauch bei 
meinem Projekt zu einem Problem führt -- es wird einfach zu viel 
alloziert. Das hat aber mit der Umsetzung des Stackpointers nichts zu 
tun.

Was mir noch dazu einfällt, hängt mit meinem build Prozess zusammen, da 
ich ja wie oben gesagt das 
[Arduino-Makefile](https://github.com/sudar/Arduino-Makefile) ist da 
noch Müll dabei.

`/usr/bin/avr-gcc -mmcu=atmega328p -Wl,--gc-sections -Os -flto 
-fuse-linker-plugin -o build-nano-atmega328old/BlinkInAVRC_.elf 
build-nano-atmega328old/main.S.o build-nano-atmega328old/libcore.a -lc 
-lm`

Ich weiß nicht, was libcore macht. Die Quellen habe ich noch nicht 
gefunden. Auf welchem Weg dabei dann die `main()` manipuliert wird, ist 
mir deshalb immer noch schleierhaft. Weil ich mir aber insgesamt mehr 
Kontrolle wünsche, werde ich wohl alles in Assembler umsetzen.

von Lotta  . (mercedes)


Lesenswert?

EAF schrieb:
> Lotta  . schrieb:
>> Wenn Du Klassen dynamisch zuweist, vergiß
>> bitte nach Gebrauch der Klasse das rausschmeißen aus dem
>> Speicher nicht, also
>
> Selbst wenn du recht hast, hat es doch nichts mit dem Problem zu tun.
> Denn new lässt zwar den Heap sich füllen, aber nicht den Stack in den
> Heap wachsen.
>
> Also: Deine Aussage ist richtig, aber Thema verfehlt.

Entschuldige bitte, @EAF, ich wollte doch blos helfen,
und keine Märchen erzählen.
Kannst Du mir noch mal verzeihen? :-O

mfg

von EAF (Gast)


Lesenswert?

Lotta  . schrieb:
> Kannst Du mir noch mal verzeihen? :-O

Nagut!

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.