Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter fuer schwierige und grosse Gaerten im Test

K (Nutzung des SRAM durch avr-gcc)
(Dynamischer RAM-Verbrauch)
Zeile 68: Zeile 68:
  
 
=Dynamischer RAM-Verbrauch=
 
=Dynamischer RAM-Verbrauch=
 +
 +
Mit dieser kleinen Routine kann der noch freie SRAM-Bereich bestimmt werden. Es wird allerdings nicht der momentan freie Speicher bestimmt, sondern das Minimum an Speicher, das bis dato frei geblieben ist. Dazu wird im Startup-Code das Muster <tt>0xaa</tt> in den SRAM geschrieben. Durch Aufruf der Funktion <tt>get_mem_unused</tt> wird bestimmt, wieviel von diesem Muster noch intakt ist.
 +
 +
Um den momentan freien Speicher zu bestimmen, zieht man einfach den Anfang des Heaps vom Stackpointer ab:
 +
<pre>
 +
#include <avr/io.h>
 +
 +
// From linker script
 +
extern unsigned char __heap_start;
 +
  ...
 +
  uint16_t momentan_frei = SP - (uint16_t) &__heap_start;
 +
</pre>
 +
 +
Interessanter ist es jedoch, den Maximalverbrauch an Speicher zu kennen, bzw das Minimum an freiem Speicher, also den worst case zu kennen.
  
 
;<tt>mem-check.h</tt>
 
;<tt>mem-check.h</tt>

Version vom 23. März 2006, 18:07 Uhr

Baustelle.gif 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.

Nutzung des SRAM durch avr-gcc

RAM-Layout für ein AVR mit 1kByte SRAM

avr-gcc legt die Daten in Sections an. Nach aufsteigenden Speicheradressen sortiert sind dies:

.data
Statische und (modul-)globale, initialisierte Daten, denen man per Initializer einen Wert ungleich 0 zuweist. Beginnt an er unteren SRAM-Adresse 0x60 nach dem SFR-Bereich.
.bss
Statische und (modul-)globale, initialisierte Daten, die zu 0 initialisiert sind bzw. keinen Initializer haben (und also auch zu 0 initialisiert werden).
.noinit
Statische und (modul-)globale Daten, die nicht vom Startup-Code initialisiert werden.
Heap
Danach folgt der Heap. Das ist ein Speicherbereich, aus dem Speicherplatz via malloc etc allokiert wird.
Stack
Aus dem Stapel werden lokale Variablen/Register während Funktionsaufrufen gesichert. Der Stack wird auch zur Parameterübergabe verwendet und die return-Adresse von Funktionen und ISRs wird dort abgelegt. Der Stack beginnt an der oberen SRAM-Adresse und wächst nicht wie die andern Bereiche nach oben, sondern er wächst nach unten.

Die Größe der ersten drei Speicherbereiche kann man bereits zur Compile-Zeit bestimmen, da sie unabhängig ist von der Programmausführung. Wie viel Platz die letzten beiden Bereiche brauchen, ergibt sich erst zur Laufzeit des Programms. Diese Größen ändern sich in aller Regel mit der Zeit.

Flash- und statischer RAM-Verbrauch

Zur Bestimmung des Speicherplatzes, den statische Daten belegen, verwendet man avr-size, das zu den Binutils gehört und z.B. bei WinAVR dabei ist.

Abhängig von der Section schlägt ihr Platzverbrauch in Flash/SRAM/EEPROM zu Buche:

Tabelle: Zuordung des Platzberbrauchs zur Section-Größe
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:

Das '>' nicht mittippen, es ist der Kommandozeilen-Prompt.

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

Mit dieser kleinen Routine kann der noch freie SRAM-Bereich bestimmt werden. Es wird allerdings nicht der momentan freie Speicher bestimmt, sondern das Minimum an Speicher, das bis dato frei geblieben ist. Dazu wird im Startup-Code das Muster 0xaa in den SRAM geschrieben. Durch Aufruf der Funktion get_mem_unused wird bestimmt, wieviel von diesem Muster noch intakt ist.

Um den momentan freien Speicher zu bestimmen, zieht man einfach den Anfang des Heaps vom Stackpointer ab:

#include <avr/io.h>

// From linker script
extern unsigned char __heap_start;
   ...
   uint16_t momentan_frei = SP - (uint16_t) &__heap_start;

Interessanter ist es jedoch, den Maximalverbrauch an Speicher zu kennen, bzw das Minimum an freiem Speicher, also den worst case zu kennen.

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)
	);
}

LiFePO4 Speicher Test