An diesem Artikel arbeitet gerade Mitglied SprinterSB.
Am besten momentan noch keine gravierenden Ergänzungen / Änderungen vornehmen. Dieser Hinweis verschwindet wenn der Autor soweit ist. Sollte dieser Hinweis länger als drei Tage auf einer Seite sein, bitte beim Autor SprinterSB per PM / Mail oder Forum nachfragen ob er vergessen wurde. |
Der Speicherverbrauch eines Programmes lässt sich unter verschiedenen Gesichtspunkten betrachten.
- Ort der Datenablage
- SRAM
- Flash
- EEPROM
- Art der Daten
- Programmcode
- Daten
- veränderlich/unveränderlich
- persistent oder flüchtig
- Speicherklasse
- Statischer Speicherverbrauch
- Dynamischer Speicherverbrauch
Der Ort der Datenablage wird bestimmt von der Art der Daten. Daten, die nicht verändert werden dürfen oder nicht verändert werden müssen, wie der Programm-Code oder konstante Strings, wie sie oft zur Ausgabe verwendet werden, können im Flash gespeichert werden. Konstanten/Tabellen kann man auch im EEPROM speichern, wenn der Platz im Flash knapp ist. Daten, die zur Laufzeit verändert werden müssen, wird man im SRAM speichern, wenn sie einen Reset bzw. ein Ausschalten des Controllers nicht überleben müssen. Sollen die Daten auch ohne Strom erhalten bleiben, muss man den EEPROM oder den Flash als Ablageort wählen, wobei die Ablage im Flash sehr aufwändig ist, weil man einen Bootloader für ihre Änderung anstrengen muss.
Die Art der Daten ergibt sich aus dem Programm und den zu lösenden Aufgaben.
Gleiches gilt für die Speicherklasse. Die Ablageorte der statische Daten sind bereits zur Compilezeit bekannt. Hierzu gehören globale und statische Variablen. Dementsprechend ist auch schon zur Compile- bzw. Linkzeit bekannt, wieviel Speicher diese Daten belegen. Speicherorte und Platzverbrauch dynamischer Variablen sind dem Compiler nicht bekannt. Sie ergeben sich erst zur Laufzeit durch das Allokieren von Speicher mit malloc, oder durch den Aufbau eines Stapels, auf dem lokale Variablen gesichert werden, während eine Funktion aufgerufen wird. Je nach Verschachtelungstiefe der Funktionsaufrufe — dazu gehören auch Interrupt Service Routinen, die prinzipiell jederzeit aufgerufen werden können — wird dafür auch unterschiedlich viel Speicher benötigt.
Flash- und statischer RAM-Verbrauch
Das geht nicht mit avr-gcc, sondern mit Werkzeugen, die zu den Binutils gehören und z.B bei WinAVR dabei sind.
Abhängig von der Section schlägt ihr Platzverbrauch in Flash/SRAM/EEPROM zu Buche:
belegter Speicher | Sections (Einzelgrößen addieren) | Beschreibung |
---|---|---|
Flash | .text + .data | Programmcode und Tabelle für initialisierte Daten |
SRAM | .data + .bss + .noinit | Daten (initialisiert, zu 0 initialisiert, nicht initialisiert) |
EEPROM | .eeprom | Daten, die man ins EEPROM gelegt hat |
Beispiele:
Verbrauch der einzelnen Module auflisten:
> avr-size -x foo1.o foo2.o ...
Verbrauch des gesamten Programms auflisten:
> avr-size -x -A foo.elf
Platzverbrauch von Funktionen, Objekten, etc. nach Größe sortiert:
> avr-nm --size-sort -S foo.elf
Dynamischer RAM-Verbrauch
- mem-check.h
#ifndef _MEM_CHECK_H_ #define _MEM_CHECK_H_ extern unsigned short get_mem_unused (void); #endif /* _MEM_CHECK_H_ */
- mem-check.c
#include <avr/io.h> // RAMEND #include "mem-check.h" // Mask to init SRAM and check against #define MASK 0xaa // From linker script extern unsigned char __heap_start; unsigned short get_mem_unused (void) { unsigned short unused = 0; unsigned char *p = &__heap_start; do { if (*p++ != MASK) break; unused++; } while (p <= (unsigned char*) RAMEND); return unused; } /* !!! never call this function !!! */ void __attribute__ ((naked, section (".init8"))) __init8_mem (void) { __asm volatile ( "ldi r30, lo8 (__heap_start)" "\n\t" "ldi r31, hi8 (__heap_start)" "\n\t" "ldi r24, %0" "\n\t" "ldi r25, hi8 (%1)" "\n" "0:" "\n\t" "st Z+, r24" "\n\t" "cpi r30, lo8 (%1)" "\n\t" "cpc r31, r25" "\n\t" "brlo 0b" : : "i" (MASK), "i" (RAMEND+1) ); }