Archiv verlassen und diese Seite im Standarddesign anzeigen : Speicherfresser finden
Hallo RoboterNetz-Gemeinde,
ich bin vor kurzem bei einem Projekt eingestiegen, bei dem wir auf Basis der AT90CAN128 von Atmel entwickeln. Die haben 4kb SRAM. Da das Projekt schon etwas umfangreicher ist und ich nicht den vollen Überblick habe, suche ich nach einer Möglichkeit herauszufinden, an welchen Stellen im Code viel Arbeitsspeicher statisch belegt wird. Denn schon zur Compile-Zeit ist Data zu 90% voll, da bleibt nicht mehr viel übrig für dynamische Speicherbedarfe.
Gibt es ein Programm, das einem dabei hilft oder wie macht ihr das?
Besserwessi
20.12.2008, 18:29
Ein erster Ansatz ist es mal in das .map File zu sehen. Normalerweise steht da drin welche globale Variable wie viel Speicher braucht.
SprinterSB
22.12.2008, 00:15
avr-nm --size-sort -S xxx.elf
Zeigt die die Größen aller Symbole.
@SprinterSB wo muss man das obige eingeben?
Habe nämlich ein ähnliches Problem, bei mir ist der Speicher des ATmega8 mit folgendem Code schon zu 47% voll:
#include <avr/io.h>
#ifndef F_CPU
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert
(z.B. durch Übergabe als Parameter zum Compiler innerhalb
des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
"nachträgliche" Definition hinweist */
#warning "F_CPU war noch nicht definiert, wird nun mit 1000000 definiert"
#define F_CPU 1000000 /* Quarz mit 1 Mhz */
#endif
#include <util/delay.h> /* in älteren avr-libc Versionen <avr/delay.h> */
#define TEMPO 1800
#define TEMPO2 2000
int main (void)
{
DDRC = 0xFF;
int ba=0;
int const _A[] = {0b00001, 0b10110, 0b10110, 0b00001};
int const _H[] = {0b00000, 0b11011, 0b11011, 0b00000};
int const _L[] = {0b00000, 0b01111, 0b01111, 0b01111};
int const _O[] = {0b10001, 0b01110, 0b01110, 0b10001};
void B (int bb[])
{
PORTC = bb[0];
_delay_us(TEMPO);
PORTC = bb[1];
_delay_us(TEMPO);
PORTC = bb[2];
_delay_us(TEMPO);
PORTC = bb[3];
_delay_us(TEMPO);
PORTC = 0b11111;
_delay_us(TEMPO2);
ba += 8;
}
while(1)
{
B(_H);
B(_A);
B(_L);
B(_L);
B(_O);
_delay_ms(157 - ba);
ba = 0;
}
return 0;
}
@Jacob2:
Deine Konstanten belegen schon 128 Byte!
Eine Möglichkeit ist die Variablentypen immer so zu wählen, dass der Wert gerade rein passt, also die kleinst mögliche Variable. Bei 6 Bit wäre das unsigned char, der Speicherbedarf würde um Faktor 4 auf 32 Byte sinken.
Bei Variablen für Berechnungsergebnisse musst du gucken, dass das größtmögliche Ergebnis auch noch rein passt.
Oder gleich fest definieren, dann brauchst du dafür überhaupt keinen Speicherplatz.
Grus
Lorcan
wie fest definieren? mit #define? als int const?
ich habs doch gefunden, aber mir sagt das nichts was da steht!
Ich habs jetzt so umgeschrieben:
const unsigned char _A[] = {0b00001, 0b10110, 0b10110, 0b00001};
const unsigned char _H[] = {0b00000, 0b11011, 0b11011, 0b00000};
const unsigned char _L[] = {0b00000, 0b01111, 0b01111, 0b01111};
const unsigned char _O[] = {0b10001, 0b01110, 0b01110, 0b10001};
allerdings hat mir das höchstens 1% gebracht
SprinterSB
23.12.2008, 11:17
@SprinterSB wo muss man das obige eingeben?
In einer shell/Konsole/Eingabeaufforderung.
Du kannst aber auch ein neues Target im Makefile hinzufügen, wenn du lieber mit make arbeitest.
Das Problem ist jetzt, dass ich noch 22 weitere solcher Variablen unterzubringen hätte! Ich hab leider keinen anderen Controller zur Hand ](*,)
Der Speicherstand sieht momentan so aus:
Program: 3830 bytes (46.8% Full)
(.text + .data + .bootloader)
Data: 280 bytes (27.3% Full)
(.data + .bss + .noinit)
Wie könnte man noch Platz sparen?
Hi!
Was auch sehr viel Platz braucht, sind Funktionsaufrufe, wenn die Aufruftiefe größer ist. Jeder Funktionsaufruf muss Register und die Rücksprungadresse auf dem Stack sichern.
Für die nächsten beiden Vorschläge werden mich meine Kollegen der Softwareentwicklung gleich steinigen, weil Code unleserlich wird oder böse Stolperfallen bereits halten kann:
1. Kleine Funktionen als Präprozessor-Makros schreiben. Ist für Funktionen gut, die z.B. Ports setzen oder auswerten. Ergebnis: Mehr Speicherbedarf durch Code, aber weniger durch Stack.
2. Funktionen, die nur zum Strukturieren des Codes eingeführt wurden rauswerfen und alles als einen Codeblock schreiben. Ergebnis: Etwas weniger Code, da Aufrufe wegfallen, weniger Stackbedarf. Der Code wird jedoch schwerer lesbar.
Auf die beiden Methoden greife ich bei meinen Projekten jedoch nur zurück, wenn's wirklich nicht mehr anders geht. Lieber greife ich zu einem größeren Controller.
Gruß,
Markus
SprinterSB
23.12.2008, 23:34
1. Kleine Funktionen als Präprozessor-Makros schreiben. Ist für Funktionen gut, die z.B. Ports setzen oder auswerten. Ergebnis: Mehr Speicherbedarf durch Code, aber weniger durch Stack.
Wenn man es richtig mach, braucht man weniger Code :-) Nämlich nur zuviel, wie eben notwendig ist.
2. Funktionen, die nur zum Strukturieren des Codes eingeführt wurden rauswerfen und alles als einen Codeblock schreiben. Ergebnis: Etwas weniger Code, da Aufrufe wegfallen, weniger Stackbedarf. Der Code wird jedoch schwerer lesbar.
Heutzutage Unsinn. Eine Funktion, die (statisch) nur 1x verwendet wird, macht man natürlich static anstatt extern. Und ein Compiler wie GCC wird die ohne groß Aufhebens zu machen inlinen. Und dies entspricht dann im Endeffekt genau deinem Spaghetti-Code...
Bei neuen avr-gcc-Versionen ist es allerdings noch weniger durchsichtig, wie er den erzeugten Code zusammenbaut. Immerhin gibt es weit über 100 Passes, und in jedem wird der Code nochmal durch die Mangel gedreht...
In meinen Projekten bekomm ich mit avr-gcc 3.4.6 rund 10% kleineren (und entsprechend auch schnelleren) Code als mit avr-gcc 4.x. :-)
SprinterSB
23.12.2008, 23:50
@Jacob2: Es ist schlecht, wenn du diese Tabellen als lokale Variablen anlegst, denn so belegen sie Flash /und/ RAM und werden beim Betreten der Funktion erst initialisiertm was Code und Zeit kostet.
Ausserdemm sollte vermieden werden, lokale Funktionen zu schreiben, also Funktionen die innerhalb einer anderen definiert werden.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#ifndef F_CPU
/* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert
(z.B. durch Übergabe als Parameter zum Compiler innerhalb
des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
"nachträgliche" Definition hinweist */
#warning "F_CPU war noch nicht definiert, wird nun mit 1000000 definiert"
#define F_CPU 1000000 /* Quarz mit 1 Mhz */
#endif // F_CPU
#define TEMPO 1800
#define TEMPO2 2000
static uint8_t const _A[] PROGMEM = {0b00001, 0b10110, 0b10110, 0b00001};
static uint8_t const _H[] PROGMEM = {0b00000, 0b11011, 0b11011, 0b00000};
static uint8_t const _L[] PROGMEM = {0b00000, 0b01111, 0b01111, 0b01111};
static uint8_t const _O[] PROGMEM = {0b10001, 0b01110, 0b01110, 0b10001};
void B (uint8_t bb[]);
void B (uint8_t bb[])
{
PORTC = pgm_read_byte (& bb[0]);
_delay_us (TEMPO);
PORTC = pgm_read_byte (& bb[1]);
_delay_us (TEMPO);
PORTC = pgm_read_byte (& bb[2]);
_delay_us (TEMPO);
PORTC = pgm_read_byte (& bb[3]);
_delay_us (TEMPO);
PORTC = 0b11111;
_delay_us(TEMPO2);
}
int main (void)
{
DDRC = 0xFF;
while(1)
{
B(_H);
B(_A);
B(_L);
B(_L);
B(_O);
_delay_ms (157);
}
}
i) Die Funktion ist nicht mehr lokal
ii) Für die Konstanten reichen 8 Bit, sie brauchen kein int (16 Bit) zu sein.
iii) Die Konstanten bleiben in Flash und verbrauchen kein RAM (PROGMEM). Allerdings muss anders auf sie zugegriffen werden (pgm_read_...)
iv) Das Argument dieser delay-Funktionen sollte eine Compilezeit-Constante sein, weil sonst der Compiler anfängt (float)-Berechnungen zu machen. Das kostet krass Code!!! DOKUS LESEN!!!
v) Code ist ohne Garantie, hab's nur eingetippst und nicht getestet.
thewulf00
30.12.2008, 11:19
@Jacob2:
Deine Konstanten belegen schon 128 Byte!
Kannst Du mir das mal vorrechnen? Soll keine Kritik sein, ich verstehs nur nicht, ich komme nur auf ca. 32 Bytes.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.