Hallo Experten,
ich habe ein paar grundsätzliche Fragen zum Programmieren eines µC. Ich
selbst programmiere µC auf ARM Cortex-M4 Basis, falls das bei den Fragen
eine Rolle spielt.
- Werden lokale Variablen werden sofort nach Start des µC in den SRAM
gelegt? Liegen diese liegen im Stack?
- Globale Variablen werden auch sofort nach Start in den SRAM gelegt?
Diese liegen glaube nicht im Stack, ist das richtig?
- Wo sind constant Variablen allokiert? Liegen diese im Flash?
- Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man
wann er zu klein ist?
Vielen Dank für eure Antworten!!
Tob75 schrieb:> Vielen Dank für eure Antworten!!
alle Fragen habe nicht mit dem µC zu tun sondern mit der
Programmiersprache und deren Einstellungen. Davon hast du uns aber keine
Info geliefert.
Hallo,
> Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man> wann er zu klein ist?
Theoretisch kannst du den Stack exakt bemessen. Zunächst mal: mach ihn
so gross wie möglich. Dann fülle ihn mit einem bestimmten Bitmuster,
z.B. 0xAFFEAFFE oder 0xDEADBEEF oder so etwas. Und dann lass einfach
deine Software laufen. Später kannst du dann z.B. mit dem Debugger
schauen, bis wie weit herunter dein Bitmuster überschrieben wurde, so
eine Art 'Wassermarke' oder sowas in der Art. Das war der grösste
Stackverbrauch. Einerseits könnte man den also so bemessen. Andererseits
könntest du auf dem PC in deiner Entwicklungsumgebung z.B. mit Doxygen
oder so einen Callgraph erzeugen lassen und die tiefste Verschachtelung
von Funktionen bestimmen. Da jeder Funktionsaufruf einen bestimmten
Stackverbrauch mit sich zieht, kann man dann so den Stackverbrauch
abschätzen.
> Wo sind constant Variablen allokiert? Liegen diese im Flash?
ja.
> Globale Variablen werden auch sofort nach Start in den SRAM gelegt?> Diese liegen glaube nicht im Stack, ist das richtig?
Nein, weil sie global sind. Die liegen 'irgendwo' im Memory. Je nach dem
ob sie mit 0 initialisiert werden oder nicht liegen sie im BSS-Segment
oder aber in einem anderen Segment, dessen Name mir jetzt nicht einfällt
und das durch die Kopierereri im Startup-Code initialisiert wird.
Genauso wie die lokalen static-Variablen in Funktionen. Die liegen im
selben Segment. Aber 'normale' auto-Variablen liegen auf dem Stack.
Gruss
Tobias
Edit: Peter II war schneller. Er hat natürlich recht, aber ich gehe mal
von C aus ;-)
Tob75 schrieb:> - Werden lokale Variablen werden sofort nach Start des µC in den SRAM> gelegt? Liegen diese liegen im Stack?
Lokale Variablen liegen im Stack und werden dann angelegt, wenn sie im
Programmcode deklariert werden.
> - Globale Variablen werden auch sofort nach Start in den SRAM gelegt?> Diese liegen glaube nicht im Stack, ist das richtig?
Globale Variablen werden beim Start in den SRAM gelegt und zwar nicht in
den Stack, sondern in einen separaten Bereich für globale Variablen.
(Optimierungen und Eigenheiten des Compilers ausgenommen.)
> - Wo sind constant Variablen allokiert? Liegen diese im Flash?
Kann, muss aber nicht sein. Wenn der Mikrocontroller nicht unkompliziert
direkt wahlfrei aus dem Flash lesen kann, so wird man sie eher im RAM
anlegen.
> - Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man> wann er zu klein ist?
Typischerweise wächst der Stack vom oberen Ende des Speichers nach
unten, während alles andere von unten nach oben wächst. So kollidieren
der Stack erst mit dem Rest des Speichers, wenn der Speicher randvoll
ist. Das merkst du daran, dass dein Programm komplett verrückt spielt.
Den Stack "dimensioniert" man somit eigentlich nicht. Man muss bloss
sicherstellen, dass er nie zu gross wird. Auf einem ARM dürfte das aber
ohne Rekursion fast nicht passieren.
Danke für die schnellen antworten. Und um die Info nachzureichen: Ja ich
programmiere nur in C und die Entwicklungsumgebung (Rowley Crossworks)
verwendet den GCC.
Tob75 schrieb:> - Wo sind constant Variablen allokiert? Liegen diese im Flash?
Diese Frage hängt stark von der verwendeten Plattform und den
verwendeten Werkzeugen ab. AVRs mit ihrer Harvard Architektur werden
auch das im RAM ablegen. Hier bedarf es einiger Tricks (Attribute
PROGMEM), um das Ganze wirklich in den Flash-Speicher zu bekommen.
"const" ist überhaupt nur ein Hinweis an den Compiler (und an
Mitmenschen), dass der Inhalt der Variable nicht verändert wird. Dadurch
kann der Compiler vor Dummheiten warnen und ggf. Optimierungen
durchführen. In erster Linie kann man sich das aber wie "normale"
Variablen vorstellen.
Mit freundlichen Grüßen,
Karol Babioch
Tobias Plüss schrieb:> Theoretisch kannst du den Stack exakt bemessen. Zunächst mal: mach ihn> so gross wie möglich. Dann fülle ihn mit einem bestimmten Bitmuster,> z.B. 0xAFFEAFFE oder 0xDEADBEEF oder so etwas. Und dann lass einfach> deine Software laufen. Später kannst du dann z.B. mit dem Debugger> schauen, bis wie weit herunter dein Bitmuster überschrieben wurde, so> eine Art 'Wassermarke' oder sowas in der Art. Das war der grösste> Stackverbrauch.
Gefährlicher Ansatz. Hierbei ist nicht sichergestellt, daß der worst
case während der Prüfzeit überhaupt aufgetreten ist. Also besser ganz
schnell wieder vergessen.
> Andererseits> könntest du auf dem PC in deiner Entwicklungsumgebung z.B. mit Doxygen> oder so einen Callgraph erzeugen lassen und die tiefste Verschachtelung> von Funktionen bestimmen.
Schon besser, aber immer noch nur die halbe Miete. Der Stackverbrauch
von Interrupts muß gesondert berücksichtigt werden.
c-hater schrieb:>> Andererseits>> könntest du auf dem PC in deiner Entwicklungsumgebung z.B. mit Doxygen>> oder so einen Callgraph erzeugen lassen und die tiefste Verschachtelung>> von Funktionen bestimmen.>> Schon besser, aber immer noch nur die halbe Miete. Der Stackverbrauch> von Interrupts muß gesondert berücksichtigt werden.
zzgl. Rekursion...
c-hater schrieb:>> Später kannst du dann z.B. mit dem Debugger schauen, bis wie weit>> herunter dein Bitmuster überschrieben wurde, so eine Art 'Wassermarke'>> oder sowas in der Art. Das war der grösste Stackverbrauch.>> Gefährlicher Ansatz. Hierbei ist nicht sichergestellt, daß der worst> case während der Prüfzeit überhaupt aufgetreten ist.
Immer noch besser als die Art, wie es normalerweise ermittelt wird,
nämlich gar nicht.
>> Andererseits könntest du auf dem PC in deiner Entwicklungsumgebung z.B.>> mit Doxygen oder so einen Callgraph erzeugen lassen und die tiefste>> Verschachtelung von Funktionen bestimmen.>> Schon besser, aber immer noch nur die halbe Miete. Der Stackverbrauch> von Interrupts muß gesondert berücksichtigt werden.
Und den Aufwand gibst du dir für jedes Programm?
Mal von C und ähnlichen Sprachen ausgehend, üblicherweise:
Tob75 schrieb:> - Werden lokale Variablen werden sofort nach Start des µC in den SRAM> gelegt?
Nein. Der Platz wird meist am Anfang einer Funktion auf dem Stack
angelegt und mit Beendigung wieder freigegeben. Drum ist es ein Stack.
Ausnahme: Lokale Variablen mit "static" dran. Die sind vom Speicher her
wie globale Variablen, aber nur in der Funktion selbst per Name
ansprechbar.
> Liegen diese liegen im Stack?
Ja. Ausnahme sind kleine µC wie z.B. 8051 und PIC16, die sich mit der
Verwendung von Stacks als Speicher für Variablen etwas schwer tun, und
deren Compiler deshalb auch lokale Variablen gerne statisch anlegen,
ggf. überlappend.
> - Globale Variablen werden auch sofort nach Start in den SRAM gelegt?> Diese liegen glaube nicht im Stack, ist das richtig?
Ja.
> - Wo sind constant Variablen allokiert? Liegen diese im Flash?
Keine generelle Regel. Aber als Daumenregel kann man wohl sagen, dass
dies nur der Fall ist, wenn das Flash im Datenadressraum liegt. Bei
32-Bittern ist das üblich, bei kleineren teils nicht.
> - Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man> wann er zu klein ist?
Das er überläuft. :-)
Im Ernst: es gibt keine Regel dafür wie sich das äussert. In vielen
Umgebungen laufen Stack und Heap / statische Daten gegeneinander,
weshalb ein überbordender Stack zunächst in den Heap und dann in die
statischen Daten reinläuft und da Chaos anrichtet. Aber es gibt auch
andere Umgebungen.
Steffen Rose schrieb:> konstante auto-Variable liegen (immer?) auf dem Stack.
Oder nirgends. Oder werden statisch angelegt, weil sie als
unveränderlich nicht reentrant sein müssen:
1
intg(constint*);
2
3
intf(void)
4
{
5
constintx[2]={1,2};
6
returng(x);
7
}
1
f:
2
subl $12, %esp
3
movl $x.1281, (%esp)
4
call g
5
addl $12, %esp
6
ret
7
x.1281:
8
.long 1
9
.long 2
Lokale Variablen liegen je nach Prozessor auch in Registern, ohne dass
auf dem Stack dafür Platz angelegt werden muss. Oder sie liegen
nirgends, weil vollständig wegoptimiert.
Steffen Rose schrieb:> Tob75 schrieb:>> - Wo sind constant Variablen allokiert? Liegen diese im Flash?>> konstante auto-Variable liegen (immer?) auf dem Stack.
Konstante Variablen?
unveränderliche Variable (versteht so bloß keiner, was gemeint ist)
Edit: ok, auch nicht viel besser.
Ich wollte auf den Unterschied zwischen
Konstanten mit automatic storage
und static Konstanten aufmerksam machen.
Bernhard D. schrieb:> Konstante Variablen?
Stimmt schon. Leider. In C ist
const int n = 10;
keine lexikalische Konstante, sondern tatsächlich eine unveränderliche
Variable.
In C++ ist das etwas anders (und komplizierter :-).
@ P. M. (o-o)
>Typischerweise wächst der Stack vom oberen Ende des Speichers nach>unten, während alles andere von unten nach oben wächst. So kollidieren>der Stack erst mit dem Rest des Speichers, wenn der Speicher randvoll>ist. Das merkst du daran, dass dein Programm komplett verrückt spielt.
Naja, manchmal hat man aber auch Pech und es kommt zu Fehlern, die lange
nicht auffallen, z.B. weil eine maximale Stacktiefe nur selten erreicht
wird oder nur kleine Fehler passieren aber kein Absturz bzw. massives
Fehlverhalten. Wäre das nicht ein wichtiges Feature für CPUs, eine
Stackprüfung in HARDWARE? Oder wenigstens in Software? Und damit meine
ich nicht den Trick mit dem Füllmuster, sondern eine automatische
Prüfung des Stacks bei jedem Funktionsaufruf!
Falk Brunner schrieb:> Wäre das nicht ein wichtiges Feature für CPUs, eine> Stackprüfung in HARDWARE? Oder wenigstens in Software? Und damit meine> ich nicht den Trick mit dem Füllmuster, sondern eine automatische> Prüfung des Stacks bei jedem Funktionsaufruf!
Ja, das fände ich auch wichtig. Ein Stack-Underrun-Interrupt bzw. eine
Exception sollte sich mit einem einfachen Compare realisieren lassen.
Gibt es Architekturen, die sowas schon unterstützen?
Wie sieht eine brauchbare Realisierung in Software aus?
Eine Abfrage bei jedem Funktionsaufruf dürfte zuviel Performance kosten.
Mark
Falk Brunner schrieb:> Wäre das nicht ein wichtiges Feature für CPUs
Ausserhalb der kleinen µC-Sphäre lässt sich das oft mühelos über die
Speicherverwaltung erledigen. Also indem der Stack sein Ende in einem
finalen page fault findet. Der Compiler muss dann nur drauf achten, dass
bei grossen lokalen Daten die guard page nicht übersprungen wird, muss
also ggf. bei Arrays entsprechende Zugriffe alle paar KB einflechten.
> Oder wenigstens in Software?
Gibts in GCC im Prinzip, wobei es natürlich Sache der
Zielsystemanpassung ist, etwas draus zu machen.
P. M. schrieb:> Typischerweise wächst der Stack vom oberen Ende des Speichers nach> unten
Bei den 16-Bittern von Microchip, also PIC24/30/33, wächst der Stack
aufwärts. Irgendwas macht Microchip eben immer anders. ;-)
Rolf Magnus schrieb:> Und den Aufwand gibst du dir für jedes Programm?
Natürlich. Ich bin der Programmierer und deshalb nicht nur dafür
verantwortlich, sondern auch dazu in der Lage.
Wer sonst sollte das denn deiner Meinung nach tun? Der Pförtner?