Forum: Compiler & IDEs double-Gebrauch verpfuscht RAM?


von Zotteljedi (Gast)


Lesenswert?

Hi,

ich habe hier gerade ein merkwürdiges Problem mit einem ATmega8. Es geht 
um PWM und die Ausgabe von Sinus-Schwingungen. Ich weiß, daß man die 
Werte "offline" ausrechnen und im Flash versenken kann, aber ich will im 
Moment das Problem verstehen.

Ich habe ca. 450 Byte RAM mit globalen Variablen voll ('n dickes Array, 
int8_t[435], und etwas Kleinzeug). Nun scheint der Gebrauch von 
double-Variablen und der sin-Funktion einen Teil des Arrays zu 
überschreiben, jedenfalls steht da gegen Ende kranker Kram drin, den ich 
nicht reingeschrieben habe. Wenn ich das Array kleiner mache, geht's. 
Wenn ich den double-Kram rausnehme, geht's.

Ich habe mir nix dazu ergoogeln können, wie der avr-gcc das mit den 
Doubles auf dem AVR hinbekommt. Daß da ein fetter Batzen Code dranhängt 
sehe ich (das Programm hat 4 KB, ohne double-Kram was um 400 Byte). Aber 
braucht der auch RAM von der Größenordnung eines halben Ks? Und wieso 
(falls das der Grund ist) zerballert er dabei meine Variablen? Das ist 
nicht nett! ;-)

Code kann ich im Moment nicht liefern, der sieht noch etwas chaotisch 
aus, aber ich habe etliche Stunden mit dem Vieh verbracht, und bin mir 
ziemlich sicher, daß in dem verbliebenen Code nix mehr begraben liegt 
(zeilenweise mit #ifdef durchgekaut...), kann ihn aber in den nächsten 
Tagen nachliefern, falls nötig.

Nur um nochmal sicher zu gehen: von dem 1 KB SRAM des Mega8 steht mir 
doch auch 1 KB zu, abzüglich Stack latürnich, oder?

Gruß, Felix

von Karl H. (kbuchegg)


Lesenswert?

Was du da beschreibst, klingt stark nach einem Stack-Overflow.

von Skasko (Gast)


Lesenswert?

Aloha,

hier gibts eine kleine Routine, mit der Du das prüfen kannst:

http://www.roboternetz.de/wissen/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc

von Zotteljedi (Gast)


Lesenswert?

Ich habe die Routine mal ausprobiert, in einem frischen Projekt. Und 
siehe da, von 1 KB SRAM lässt sich nur die Hälfte verwenden. Das erklärt 
natürlich alles.

Allerdings frage ich mich, warum das so ist. Hier die Ausgabe:

$ avr-size -x -A mem_demo.elf
mem_demo.elf  :
section     size       addr
.text      0x10e        0x0
.data        0x0   0x800060
.bss         0x1   0x800060
.noinit      0x0   0x800061
.eeprom      0x0   0x810000
.stab      0x36c        0x0
.stabstr    0x54        0x0
Total      0x4cf

und in meinem Programm steht

--------------------------------
    free = get_mem_unused();

    if (free < 500)
        zappel();
--------------------------------

wobei zappel() mit PB0 wackelt, was ich am Oszi sehen kann. Wenn ich 
jetzt noch ein paar Variablen definiere, schlägt's zu. Wenn man also 
großzügig auf die nächste 2er-Potenz aufrundet, ergibt sich daß nur 512 
Byte RAM nutzbar sind, obwohl 1024 Byte vorhanden wären. Wozu ist das 
gut?

Gruß, Felix

von Karl H. (kbuchegg)


Lesenswert?

Na ja.
Der Stack braucht ein paar Bytes.
Und für dynamische Speicherallokierung (malloc, free)
muss auch noch etwas reserviert werden.

Insgesamt hast du ja im SRAM 3 Bereiche

   +------------+---------------+--------+
   | statische  | dynamische    | Stack  |
   | Variablen  | Allokierungen |        |
   +------------+---------------+--------+

Irgendwann krachts da halt.

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


Lesenswert?

Wobei 500 Byte Stackverbrauch schon auf eine, sagen wir mal
großzügige Nutzung lokaler Variablen hin deuten.

von Peter D. (peda)


Lesenswert?

Zotteljedi wrote:

> großzügig auf die nächste 2er-Potenz aufrundet, ergibt sich daß nur 512
> Byte RAM nutzbar sind, obwohl 1024 Byte vorhanden wären. Wozu ist das
> gut?

Riecht verdächtig stark nach dem bekannten "Konstanten sind per default 
im SRAM"-Problem.


Der AVR-GCC will leider für Konstanten ne extra Syntax haben, damit sie 
im Flash und nicht im SRAM landen.
Das macht auch konstante Strukturen besonders haarig (d.h. geht 
garnicht).

Bei kleineren Prohjekten lasse ich ihm daher seinen Willen und versuche 
nicht zu überschwenglich lange Texte zu schreiben.


Peter

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


Lesenswert?

Peter Dannegger wrote:

> Riecht verdächtig stark nach dem bekannten "Konstanten sind per default
> im SRAM"-Problem.

Nein.  Guck dir mal die Ausgabe von avr-size an: .data ist leer.

von Zotteljedi (Gast)


Angehängte Dateien:

Lesenswert?

@Jörg: beim ursprünglichen Programm liegt in der Tat viel auf dem Stack, 
aber bei dem, zu dem die avr-size-Ausgabe gehört, liegt quasi nichts 
drauf, trotzdem habe ich nur ca. 500 Byte Platz.

Quellen hängen an. In dem Zustand wird zappel() aufgerufen, wenn ich 
eine Variable wegnehme nicht mehr, das ist genau die Grenze, also ca. 
500 Byte modulo etwas Kleinkram durch die Funktionen. Von 1 KB kann da 
nicht die Rede sein...

@Karl Heinz: ich weiß, aber wenn kein malloc stattfindet, und der Stack 
"frisch" ist, können Variablen im Wert von 10 Byte auf Stack und Heap 
nicht alles bis auf 500 Byte aufmampfen. Ich fände es auch leicht 
beknackt, wenn Platz für mallocs reserviert werden würde, wenn im ganzen 
Programm kein malloc drin ist, und der Compiler das ja auch weiß.


Es ist ja nicht so, daß ich nicht einsehen wollte, daß man mit RAM 
sparsam umgehen sollte (inzwischen kommt das Zeug eh alles offline 
berechnet ins Flash rein). Nur wundere ich mich, daß ich an die Hälfte 
des ohnehin knappen RAMs nicht dran darf.

Gruß, Felix

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


Lesenswert?

Umm, der war wirklich fies!  Das kostet 'n Bier, Felix.

Ich wollte das Ganze debuggen und hab's daher auf einem Controller
ausführen wollen, der debugWire kann.  Da ich gerade nur einen
ATmega48 (keinen 88) hatte, OK, dafür genügt der ja auch.  Irgendwie
wollte aber das AVaRICE bzw. der AVR Dragon nicht recht glücklich
werden.  Alles funktionierte normal bis zu diesem Hack, der da den RAM
zumüllen soll.  Danach wollte der Dragon irgendwie mit dem AVaRICE
nicht mehr reden.

Da habe ich mir das __init8_mem() mal angeguckt, auf Anhieb auch
nichts gefunden, bis ich dann irgendwie über die RAM-Adressen gestutzt
habe.  Der Stackpointer wurde auf 0x25f initialisiert, aber der RAM
bis < 0x300 beschrieben...  Ja, der ATmega48 hat wirklich seinen RAM
von 0x100 bis 0x2ff, aber warum dann 0x25f?

Da ging mir der Seifensieder auf: du hast das -mmcu beim Linken
weggelassen!

Schade, sonst habe ich sowas nicht erst nach 'ner Stunde Arbeit
gemerkt wie hier.

Übrigens gibt's keinen Grund für diesen schrägen inline-asm-Hack,
das geht in C genauso gut:
1
void __attribute__ ((naked, section (".init8")))
2
__init8_mem (void)
3
{
4
   uint8_t *cp = &__heap_start;
5
   do
6
     {
7
       *cp++ = MASK;
8
     }
9
   while (cp < (uint8_t *)RAMEND);
10
}

Der resultierende Code ist ähnlich kompakt wie das unverständliche
inline-Kauderwelsch. ;-)

p.s.: Es sind natürlich nur 3 Byte RAM in Benutzung: eins für die
Variable a am unteren Ende und zwei für die eine Ebene an
Rückkehradressen am oberen Ende des RAMs.

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.