Forum: Compiler & IDEs Ist mein Ram bereits voll?


von Sepp (Gast)


Lesenswert?

Hallo,

ich nutze einen AT90CAN128. Mein Compiler zeigt mir mittlerweile an, 
dass mein Speicher ziemlich voll sein sollte (vermute ich zumindest).
.bss ist 3420 groß und .data hat bereits 5107. Das sollte meinen Ram von 
4k um gut das doppelte sprengen, oder?

Ich habe des weiteren gelesen, dass Strings in printf oder in structs 
besser ins Flash gelegt werden sollen. Wenn ich mich auf diese beiden 
Threads beziehen, dann wird das nicht ohne große Änderungen am Programm 
möglich sein:
Beitrag "struct mit strings im flash unterbringen"
Beitrag "Strings im Flash"

Gibt es da noch eine besser Möglichkeit meine String-Variablen ins Flash 
zu legen?
Bzw. sollte das dann mit einer externen Ram-Erweiterung funktionieren? 
Kann das der Compiler schon bei der Generierung berücksichtigen?

von Εrnst B. (ernst)


Lesenswert?

Es gibt compiler, die das automatisch können, allerdings nur unter 
Verletzung des C-Standards (Pointer sind dann z.B. 3 Bytes lang, etc).

Der GCC hält sich lieber an den Standard, und überlässt das hantieren 
mit Flash und EEProm dem Programmierer.

von Sepp (Gast)


Lesenswert?

OK. Das ist ja schon mal eine Aussage.

Was genau sagt mir eigentlich .bss? Ich habe gelesen, dass dies die zur 
Laufzeit generierten Variablen darstellt. Sprich: Das ist nur ein 
Worst-Case Fall, oder? Ich werde ja nicht alle meine Variablen zur 
gleichen Zeit nutzen. Wenn ich diese in einer Funktion generiere, dann 
sind die beim Verlassen der Funktion ja wieder weg, oder?
Muß ich mich dann nur um .data kümmern, so dass dieser die 4k nicht 
überzeigt bzw noch Luft für die Daten von .bss lässt?!

von Εrnst B. (ernst)


Lesenswert?

.bss sind Variablen, die beim Programmstart mit Nullen überschrieben 
werden.
.data sind Variablen, die beim Programstart einen Startwert aus dem 
Flash bekommen.

Sind also beides nicht "Worst-Case" Abschätzungen, sondern der 
tatsächliche Bedarf.
Der Rest vom Ram geht für Stack (und Heap, wenn verwendet) drauf, HIER 
braucht man die Worst-Case Abschätzung, z.B. max. Rekursionstiefe.
Sonst kann dein Stack über die Variablen "wachsen" und diese 
überschreiben...

von Norgan (Gast)


Lesenswert?

> Was genau sagt mir eigentlich .bss?

http://en.wikipedia.org/wiki/.bss

von Oliver (Gast)


Lesenswert?

>Wenn ich mich auf diese beiden
>Threads beziehen, dann wird das nicht ohne große Änderungen am Programm
>möglich sein

So groß ist der Aufwand für die Strings doch gar nicht. Überall ein PSTR 
mit den zugehörigen Klammern eingefügt, dazu per replace die 
entsprechenden Funktionen durch ihre _P-Version ersetzt. Ist halt mal 
eine Stunde Fleißarbeit.

Was ich nicht nachvollziehen kann, ist, wie man so viel Code schreiben 
kann, ohne schon viel früher über das Problem zu stolpern.

Oliver

von Sepp (Gast)


Lesenswert?

1
Was ich nicht nachvollziehen kann, ist, wie man so viel Code schreiben
2
kann, ohne schon viel früher über das Problem zu stolpern.

Die Antwort ist einfach. Alle Teile des Gesamtcodes hab ich schon 
irgendwo mal genutzt/geschrieben. Jetzt brauche ich zufälligerweise alle 
diese Funktionen für ein neues Projekt, dessen Hardware noch nicht 
vorhanden ist. Um mir die Zeit tot zu schalgen mach ich was, was man 
eigentlich nicht sollte. Ich schreibe schon mal alles zusammen und mache 
einen BigBang Test mit der Hardware, wenn sie denn mal da ist. Was 
sicher nicht gut gehen wird :)

von Sepp (Gast)


Lesenswert?

PS: Das ganze ist nur so groß geworden, weil ich unmengen von Structs 
benutze wobei die meisten noch zu Struckarrays werden. Hab mal bei ein 
paar nachgerechnet. Da wird ein "kleines" Array schon mal 1,5k groß.

von Sepp (Gast)


Lesenswert?

1
.bss sind Variablen, die beim Programmstart mit Nullen überschrieben
2
werden.

Was mich wundert ist, dass Befehle wie z.B.
1
unsigned char inbuff[Filelength()];
2
Fread(inbuff,Filelength());
funktionieren wobei Filelength() zur Compilerzeit noch nicht definitv 
fest steht. Wie wird das dann da gehandhabt?

von Εrnst B. (ernst)


Lesenswert?

inbuff liegt auf dem Stack ("Automatic" Variable), d.H. es wird erst 
beim Funktionsaufruf bzw betreten des Scopes angelegt.
Die Größe muss zur Compilezeit also nicht feststehen.

von Sepp (Gast)


Lesenswert?

Wenn ich die Funktion verlasse wird aber der Speicher wieder frei 
gegeben, oder? Mit malloc() wirds dann wohl ähnlich sein.
Erscheinen solche Konstaten dann in .bss oder .data?

Tut mir leid wenn ich da ein bißchen nervend bin, aber mich 
interessierts eben :)

von Oliver (Gast)


Lesenswert?

Alle automatischen (=lokalen) Variablen, sowie alle per malloc 
reservierten Speicherbereiche werden auf dem stack bzw. heap zur 
Laufzeit reserviert und freigegeben. Die erscheinen nicht in .bss oder 
.data. Dort landen nur globale Variablen.

Oliver

von Εrnst B. (ernst)


Lesenswert?

ok, Beispiel, dann wirds hoffentlich klar:
1
uint8_t globale_variable1;
2
uint8_t goblale_variable2=42;
3
4
void func() {
5
  static uint8_t statisch1;
6
  static uint8_t statisch2=23;
7
  uint8_t automatisch1;
8
  uint8_t automatisch2=17;
9
10
  void * dynamisch;
11
12
  dynamisch=malloc(10);
13
}

So, was macht der Compiler draus?
Statische variablen werden wie globale behandelt, in der .data-Section 
landen also zwei Bytes, goblale_variable2 und statisch2.
Die Variablen ohne initialwert landen in .bss, das sind auch zwei Bytes,
globale_variable1 und statisch2.

Auf dem Stack landen die automatic-Variablen in func, ABER NUR bei 
Funktionsaufruf. D.h. am Anfang von func fügt der Compiler Code ein, der 
auf dem Stack 2+sizeof(void *) Bytes reserviert, einfach durch 
Verschieben des Stack-Pointers.
Ausserdem wird hier Code generiert, um "automatisch2" zu initialisieren.

Beim Verlassen der Funktion wird der Stackpointer wieder zurückgesetzt, 
dieser Speicher also wieder freigegeben.

der malloc-Aufruf reserviert Speicher vom Heap (bei AVR-GCC liegt der 
üblicherweise zwischen den fix allozierten Speicherbereichen und dem 
Stack). Bei Funktionsende wird dieser Speicher NICHT wieder automatisch 
freigegeben, das muss der Programmierer selber tun, mit "free"...

von Pete K. (pete77)


Lesenswert?

Ein Blick in das Mapfile kann vielleicht nicht schaden.

von Oliver (Gast)


Lesenswert?

Also wenn schon blicken, dann doch gleich RTFM. Da hat sich schließlich 
jemand viel Mühe gemacht, das alles aufzuschreiben.

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

Oliver

von daniel (Gast)


Lesenswert?

>>unsigned char inbuff[Filelength()];
>>Fread(inbuff,Filelength());

>>funktionieren wobei Filelength() zur Compilerzeit noch nicht definitv
>>fest steht. Wie wird das dann da gehandhabt?

>inbuff liegt auf dem Stack ("Automatic" Variable), d.H. es wird erst
>beim Funktionsaufruf bzw betreten des Scopes angelegt.
>Die Größe muss zur Compilezeit also nicht feststehen.

als Gegenargument könnte man einbringen, dass
in keine Funktion eher verzweigt wird, bevor stackframe
nicht eingerichtet ist. Genaudas würde aber FileLength machen.
Ich glaube Standard C = C89 hatte (bis C99) und c++
bis heute, die Bedingung dass Arraylängen compiletime constanten sind.
Wahrscheinlich aus diesem Grund, generell hast Du aber recht,
einfach zu berechnen sind sie schon .. nur halt mit dem Stackrahmen
kommt man in die Quere.
Wenn ich falsch liege, bitte berichtigt mich.

Grüsse,
    Daniel

von Εrnst B. (ernst)


Lesenswert?

Hab jetzt nicht nachgelesen, aber mal kurz mit dem GCC angetestet:
1
#include <stdint.h>
2
3
extern int Filesize();
4
extern void do_something(uint8_t *);
5
6
void func() {
7
  uint8_t test[Filesize()];
8
  do_something(test);
9
}

Beim Compilieren gibts ne Warning:
1
> avr-gcc -Os -Wall -pedantic -S test.c
2
test.c: In Funktion »func«:
3
test.c:7: Warnung: ISO-C90 verbietet Feld »test« variabler Größe

Die aber bei C99 weg ist:
1
> avr-gcc -Os -Wall -pedantic -std=c99 -S test.c

Im assembler kommt dann sowas raus:
1
        .file   "test.c"
2
__SREG__ = 0x3f
3
__SP_H__ = 0x3e
4
__SP_L__ = 0x3d
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
        .global __do_copy_data
8
        .global __do_clear_bss
9
        .text
10
.global func
11
        .type   func, @function
12
func:
13
/* prologue: frame size=0 */
14
        push r16
15
        push r17
16
        push r28
17
        push r29
18
        in r28,__SP_L__
19
        in r29,__SP_H__
20
/* prologue end (size=6) */
21
        in r16,__SP_L__
22
        in r17,__SP_H__
23
        rcall Filesize
24
        in r18,__SP_L__
25
        in r19,__SP_H__
26
        sub r18,r24
27
        sbc r19,r25
28
        in __tmp_reg__,__SREG__
29
        cli
30
        out __SP_H__,r19
31
        out __SREG__,__tmp_reg__
32
        out __SP_L__,r18
33
        in r24,__SP_L__
34
        in r25,__SP_H__
35
        adiw r24,1
36
        rcall do_something
37
        in __tmp_reg__,__SREG__
38
        cli
39
        out __SP_H__,r17
40
        out __SREG__,__tmp_reg__
41
        out __SP_L__,r16
42
/* epilogue: frame size=0 */
43
        pop r29
44
        pop r28
45
        pop r17
46
        pop r16
47
        ret
48
/* epilogue end (size=5) */
49
/* function func size 32 (21) */
50
        .size   func, .-func
51
/* File "test.c": code   32 = 0x0020 (  21), prologues   6, epilogues   5 */

Also speichert er sich den alten Stackpointer in r16,r17 zwischen, ruft 
Filesize auf, verringert den Stackpointer um den Rückgabewert (Mit 
gesperrten Interrupts), ruft do_something auf, und setzt dannach den 
Stackpointer auf die gespeicherten Werte zurück, wieder ohne Interrupts.

Nicht grad schöner Code der dabei rauskommt, funktionieren sollts aber.

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.