Hallo,
mir sind Begriffe wie aliged, Endian etc und die Zugriffsarten schon
bekannt, dass es eben einfacher ist wenn ein uint8_t 32 Bit beansprucht,
da der ARM nunmal einen 32 Bit Datenbus hat und 4 Bytes in einem Haps
holt, wenn sie an einer / 4 Grenze liegen.
In meinem Fall muss ich aber zwingend dafür sorgen, dass
uint8_t feld[1024] eben nur 1024 Bytes braucht und nichts mehr. Und das
als lokale automatische Var auch nicht auf dem Stack, das muss ins .data
segment. Stack ist nur 0x400 gross und es gibt keine Fehlermeldungen
wenn der überläuft, nur Abstürze.
Zugriffe dürfen kostspieliger sein
_attribute((packed)) lässt sich leider nur auf structs und unions
anwenden, scheinbar.
Wie löse ich mein Problem mit dem GCC, so dass ich auch nachschauen kann
was er gemacht hat?
Das hier hilft mir auch nicht wirklich weiter:
https://www.uninformativ.de/blog/postings/2015-10-25/0/POSTING-de.html
Grüße,
Christian
Chris J. schrieb:> uint8_t feld[1024] eben nur 1024 Bytes braucht und nichts mehr.
Das tut es schon. Da musst du nix mehr dran machen, denn uint8_t hat ein
Alignment von 1. Bei gegebener Paranoia kannst du (bei Verwendung von
C++) prüfen dass es wirklich nur 1024 Bytes sind:
1
static_assert(sizeof(feld)==1024,"Feld hat falsche Groesse!");
Chris J. schrieb:> Wie löse ich mein Problem mit dem GCC, so dass ich auch nachschauen kann> was er gemacht hat?
Was hast du denn für ein Problem? Es sollte so schon funktionieren.
Chris J. schrieb:> mir sind Begriffe wie aliged, Endian etc und die Zugriffsarten schon> bekannt, dass es eben einfacher ist wenn ein uint8_t 32 Bit beansprucht,> da der ARM nunmal einen 32 Bit Datenbus hat und 4 Bytes in einem Haps> holt, wenn sie an einer / 4 Grenze liegen.
In den Registern ja, weil der ARM nur 32-Bit-Register hat. Im Speicher
braucht eine 8-Bit-Variable natürlich auch nur 8 Bit.
> In meinem Fall muss ich aber zwingend dafür sorgen, dass>> uint8_t feld[1024] eben nur 1024 Bytes braucht und nichts mehr.
Das tut es (muss es - das ist in C so vorgeschrieben).
> Und das als lokale automatische Var auch nicht auf dem Stack,
Lokale automatische Variablen landen immer auf dem Stack oder direkt in
Registern (was natürlich bei einem Array dieser Größe nicht geht).
> das muss ins .data segment.
Dann musst du sie statisch machen und nicht automatisch.
> _attribute((packed)) lässt sich leider nur auf structs und unions> anwenden, scheinbar.
Weil Arrays per Definition immer gepackt sind. Das Attribut wäre also
für Arrays völlig sinnlos.
> Wie löse ich mein Problem mit dem GCC, so dass ich auch nachschauen kann> was er gemacht hat?
Du kannst dir mit objdump -t (bzw arm-none-eabi-objdump oder wie immer
das bei dir heißt) eine Liste aller Symbole anschauen, wo auch die Größe
drin steht. Da sollte auch deine Variable zu finden sein, sofern du sie
statisch definierst.
Rolf M. schrieb:> In den Registern ja, weil der ARM nur 32-Bit-Register hat. Im Speicher> braucht eine 8-Bit-Variable natürlich auch nur 8 Bit.
Ok... nur handelt es sich um internen Speicher. Der ist u.a. auch
bitradressierbar. Ist das da dann auch so? Mache ich Wind um nichts? Ich
habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau
die Größe hat wie errechnet, sondern etwas größer ist. Das gab Probs
als ich einen struct per Funk an an einen 8 Bitter verschickt habe,
obwohl ich da auch nur mit uint_xx gearbeitet hatte und nicht mit den
üblichen Größen wie int, char etc.
Das mit dem Stack geht leider nicht, da bleibt mir wahrscheinlich nur
der Heap.... auch wenn der vielleicht etwas altmodisch ist.
Chris J. schrieb:> Ok... nur handelt es sich um internen Speicher. Der ist u.a. auch> bitradressierbar.
Bit-Adressierbar auf ARM? Wie das? Meinst du Bit-Banding? Das zählt
nicht, da weiß C nix von.
Chris J. schrieb:> Ist das da dann auch so?
Du meinst den ganz normalen SRAM vom Controller? Dann Ja.
Chris J. schrieb:> Mache ich Wind um nichts?
Vermutlich.
Chris J. schrieb:> Ich> habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau> die Größe hat wie errechnet, sondern etwas größer ist.
Ja, bei structs gibts ggf. Padding-Bytes, damit die Adressen aller
Member ein Vielfaches ihres jeweiligen Alignments sind.
Chris J. schrieb:> da bleibt mir wahrscheinlich nur> der Heap.... auch wenn der vielleicht etwas altmodisch ist.
Altmodisch? Wurde mittlerweile was besseres gefunden? Das Layout eines
Datentyps hat nichts mit der Art zu tun, wie es angelegt wird, das ist
auf Stack, Heap, globalen Variablen gleich. Interessant wird's höchstens
bei lokalen temporären Variablen in Registern. Bei korrektem C Code
merkst du da aber überhaupt nichts von.
Warum ist das ganze überhaupt wichtig? Ist dein Code vielleicht nicht
korrekt? Du hast ein Array, du greifst auf die Elemente zu, C sorgt
dafür dass es funktioniert. In welchem Speicher das wie abgelegt wird
ist völlig unerheblich. Was ist das eigentliche Problem? Willst du das
mit DMA benutzen, direkt über eine Schnittstelle rausschicken o.ä.?
Niklas G. schrieb:> Altmodisch? Wurde mittlerweile was besseres gefunden? Das Layout eines> Datentyps hat nichts mit der Art zu tun, wie es angelegt wird, das ist> auf Stack, Heap, globalen Variablen gleich. Interessant wird's höchstens> bei lokalen temporären Variablen in Registern. Bei korrektem C Code> merkst du da aber überhaupt nichts von.
Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe
ich eintrage bei dem array keine Compiler Warnung kommt. Ich habe mich
schon mal totgesucht wegen "HardFault Errors" bis ich dann merkte, dass
mir der Stack übergelaufen ist, der auf 0x100 stand.
Trotzdem danke für die Ausführungen, wieder etwas schlauer geworden.
Naja, den heap würde ich allenfalls für binäre Bäume benutzen und damit
habe ich auf uC noch nie was zu tun gehabt. Irgendwie geht alles immer
ohne diese Haldenspeicher, den ich noch aus den 80igern kenne als ich
anfing zu programmieren an der Uni.
Chris J. schrieb:> Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe> ich eintrage bei dem array keine Compiler Warnung kommt.
Klingt komisch. Wird das array überhaupt auf nichttriviale Art genutzt
oder wird es vielleicht wegoptimiert?
Chris J. schrieb:> ? Ich> habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau> die Größe hat wie errechnet, sondern etwas größer ist. Das gab Probs> als ich einen struct per Funk an an einen 8 Bitter verschickt habe,> obwohl ich da auch nur mit uint_xx gearbeitet hatte und nicht mit den> üblichen Größen wie int, char etc.
aus dem Grund verschickt man auch keine Structs einfach so. Man baut das
Datenpaket "manuell" zusammen.
Chris J. schrieb:> Mache ich Wind um nichts?
Ja.
Chris J. schrieb:> Ich habe nur gemerkt, dass ein struct
Ein struct ist ja aber auch was anderes als ein Array. In einem Array
von structs liegen auch alle structs hintereinander, wenn auch die
Member im struct selber nicht direkt hintereinander liegen muessen
(padding).
Chris J. schrieb:> da bleibt mir wahrscheinlich nur> der Heap.... auch wenn der vielleicht etwas altmodisch ist.
???
Chris J. schrieb:> Stack ist nur 0x400 gross und es gibt keine Fehlermeldungen> wenn der überläuft, nur Abstürze.Chris J. schrieb:> Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe> ich eintrage bei dem array keine Compiler Warnung kommt.
Und was hat der Compiler damit zu tun? Der Compiler weiss nicht wie viel
Speicher du hast.
Chris J. schrieb:> Ich habe mich> schon mal totgesucht wegen "HardFault Errors" bis ich dann merkte, dass> mir der Stack übergelaufen ist, der auf 0x100 stand.
Schau mal hier, vielleicht hilft dir das:
https://mcuoneclipse.com/2015/08/21/gnu-static-stack-usage-analysis/https://stackoverflow.com/questions/6387614/how-to-determine-maximum-stack-usage-in-embedded-system-with-gccChris J. schrieb:> Naja, den heap würde ich allenfalls für binäre Bäume benutzen und damit> habe ich auf uC noch nie was zu tun gehabt. Irgendwie geht alles immer> ohne diese Haldenspeicher, den ich noch aus den 80igern kenne als ich> anfing zu programmieren an der Uni.
Wenn du dynamischen Speicher brauchst, wird der auf dem Heap allokiert.
Muss ja auch, weil der Stack ja staendig neu beladen und auch wieder
abgeraeumt wird (Funktions ein- und austritt). Und dynamischen Speicher
braucht man fuer sehr viel mehr als binary-trees. Wenn du ohne
dynamischen Speicher (malloc/calloc/new, free/delete) auskommst ist doch
okay. Man kann vieles auch ohne dynamischen Speicher loesen. Auf einem
uC ist dynamische Speicher aber mit etwas vorsicht zu geniessen, ggf.
muss man sich selbst eine Speicherverwaltung bauen.
Mmh, du hast also Probleme und weisst nicht welche. Und vermutest, dass
ein Array irgendwie ein bisschen größer ist als gedacht. Eigentlich 4
Mal so groß, aber sizeof ist zu kompliziert, deshalb fragst Du hier nach
einer Lösung. Für ein Problem, dass Du nicht kennst aber das ja da sein
könnte.
Chris J. schrieb:> Rolf M. schrieb:>> In den Registern ja, weil der ARM nur 32-Bit-Register hat. Im Speicher>> braucht eine 8-Bit-Variable natürlich auch nur 8 Bit.>> Ok... nur handelt es sich um internen Speicher.
Wo der Speicher ist, hat keinen Einfluss auf das Speicherlayout des
Compilers.
> Ich habe nur gemerkt, dass ein struct mit diversen Größen eben nicht genau> die Größe hat wie errechnet, sondern etwas größer ist.
Ja, aber dann waren in deiner Struct die Elemente vermutlich von
verschiedenen Typen. In einem Array sind aber alle Elemente vom selben
Typ und haben damit die selben Alignment-Anforderungen.
> Das mit dem Stack geht leider nicht, da bleibt mir wahrscheinlich nur> der Heap....
Verstehe ich nicht. Warum kann die Variable nicht statisch sein? Das
scheint genau das zu sein, was du brauchst.
Chris J. schrieb:> Mein Problem ist, dass ich nur noch 2kb frei habe aber egal welche Größe> ich eintrage bei dem array keine Compiler Warnung kommt.
Solange das automatisch ist, existiert es zur Compilezeit noch gar
nicht, da es erst beim Funktionsaufruf angelegt wird. Es wird bei der
Speicherbelegung gar nicht berücksichtigt. Wenn du es dynamisch ("Heap")
anlegst, wäre das übrigens auch nicht anders.
> Ich habe mich schon mal totgesucht wegen "HardFault Errors" bis ich dann> merkte, dass mir der Stack übergelaufen ist, der auf 0x100 stand.
Wie viel Platz für den Stack später zur Laufzeit nötig sein wird, weiß
der Compiler ja auch gar nicht.
> Naja, den heap würde ich allenfalls für binäre Bäume benutzen und damit> habe ich auf uC noch nie was zu tun gehabt. Irgendwie geht alles immer> ohne diese Haldenspeicher, den ich noch aus den 80igern kenne als ich> anfing zu programmieren an der Uni.
Auf dem µC braucht man es auch eher selten, bzw. sollte eher darauf
verzichten, wenn möglich. Auf dem PC ist es unverzichtbar. So sehr, dass
in manchen Sprachen grundsätzlich alle Objekte dynamisch angelegt
werden. Ist also alles andere als "altmodisch".
Wie oben schon jemand schrieb, die Variable "static" machen.
1
voidf(...)
2
{
3
staticcharx[1024];// in .data
4
...
5
}
6
7
voidg(...)
8
{
9
staticchary[1024];// in .data
10
...
11
}
12
voidh(...)
13
{
14
charz[1024];// on stack
15
...
16
}
Die Variablen x und y belegt dann natürlich dauerhaft den Speicher, je
1024 Bytes - beim wiederholten Aufruf von f/g findet man sogar noch den
Inhalt des vorherigen Durchlaufs drin. Wenn du dieses "Feature" nicht
brauchst, wäre es sinnvoller, den Stack einfach 1024 Bytes größer zu
machen und auto-Variablen (wie z) zu benutzen.
asdfasd schrieb:> Wenn du dieses "Feature" nicht brauchst, wäre es sinnvoller, den Stack> einfach 1024 Bytes größer zu machen und auto-Variablen (wie z) zu> benutzen.
Dann steht eben nicht das drin, was man beim letzten mal reingeschrieben
hat, sondern irgendwas, was zufällig an dieser Speicherstelle steht.
Kaj G. schrieb:> Und was hat der Compiler damit zu tun? Der Compiler weiss nicht wie viel> Speicher du hast.
Aber der Linker weiss es aus dem Linkerfile.... eigentlich muss der
alles wissen, auch zur Laufzeit. Das ergibt sich aus der Codeananalyse.
In teuren IDEs sieht man auch wieviel Stack man braucht, da die eigene
Parser haben in den Editoren. Bei Segger siehste auch ganz genau wieviel
welches Modul braucht. Gehen tut das schon.
Aber insgesam sind meine Sorgen gelöst, vielen Dank dafür! Auch wenn es
nur Hobby ist.. aber eines was mich nie loslassen wird :-)
Chris J. schrieb:> Aber der Linker weiss es aus dem Linkerfile.... eigentlich muss der> alles wissen, auch zur Laufzeit.
Zur Laufzeit des Programms? Der Satz ergibt so keinen Sinn!
Chris J. schrieb:> Das ergibt sich aus der Codeananalyse.> In teuren IDEs sieht man auch wieviel Stack man braucht, da die eigene> Parser haben in den Editoren. Bei Segger siehste auch ganz genau wieviel> welches Modul braucht. Gehen tut das schon.
So, und wie will deine Codeanalyse zum Beispiel bei einer rekursiven
Funktion erkennen in welcher Rekursionstiefe die Rekursion (hoffentlich)
abbricht?
Solche Tools funktionieren immer nur für bestimmte (einfache) Szenarien.
Sie ersetzan aber nie Brain 2.0
Chris J. schrieb:> Kaj G. schrieb:>> Und was hat der Compiler damit zu tun? Der Compiler weiss nicht wie viel>> Speicher du hast.>> Aber der Linker weiss es aus dem Linkerfile.... eigentlich muss der> alles wissen, auch zur Laufzeit. Das ergibt sich aus der Codeananalyse.
Der Linker macht keine Code-Analyse.
> In teuren IDEs sieht man auch wieviel Stack man braucht, da die eigene> Parser haben in den Editoren.
Das reicht aber bei weitem nicht, um den Stackverbrauch zu kennen. Im
Prinzip müsstest du den kompletten Code ausführen. Allerdings hängt auch
dann der Stackverbrauch davon ab, welche Daten man zur Laufzeit
reinsteckt. Man muss also erstmal die Inputs kennen, die zu maximalem
Stackverbrauch führen.
Chris J. schrieb:> eigentlich muss der> alles wissen, auch zur Laufzeit.
Noe, kann er ja nicht wenn die Daten erst zur Laufzeit bekannt werden
(z.B. Daten ueber Netzwerk etc.) Wie soll Linker, Compiler, etc. das
ausmessen? Das geht nicht. Kein Tool kann wissen, ob da zur Laufzeit 1
Byte oder 10 TB reinkommen. Was ja auch schon gesagt wurde: Rekursion.
Mal angenommen du willst die Fakultaet per rekursiver Funktion
berechnen. Da macht es schon einen Unterschied ob du !6 oder !42
berechnest. Sind die Argumente aber nicht zur Compilezeit bekannt, kann
da auch nichts Analysiert werden.
Was ich oben verlinkt habe:
Mit dem Compilerflag -fstack-usage bekommst du den statischen
Stackverbrauch pro Funktion, daraus kannst du Pi x Daumen grob was zu
deinem Stackverbrauch sagen.
Chris J. schrieb:> eigentlich muss der alles wissen
Eigentlich muss der Programmierer alles wissen und sollte sich gedanken
zu seinen Datenstrukturen machen und selbst darauf aufpassen, dass sein
Speicher reicht. ;)