Forum: Compiler & IDEs [arm-none-eabi] Wie Stack-Grösse definieren?


von Gelöscht (kami89)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

Ich hänge gerade fest und komme nicht weiter...
Ich programmiere unter Ubuntu 13.10 mit dem arm-none-eabi Compiler aus 
einem Repository [1] einen ARM Cortex M4, genau genommen den TI Tiva 
TM4C123GH6PGEI. Eigentlich funktioniert das auch, doch eine UARTprintf() 
Funktion von TI hängt sich nach spätestens ein paar Sekunden immer auf. 
Eine Recherche im Internet ergab dann, dass der Stack vergrössert werden 
muss damit diese printf() Funktion richtig funktioniert, da sie viel 
Speicher im Stack benötigt. Ein Versuch unter Windows mit dem Code 
Composer Studio hat diesen Verdacht bestätigt, nach dem Vergrössern des 
Stacks/Heaps in den Projekteinstellungen läuft mein Programm 
einwandfrei.

Doch wie kann ich bei meinem Compiler bzw. Linker arm-none-eabi-g++ die 
Stack Grösse angeben? Im Linker Script? Wenn ja, wie?
Ich habe bisher erst AVRs programmiert und habe die ganze Geschichte mit 
den Linker Scripts noch nicht so richtig durchschaut...

Ich verwende das Linker Script aus den Tiva Beispielen (siehe Anhang).

Compilliert wird folgendermassen:
1
arm-none-eabi-gcc -DPART_TM4C123GH6PGE -DARM_MATH_CM4 -DTARGET_IS_BLIZZARD_RA1 -I/opt/tivaware -O0 -g3 -Wall -c -fmessage-length=0 -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -ffloat-store -MMD -MP -MF"src/main.d" -MT"src/main.d" -o "src/main.o" "../src/main.c"


Der Linker Befehl sieht so aus:
1
arm-none-eabi-g++ -L/opt/tivaware/driverlib/gcc -L/opt/tivaware/grlib/gcc -Wl,--static,--gc-sections,-T../TM4C123G.ld -mthumb -mcpu=cortex-m4 -o "Fahrplattform.elf"  ./src/tiva_utils/uartstdio.o  ./src/tiva_drivers/cfal96x64x16.o  ./src/hal/encoder.o ./src/hal/motor.o  ./src/chassis.o ./src/main.o ./src/pid_controller.o  ./startup_gcc.o   -ldriver -lgr

Nach dem Linken wird das *.bin mit diesem Befehl erzeugt:
1
arm-none-eabi-objcopy -O binary Fahrplattform.elf Fahrplattform.bin

Ausgabe von "arm-none-eabi-size -A -t -x Fahrplattform.elf":
1
section              size         addr
2
.text              0x2764          0x0
3
.data               0x580   0x20000000
4
.bss                0x120   0x20000580
5
.debug_info        0x10b5          0x0
6
.debug_abbrev       0x5db          0x0
7
.debug_loc          0x588          0x0
8
.debug_aranges      0x178          0x0
9
.debug_ranges       0x108          0x0
10
.debug_macro       0x89be          0x0
11
.debug_line        0x1525          0x0
12
.debug_str        0x314a1          0x0
13
.comment             0x70          0x0
14
.ARM.attributes      0x30          0x0
15
.debug_frame        0x388          0x0
16
Total             0x40448

Leider kann ich mit diesen Ausgaben irgendwie nicht viel anfangen...
Wäre super wenn mir jemand auf die Sprünge helfen könnte :-)
Vielen Dank schonmal.

[1] https://launchpad.net/~terry.guo/+archive/gcc-arm-embedded

von Michael H. (Gast)


Lesenswert?

Also ich habe hier einen Freescale MKL25, und da habe ich im Linker 
skript mit dem GCC folgende Zeile:
1
/* Generate a link error if heap and stack don't fit into RAM */
2
__heap_size = 0x000;        /* required amount of heap  */
3
__stack_size = 0x080;         /* required amount of stack */
und da kannst Du es einstellen.

Die -T "kennzeichnet" dein Linkerskript. (S.o. auch bei Dir)

Hoffe, das hilft.
Michael

von Guest (Gast)


Lesenswert?

Irgendwo in deinem Linker File wird ein Symbol für den Stack definiert 
sein, sowas wie __stack_end oder ähnlich. Üblicherweise wird es irgendwo 
hinter dem .bss und .data Segment definiert. Der Abstand zwischen der 
Adresse dieses Symbols und dem Ende von .bss definiert deine Stack 
Größe.
Das Symbol wird dann im Startup Code benutzt im den Stackpointer zu 
initialisieren bzw. steht beim Cortex M in der Interrupt Vector Tabelle.
Quick und Dirty kann man das Symbol einfach mit der letzten RAM Adresse 
definieren.

von Guest (Gast)


Lesenswert?

Habe mir gerade dein Linkerfile angeschauht, das ist ja echt mal 
übersichtlich ;-).

Du kannst folgendes nach .bss einfügen:

  /* User_heap_stack section, used to check that there is enough RAM 
left */
  ._user_heap_stack :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    _heap_start_ = .;
    . = . + _Min_Heap_Size;
    _heap_end_ = .;
    _stack_start_ = .;
    . = . + _Min_Stack_Size;
    _stack_end_ = .;
    . = ALIGN(4);
  } >RAM

Und irgendwo an den Anfang:
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x400;   /* required amount of heap  */
_Min_Stack_Size = 0x200; /* required amount of stack */

In der Vector Tabelle muss dann als erster Eintrag das _stack_end_ 
stehen.

Damit auch ein malloc() mit Heap funktioniert brauchst du auch noch die 
passende sbrk() Funktionen, die diese Symbole benutzt:

caddr_t _sbrk(int incr) {
  static char * heap_end;
  char *        prev_heap_end;

  if (heap_end == NULL) {
    heap_end = & _heap_start_;
  }
  prev_heap_end = heap_end;

  if (heap_end + incr > &__heap_end__) {
      /* Some of the libstdc++-v3 tests rely upon detecting
        out of memory errors, so do not abort here.  */
      errno = ENOMEM;
      return (caddr_t) -1;
  }

  heap_end += incr;
  return (caddr_t) prev_heap_end;
}

von Gelöscht (kami89)


Lesenswert?

Hallo,

Vielen Dank für die Antworten!
Langsam kommt Licht in die ganze Sache... Dank dem Hinweis, dass der 
Stackpointer an die ersten Stelle der Vektortabelle geschreiben werden 
muss, konnte ich jetzt herausfinden wo mein Stack geblieben ist :-)

Witzigerweise gibts in meinem Startup Code ein Array, welches 
anscheinend den Platz für meinen Stack reserviert:
1
static uint32_t pui32Stack[64];

In die Vektortabelle wird dann dieser Stackpointer reingeschrieben:
1
(void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack))

Vergrössere ich mein mysteriöses Stack-Array, läuft mein Programm 
einwandfrei, keine Abstürze mehr :-)

Jetzt wird mir die ganze Stack-Geschichte schon viel klarer.

Der Vorschlag von "Guest" möchte ich jetzt aber doch gerne noch 
ausprobieren (bzw. implementieren, wenns dann funktioniert). Das Linker 
Script habe ich ergänzt, doch im Startup Code hänge ich noch fest...

Als Initialer Stackpointer habe ich einfach "_stack_end_" 
reingeschrieben. Doch stack_end ist ja dem Compiler zur Compillierzeit 
noch unbekannt, daher müsste wohl noch eine extern Deklaration rein 
oder? Aber wie müsste das aussehen? Ich habe schon ein paar 
Möglichkeiten probiert, doch der uC will damit einfach nicht mehr 
aufstarten...

mfg

: Bearbeitet durch User
von Ohh (Gast)


Lesenswert?

Das funktioniert so auch nicht. Die Reset-Funktion löscht beim Startup 
den Speicher und somit auch den eigenen Stack. Entweder du schreibst dir 
eine eigene Reset-Funktion (evtl. gibt es die bei dir auch schon im 
Projekt) oder du lässt es einfach so. Hat alles seine Vor- und 
Nachteile.

von Guest (Gast)


Lesenswert?

Ohh schrieb:
> Das funktioniert so auch nicht. Die Reset-Funktion löscht beim Startup
> den Speicher und somit auch den eigenen Stack.

Was möchtest du uns damit sagen?? ;-)

Welche Reset funktion löscht welchen Speicher?
Und wieso sollte das ein Problem sein?
Was nach dem Reset auf dem Stack steht ist völlig irrelevant.

Urban B. schrieb:
> Als Initialer Stackpointer habe ich einfach "_stack_end_"
> reingeschrieben. Doch stack_end ist ja dem Compiler zur Compillierzeit
> noch unbekannt, daher müsste wohl noch eine extern Deklaration rein
> oder?

extern unsigned int stack_end;

und als ersten Eintrag in die Tabelle:

&_stack_end_,

von Guest (Gast)


Lesenswert?

Urban B. schrieb:
> Witzigerweise gibts in meinem Startup Code ein Array, welches
> anscheinend den Platz für meinen Stack reserviert:static uint32_t
> pui32Stack[64];

Das geht im Prinzip natürlich auch, hat aber den Nachteil das dieser 
Stackbereich irgendwo im .bss liegt und bei der Segmentinitialisierung 
mit 0 gefüllt ist, was völlig unnötig ist.
Zweiter Nachteil ist das wenn dieser Stack überläuft man sich sofort 
irgendwelche Variablen überschreibt.

von Ohh (Gast)


Lesenswert?

Guest schrieb:
> Ohh schrieb:
>> Das funktioniert so auch nicht. Die Reset-Funktion löscht beim Startup
>> den Speicher und somit auch den eigenen Stack.
>
> Was möchtest du uns damit sagen?? ;-)

:-)

von Ohh (Gast)


Lesenswert?

Guest schrieb:
> Urban B. schrieb:
>> Witzigerweise gibts in meinem Startup Code ein Array, welches
>> anscheinend den Platz für meinen Stack reserviert:static uint32_t
>> pui32Stack[64];
>
> Das geht im Prinzip natürlich auch, hat aber den Nachteil das dieser
> Stackbereich irgendwo im .bss liegt und bei der Segmentinitialisierung
> mit 0 gefüllt ist, was völlig unnötig ist.
> Zweiter Nachteil ist das wenn dieser Stack überläuft man sich sofort
> irgendwelche Variablen überschreibt.

... und nicht den Heap.

von Guest (Gast)


Lesenswert?

Genau, was natürlich auch nicht unbedingt besser sein muss ;-).

Letztlich ist man natürlich völlig frei wo man Stack und Heap in den 
Speicher legt. Klassische GCC Linker files definieren z.B. den Heap 
Anfang auf die erste Adresse nach dem Ende des .bss und den Stack auf 
die letzte RAM Adresse. Stack und Heap treffen sich dann halt irgendwo 
in der Mitte... ;-).

von Ohh (Gast)


Lesenswert?

Guest schrieb:
> Genau, was natürlich auch nicht unbedingt besser sein muss ;-).
>
> Letztlich ist man natürlich völlig frei wo man Stack und Heap in den
> Speicher legt. Klassische GCC Linker files definieren z.B. den Heap
> Anfang auf die erste Adresse nach dem Ende des .bss und den Stack auf
> die letzte RAM Adresse. Stack und Heap treffen sich dann halt irgendwo
> in der Mitte... ;-).

Es ist bei der Cortex-MCU bzw. allgemein bei ARM eher unüblich einen von 
den zwei oder mehr Stacks an das Speicherende zu legen.

von Guest (Gast)


Lesenswert?

Ohh schrieb:
> Es ist bei der Cortex-MCU bzw. allgemein bei ARM eher unüblich einen von
> den zwei oder mehr Stacks an das Speicherende zu legen.

Ich habe ja auch nur von klassischen GCC Linkerfiles gesprochen...aber 
ja, ich habe sowas auch schon zu genüge bei Cortex M Linkerfiles 
gesehen, sinnig finde ich das auch nicht.

von Gelöscht (kami89)


Lesenswert?

OK jetzt läufts mit der Variante von "Guest (Gast)". Der Fehler war die 
Bezeichnung "RAM" statt "SRAM" in der Ergänzung im Linkerscript.

Die Funktion _sbrk() kann ich einfach in irgend ein C File packen, auch 
ich den Startup Code nehme ich an?

Ohh schrieb:
> Es ist bei der Cortex-MCU bzw. allgemein bei ARM eher unüblich einen von
> den zwei oder mehr Stacks an das Speicherende zu legen.

Hat das einen bestimmten Grund? Um Probleme vorzubeugen wäre es doch 
ideal den Stack und den Heap möglichst weit auseinander zu legen.

Mein ursprüngliches Problem ist jetzt jedenfalls gelöst, vielen Dank für 
eure Hilfe!

mfg

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

Urban B. schrieb:
> Die Funktion _sbrk() kann ich einfach in irgend ein C File packen, auch
> ich den Startup Code nehme ich an?

Im Prinzip ja, ich nehme an du benutzt die NewLib?
Schau mal hier rein: http://www.sourceware.org/newlib/

von Guest (Gast)


Lesenswert?

Urban B. schrieb:
> Hat das einen bestimmten Grund? Um Probleme vorzubeugen wäre es doch
> ideal den Stack und den Heap möglichst weit auseinander zu legen.

Optimal ist es das sowas gar nicht erst passiert. Beim Stack kann man 
das nicht vermeiden, aber beim Heap sorgt gerade die sbrk() Funktion 
dafür das ein malloc() NULL zurück liefert falls kein Heap mehr frei 
ist.

von (prx) A. K. (prx)


Lesenswert?

Ohh schrieb:
> Es ist bei der Cortex-MCU bzw. allgemein bei ARM eher unüblich einen von
> den zwei oder mehr Stacks an das Speicherende zu legen.

Bei den Cortex-M Prozessoren wird ohne RTOS oft nur ein Stack verwendet. 
Man kann zwar User/System trennen, aber das lohnt eigentlich nur, wenn 
man den Speicherschutz verwendet (so überhaupt vorhanden).

von Gelöscht (kami89)


Lesenswert?

Guest schrieb:
> Urban B. schrieb:
>> Die Funktion _sbrk() kann ich einfach in irgend ein C File packen, auch
>> ich den Startup Code nehme ich an?
>
> Im Prinzip ja, ich nehme an du benutzt die NewLib?
> Schau mal hier rein: http://www.sourceware.org/newlib/

Nein, bisher benutze ich die NewLib nicht. Vielleicht brauche ich die 
später noch. Die UARTprintf() Funktion habe ich von TI übernommen, die 
haben in Ihren Utils selbst eine geschrieben (eine ganz hässliche, mit 
goto's...).

Macht es denn in Bezug auf meine Frage einen Unterschied ob ich die 
NewLib benutze?

von Ohh (Gast)


Lesenswert?

A. K. schrieb:
> Ohh schrieb:
>> Es ist bei der Cortex-MCU bzw. allgemein bei ARM eher unüblich einen von
>> den zwei oder mehr Stacks an das Speicherende zu legen.
>
> Bei den Cortex-M Prozessoren wird ohne RTOS oft nur ein Stack verwendet.
> Man kann zwar User/System trennen, aber das lohnt eigentlich nur, wenn
> man den Speicherschutz verwendet (so überhaupt vorhanden).

Ab und an kann es dann doch zu einer Exception kommen und wenn die einen 
frischen Stackpointer für den Debug-Output hat, ist es schon nicht so 
unnötig und spart etwas Zeit.

Ich verwende aber auch nur RTOS auf den Controllern und dort ist es mit 
drin.

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.