Forum: Mikrocontroller und Digitale Elektronik Globales Struct/Array nicht automatisch initialisieren


von Matthias N. (nippey)


Lesenswert?

Hi,

ich habe eine Struktur für einen Fifo
1
typedef struct {
2
  char buf[FIFO_SIZE];
3
  uint8_t writePos, readPos;
4
} fifo_t;

Wenn ich nun eine globale Variable erstelle, so wird das resultierende 
Programm unnötig groß (abhängig von FIFO_SIZE). Jetzt wollte ich eine 
automatische Initialisierung des internen Arrays unterbinden indem ich 
die Struktur in die .noinit section verfrachte:
1
fifo_t rxFifo __attribute__ ((section (".noinit")));

Aber der Erfolg bleibt aus.
Wisst ihr, wie das richtig geht? ;)

Das mit dem .noinit habe ich auf dieser Seite gefunden:
http://www.rn-wissen.de/index.php/Avr-gcc

von Stefan E. (sternst)


Lesenswert?

Matthias N. schrieb:
> Wenn ich nun eine globale Variable erstelle, so wird das resultierende
> Programm unnötig groß (abhängig von FIFO_SIZE).

Nein. Die Größe des Programms ändert sich dadurch überhaupt nicht.

Matthias N. schrieb:
> Aber der Erfolg bleibt aus.

Ja, weil es eben nichts einzusparen gibt.

von Matthias N. (nippey)


Lesenswert?

Bin direkt nochmal zurück zu AVR Studio 5 und habe folgendes 
durchprobiert

Debug (No Optimisation):
#define FIFO_SIZE 64
Program: 3484 bytes  Data: 150 bytes
#define FIFO_SIZE: 32
Program: 3420 bytes  Data: 86 bytes

DiffProg: 64 bytes DiffData: 64 bytes


Release (Optimise for size):
#define FIFO_SIZE 64
Program: 2618 bytes  Data: 150 bytes
#define FIFO_SIZE 32
Program: 2580 bytes  Data: 86 bytes

DiffProg: 64 bytes DiffData: 64 bytes

Entspricht jeweils der zweifachen Instantiierung von fifo_t

Und die einzige Stelle wo sich durch das #define effektiv was ändert ist 
diese hier:
1
fifo_t
2
  rxFifo __attribute__ ((section (".noinit"))),
3
  txFifo __attribute__ ((section (".noinit")));

Somit müssen ja anscheinend Daten generiert werden, die im Zusammenhang 
mit der Initialisierung des Arrays stehen.

Wie lässt sich das unterbinden?

von Noname (Gast)


Lesenswert?

Hm. Mag sein, dass ich da was falsch einschätze, aber ich hätte auch 
keine Änderung bei der Programmgrösse erwartet.

Vergleich doch mal die Map bzw. Listfiles. Dann sollte klarwerden, was 
da passiert.

Am besten ein Minimalbeispiel machen, das diese Verhalten hat. Also die 
Variable plus ein wenig Code der das manipuliert. Dann können wir das 
bei uns evtl. nachvollziehen.

von Matthias N. (nippey)


Lesenswert?

Ich habe mal irgendwo aufgeschnappt, dass globale variablen in c immer 
initialisiert werden. Man müsste es nur ausschalten können.
Hier der Vollständigkeit halber meine Versuche und mein Ergebnis daraus:

GLOBAL:
1
#define SIZE  24
2
char buf[SIZE];
3
int main(void) { }

Program 116 (konstant), Data 24 (entspricht SIZE)

LOKAL:
1
#define SIZE  24
2
int main(void) {
3
   volatile char buf[SIZE];
4
}

Program 120 (konstant), Data 0 (konstant)

WORKAROUND für Gloablen Buffer:
1
#define SIZE  24
2
volatile char *bufp;
3
int main(void) {
4
   volatile char buf[SIZE];
5
   bufp = buf;
6
}

Program 148 (konstant), Data 2 (konstant, entspricht Pointer-Grösse)

Die Variable liegt ab dem Eintritt in main() auf dem Stack und ist somit 
gültig, solange die Routine, die sie initialisiert hat nicht wieder 
verlassen wird.
Nicht schön, spart mir aber jetzt den benötigten Platz ein

von Rolf M. (rmagnus)


Lesenswert?

Matthias N. schrieb:
> Ich habe mal irgendwo aufgeschnappt, dass globale variablen in c immer
> initialisiert werden.

Ja, aber wenn nichts explizites angegeben ist, mit 0. Und 
Nullinitialisierte Variablen landen in der BSS-Sektion, die dann im 
Startup-Code einfach nach memset-Manier mit 0 gefüllt wird. Daher 
sollten deine Fifos eigentlich keinen zusätzlichen Flash-Speicher 
verbrauchen. Es bleibt rätselhaft, warum das bei dir nicht funktioniert.

> GLOBAL:
> #define SIZE  24
> char buf[SIZE];
> int main(void) { }
>
> Program 116 (konstant), Data 24 (entspricht SIZE)
>
> LOKAL:
> #define SIZE  24
> int main(void) {
>    volatile char buf[SIZE];
> }
>
> Program 120 (konstant), Data 0 (konstant)

Also braucht das globale Array in diesem Fall keinen zusätzlichen 
Speicher. Es scheint also nur dann nicht zu funktionieren, wenn du dein 
Array in eine Struktur packst.

> WORKAROUND für Gloablen Buffer:
> #define SIZE  24
> volatile char *bufp;
> int main(void) {
>    volatile char buf[SIZE];
>    bufp = buf;
> }
>
> Program 148 (konstant), Data 2 (konstant, entspricht Pointer-Grösse)

Wozu soll das dann gut sein? Mehr Flash- und mehr RAM-Verbrauch.

von Matthias N. (nippey)


Lesenswert?

Rolf Magnus schrieb:
> Also braucht das globale Array in diesem Fall keinen zusätzlichen
> Speicher. Es scheint also nur dann nicht zu funktionieren, wenn du dein
> Array in eine Struktur packst.

Das ist dort aber nicht mehr global, sondern lokal in main() deklariert.

> Wozu soll das dann gut sein? Mehr Flash- und mehr RAM-Verbrauch.

Der entsteht durch den Code für den Kopiervorgang der Arrayadresse auf 
den globalen Pointer.

von holger (Gast)


Lesenswert?

>Das ist dort aber nicht mehr global, sondern lokal in main() deklariert.

Und volatile. Sehr sinnvoll in main() ;)

von Holger Sch (Gast)


Lesenswert?

OK, Profi Rolf war schneller, ich versuch's trotzdem...


Hallo Matthias,

ich mache eigentlich in Hardware, Software nur nebenbei, aber:
ich glaube Du bist auf der falschen Schiene.
Mit:

"fifo_t rxFifo"

wird die variable "rxFifo" in ihrer von Dir definierten Größe im RAM 
angelegt, basta!

Wenn Du nun sagst:

"__attribute__ ((section (".noinit")))"

wird diese Variable in einen Speicherbereich verfrachtet, der nicht vom 
Compiler explizit mit 0x00 vordefiniert wird; diese hat einen zufälligen 
Wert der sich nach "Power On" zufällig einstellt.
"noinit" hat nix mit der Größe zu tun.

Kurzum, Du möchtest eine zur Laufzeit dynamische Variable,
hier ist Dein Stichwort: malloc (viel Spass).


*** hab gerade nochmal Deinen neuen Beitrag gelesen und sinniert.....

"resultierende Programm unnötig groß" ist natürlich so nicht richtig.
Dein RAM-Verbrauch ist dem entsprechend groß.
Na und ? wenn's reinpasst ? wurscht egal.

"Die Variable liegt ab dem Eintritt in main() auf dem Stack und ist 
somit
gültig, solange die Routine, die sie initialisiert hat nicht wieder
verlassen wird.
Nicht schön, spart mir aber jetzt den benötigten Platz"

Trugschluß, Variablen werden ab "RAMSTART" draufgelegt, der Stack geht 
von "RAMEND" runter!!!  NIX GESPART.

Gruß Holger Sch

von Oliver (Gast)


Lesenswert?

Erzeuge die assembler-listings für beide Versionen, und schau dir die 
Unterschiede an. Danach weißt du, woher der zusätzliche Speicherbedarf 
kommt.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Matthias N. schrieb:
> Rolf Magnus schrieb:
>> Also braucht das globale Array in diesem Fall keinen zusätzlichen
>> Speicher. Es scheint also nur dann nicht zu funktionieren, wenn du dein
>> Array in eine Struktur packst.
>
> Das ist dort aber nicht mehr global, sondern lokal in main() deklariert.

Du meinst da wo "GLOBAL" drübersteht?
Das hier:

Matthias N. schrieb:
> GLOBAL:
> Program 116 (konstant), Data 24 (entspricht SIZE)

> LOKAL:
> Program 120 (konstant), Data 0 (konstant)

sagt doch aus, daß die globale Version nicht mehr Speicher braucht als 
die lokale. Daß Data 24 Byte größer ist, liegt ja nur daran, daß die 
Variable nicht wie bei der lokalen Version auf dem  Stack liegt. 
Benötigt werden die 24 Bytes natürlich in beiden Fällen,nur bei der 
lokalen Version werden sie nicht angezeigt, weil die Stackbelegung zur 
Compilezeit nicht bekannt ist.

von Matthias N. (nippey)


Lesenswert?

Ahh, meine Anfangsformulierung war somit schon sehr ungüntig ;)

Klar, dass die Variable so oder so den benötigten RAM braucht, aber es 
ging mir darum, dass der im Flash liegende Code unnötig aufgeblasen wird 
;)

Somit, danke für die nachfolgenden Hinweise, die mir mal wieder zeigen, 
dass ich meine Fragen klarer formulieren sollte ;)

Wenn es um den reinen Flashbedarf geht, dann hat der Workaround schon 
hin.

holger schrieb:
> Und volatile. Sehr sinnvoll in main() ;)

Nunja, habe ich geschrieben, damit die Optimierung mir nicht 
reinquatscht ;)

von Stefan E. (sternst)


Lesenswert?

Matthias N. schrieb:
> Wenn es um den reinen Flashbedarf geht, dann hat der Workaround schon
> hin.

Du hast doch in deinem eigenen Test selbst gezeigt, dass das nicht 
stimmt:
> GLOBAL:
> Program 116 (konstant), Data 24 (entspricht SIZE)
> LOKAL:
> Program 120 (konstant), Data 0 (konstant)

"Program" (und nur das) ist hier dein Flashbedarf.

von Karl H. (kbuchegg)


Lesenswert?

Matthias N. schrieb:

> Klar, dass die Variable so oder so den benötigten RAM braucht, aber es
> ging mir darum, dass der im Flash liegende Code unnötig aufgeblasen wird
> ;)
>
> Somit, danke für die nachfolgenden Hinweise, die mir mal wieder zeigen,
> dass ich meine Fragen klarer formulieren sollte ;)
>
> Wenn es um den reinen Flashbedarf geht, dann hat der Workaround schon
> hin.

Nicht .... wirklich.

Das wird alles mit 0 initialisiert. Der Linker sammelt alle diese 
Variablen im Speicher und wenn das Programm hochfährt, kommt eine 
Schleife zum Zug die diesen Speicher einfach mit 0 niederbügelt. Fertig. 
Wenn du mehr 0-initialisierte Variablen hast, dann ändert das nicht 
viel. Es wird einfach nur mehr Speicher mit 0 niedergebügelt, sprich die 
Schleife hat einen anderen Endwert. Mehr passiert da nicht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich kann das Problem nicht nachvollziehen:
1
#include <stdint.h>
2
3
typedef struct {
4
  char buf[FIFO_SIZE];
5
  uint8_t writePos, readPos;
6
} fifo_t;
7
8
fifo_t rxFifo, txFifo;
9
10
int main(void) {
11
  return 0;
12
}

Mit Puffergröße 32 Bytes:
1
$ avr-gcc-4.7.0 -Os -DFIFO_SIZE=32 -mmcu=atmega8 -o avrtest avrtest.c
2
$ avr-size -A avrtest
3
avrtest  :
4
section    size      addr
5
.text        82         0
6
.bss         68   8388704
7
.stab      1944         0
8
.stabstr    156         0
9
.comment     41         0
10
Total      2291

Mit Puffergröße 64 Bytes:
1
$ avr-gcc-4.7.0 -Os -DFIFO_SIZE=64 -mmcu=atmega8 -o avrtest avrtest.c
2
$ avr-size -A avrtest
3
avrtest  :
4
section    size      addr
5
.text        82         0
6
.bss        132   8388704
7
.stab      1944         0
8
.stabstr    156         0
9
.comment     41         0
10
Total      2355

Wie erwartet vergößert sich die .bss-Section um 2×32 Bytes, die
.text-Section bleibt aber gleich. Stellt man die beiden FIFO-Variablen
in die .noinit-Section, steht in der Ausgabe von avr-size eben .noinit
anstelle von .bss, an den Zahlenwerten ändert sich aber nichts.

von Rolf M. (rmagnus)


Lesenswert?

Matthias N. schrieb:
> Ahh, meine Anfangsformulierung war somit schon sehr ungüntig ;)

Die glaube ich verstanden zu haben. Nur deine Schlußfolgerungen ergeben 
irgendwie keinen Sinn.

> Klar, dass die Variable so oder so den benötigten RAM braucht, aber es
> ging mir darum, dass der im Flash liegende Code unnötig aufgeblasen wird
> ;)

Aber der Flash-Verbrauch ist doch bei der Variante GLOBAL am geringsten.

> Wenn es um den reinen Flashbedarf geht, dann hat der Workaround schon
> hin.

Inwiefern? Wenn ich mir die Zahlen ansehe:

Matthias N. schrieb:
> GLOBAL:
> Program 116 (konstant), Data 24 (entspricht SIZE)

> LOKAL:
> Program 120 (konstant), Data 0 (konstant)

> WORKAROUND für Gloablen Buffer
> Program 148 (konstant), Data 2 (konstant, entspricht Pointer-Grösse)

sehe ich, daß die Variante GLOBAL am wenigsten Flash-Speicher braucht 
und der "Workarund" am meisten.

von Matthias N. (nippey)


Lesenswert?

Dann hing ich bisher dem Irrglauben an, dass das, was in .data steht, zu 
beginn vom Flash "EINS ZU EINS" in den RAM kopiert wird.
Ich hatte diese Werte immer addiert und den Speicherbedarf eines 
Programmes zu bestimmen.

Wenn das ganze in einer Schleife erfolgt, sieht es natürlich anders aus.

Danke.

von Stefan E. (sternst)


Lesenswert?

Matthias N. schrieb:
> Dann hing ich bisher dem Irrglauben an, dass das, was in .data steht, zu
> beginn vom Flash "EINS ZU EINS" in den RAM kopiert wird.

Für die Section .data stimmt das ja auch. Aber deine Angaben sind ja 
wohl offensichtlich das, was am Ende des Build-Outputs als "Program" und 
"Data" ausgegeben wird. Und das "Data" dort entspricht nicht der Section 
.data. Es steht dabei, wie sich das zusammensetzt, du musst es nur genau 
anschauen:
1
Program:    3208 bytes (39.2% Full)
2
(.text + .data + .bootloader)
3
4
Data:        108 bytes (10.5% Full)
5
(.data + .bss + .noinit)
Wie du siehst, .data ist in "Program" bereits enthalten.
Und worum es hier geht, ist .bss. Das ist der Bereich mit den Null 
initialisierten Variablen, der beim Startup per Schleife genullt wird.

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.