Forum: Compiler & IDEs c start up init lässt Variablen aus, was kann schief gehen


von H. R. (hacker_r)


Lesenswert?

Hi

in mein Freertos + LWIP + stm32 Projekt habe ich das problem dass
static    uint16_t MyVariable_u16[256u] = { 1, 2,3, 4,5,6,7,... };

""nicht immer"" richtig  initialisiter wird. Die meisten Elemente vom 
array haben den richtigen wert, einige nicht. Hat jnd eine Idee?
Das problem taucht auf wenn ich UDP packete schon vorm Einschalten zum 
STM32 schicke. Und selbst dann taucht es nicht immer auf!

Danke

von RAc (Gast)


Lesenswert?

Kann es sein, dass dein RAM ein SDRAM und noch nicht initialisiert ist, 
wenn die Startuproutine die copy Loop vom Flash zum RAM macht? 
Normalerweise hängt der Startupcode der C Runtime direkt am Reset 
Vektor, und die Prozessorinitialisierung (einschliesslich SDRAM 
Controller Initialisierung) dahinter. In der Konfiguration mit SDRAM 
musst Du dann Teile der Controllerinitialisierung vor die C 
Runtimeinitialisierung setzen.

von H. R. (hacker_r)


Lesenswert?

hi
nee benutze kein sdram;-(

von Jim M. (turboj)


Lesenswert?

Könnte auch Stacküberlauf sein, d.h. der Stack läuft in den 
Speicherbereich des Puffers rein.

Oder irgendein Puffer in LWIP läuft über.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Als erstes ist zu eruieren, ob der start-Code das Array korrekt 
initialisiert und es danach zerstört wird, oder ob was mit dem 
Start-Code, Linker Skript etc. nicht stimmt.

von Karl (Gast)


Lesenswert?

An diesel Stelle wäre dann noch auf das Symbol File hinzuweisen falls es 
nicht korrekt initialisiert. Das Alignment des Data Bereichs im Flash 
ist auch eine Quelle für Fehler.

von W.S. (Gast)


Lesenswert?

H. R. schrieb:
> static    uint16_t MyVariable_u16[256u] = {...

Es gibt eine Regel, an die man sich besser halten sollte: Was man 
benutzen will, das soll man zuvor auch SELBST initialisieren. Punkt.

Wer das mißachtet und sich in blindem Vertrauen auf irgendwelche 
angeblichen Selbstverständlichkeiten sowas verkneift, der ist selber 
schuld.

Abgesehen davon frag ich mich, wozu du sowas überhaupt brauchst. Wen du 
ein Feld von Konstanten haben willst, wäre es besser, sie im ROM 
anzusiedeln - und wenn es tatsächlich Variablen sein sollen, dann 
sollten deine Algorithmen so aufgebaut sein, daß sie eben auch mit 
uninitialisierten Variablen zurechtkommen. Static heißt ja nur, daß die 
Variable nicht auf dem Stack liegt, sondern statisch irgendwo im RAM und 
deshalb beim Verlassen einer Funktion nicht flöten geht.

W.S.

von S. R. (svenska)


Lesenswert?

W.S. schrieb:
>> static    uint16_t MyVariable_u16[256u] = {...
>
> Es gibt eine Regel, an die man sich besser halten sollte: Was man
> benutzen will, das soll man zuvor auch SELBST initialisieren. Punkt.

Tut er doch. Nur ist aus irgendeinem Grund der Inhalt beim Start 
gelegentlich kaputt. Er initialisiert mit {1, 2, 3} und findet {1, 2568, 
3}, wenn vor dem Start ein UDP-Paket geschickt wurde.

So lese ich die Frage.

Kann gut sein, dass der Netzwerkchip (oder der Bootloader) das erste 
Paket empfängt und an eine undefinierte Stelle im RAM schreibt, während 
der Startup-Code noch fleißig .data vom Flash ins RAM kopiert oder .bss 
nullt... dazu fehlt mir aber zu viel Kontext.

von Oliver S. (oliverso)


Lesenswert?

W.S. schrieb:
> Es gibt eine Regel, an die man sich besser halten sollte: Was man
> benutzen will, das soll man zuvor auch SELBST initialisieren. Punkt.

Es gibt eine Regel, an die man sich besser halten sollte: wenn sich ein 
Compiler mit seinem Laufzeitssystem nicht an den Sprachstandard hält, 
sollte man entweder das Problem verstehen und beheben (wie hier 
versucht), oder der Compiler nebst Zubehör gehört in die Tonne. Punkt.

Oliver

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Es gibt eine Regel, an die man sich besser halten sollte: Was man
> benutzen will, das soll man zuvor auch SELBST initialisieren. Punkt.

Wenn Du eine globale oder static-Variable bei der Deklaration explizit 
mit null initialisiert, führt das auch nur dazu, daß sie ins BSS gelinkt 
wird. Das ist dasselbe, als wenn man sie nur deklarieren würde. Wenn der 
Startupcode das BSS nicht nullt, geht beides schief.

Und wenn Du jetzt Gürtel UND Hosenträger anziehst und gleich in der main 
Deine globalen Variablen nochmal initialisierst, dann weiß der Compiler, 
daß er das rauswerfen kann, denn laut C-Standard sind sie ja schon mit 
null initialisiert.

Deine Regel ist somit völlig wertlos.

von X4U (Gast)


Lesenswert?

H. R. schrieb:
> ""nicht immer"" richtig  initialisiter wird. Die meisten Elemente vom
> array haben den richtigen wert, einige nicht. Hat jnd eine Idee?
> Das problem taucht auf wenn ich UDP packete schon vorm Einschalten zum
> STM32 schicke. Und selbst dann taucht es nicht immer auf!

Klingt nach Hardware.

Kenne mich mit STM wenig aus aber vielleicht überschreibt eine Variable 
im  UDP Interrupt den Bereich oder die Queue (Buffer) wird falsch 
ausgelesen.

Kannst du den ganzen eingelesenen Kram nicht verbieten und löschen?

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Wenn Du eine globale oder static-Variable bei der Deklaration explizit
> mit null initialisiert, führt das auch nur dazu...

Du hast es nicht gerafft, mein Lieber.

Wer also immer nur sowas hinschreibt
  static int A = 4711;
der verläßt sich auf eben die von mir angesprochenen Automatismen - und 
so etwas ist blauäugig.

Ich halte es ganz anders, indem ich tatsächlich das, was es zu 
initialisieren gilt, dediziert auch initialisiere. Das geht eben NICHT 
so wie im obigen Beispiel, sondern sinngemäß so:
int A;
void InitMeinDing(void)
{ A = 4711;
  etc.. etc..
}
womit eines sichergestellt ist, daß nämlich genau das, was tatsächlich 
zu initialisieren ist, auch wirklich initialisiert wird - auch dann, 
wenn man eben nicht aus dem µC-Reset herausgekommen ist, sondern z.B. 
nach einem Fault o.ä. eine Teil-Initialisierung vornehmen muß.

Abgesehen davon finde ich die Manie gar vieler C-Leute, möglichst alles 
von der Typdefinition über die Variablendeklaration bis zur Vorbelegung 
in eine Zeile schreiben zu wollen, zum Kotzen. Sowas ist ganz übler Stil 
und auch hier gilt es, daß man in C sowas durchaus hinschreiben kann, 
aber ob man diese Möglichkeit auch ausnutzt oder sich sowas verkneift, 
ist ne Stilfrage. So. Ähem.. Damen mal bitte wegschauen: Also, es ist 
durchaus nicht verboten, auf den Eßtisch zu scheissen, aber jeder 
gesittete Mensch tut sowas einfach nicht.

Was da manche durchaus prominenten Leute wie z.B. Chan mit seinem FF 
anstellen, indem sie sich auf das verlassen, was du offenbar als 
selbstverständlich ansiehst, hab ich zur Genüge durch. Danke, sowas 
nicht mit mir. Ich bevorzuge es, möglichst zuverlässigen Code zu 
schreiben.

W.S.

von Markus F. (mfro)


Lesenswert?

W.S. schrieb:
> Wer also immer nur sowas hinschreibt
>   static int A = 4711;
> der verläßt sich auf eben die von mir angesprochenen Automatismen - und
> so etwas ist blauäugig.

Na ja. Kann man machen, muß man aber nicht.

Sich für den Startup-Code genauso wie für jeden anderen Bestandteil des 
Programms verantwortlich zu fühlen und dafür zu sorgen, dass der so 
funktioniert, dass man sich darauf verlassen kann, reicht auch. 
Schließlich ist der genau dazu da. Nur leider scheinen sich viele erst 
dann dafür zu interessieren, wenn was nicht so tut, wie's soll.

Im Übrigen scheint mir hier noch längst nicht geklärt, ob's daran 
überhaupt liegt oder bloss an irgendeinem schludrigen Überschreiber.

Da hilft dein Gürtel und Hosenträger nämlich auch nix.

von Peter D. (peda)


Lesenswert?

W.S. schrieb:
> Ich halte es ganz anders ...

Man kann sich auch die Hose mit der Kneifzange anziehen, nen Knopf an 
die Backe nähen, nen Pudding ans Knie nageln usw..
Du solltest es nur nicht anderen aufoktroyieren.

von Nop (Gast)


Lesenswert?

W.S. schrieb:
> Nop schrieb:
>> Wenn Du eine globale oder static-Variable bei der Deklaration explizit
>> mit null initialisiert, führt das auch nur dazu...
>
> Du hast es nicht gerafft, mein Lieber.

Lies genauer, was ich schrieb. Ich bezog mich auf die 
Nullinitialisierung (die der häufigste Fall ist), Du bringst als 
Beispiel eine mit 4711. Finde den Fehler.

> Ich halte es ganz anders, indem ich tatsächlich das, was es zu
> initialisieren gilt, dediziert auch initialisiere.

Und wie initialisierst Du funktionslokale static-Variablen? Oder 
verwendest Du sowas nicht?

> Ich bevorzuge es, möglichst zuverlässigen Code zu schreiben.

Tja, und ich bevorzuge es, meine Umgebung im Startupcode korrekt so 
aufzusetzen, daß sie vor der main Bedingungen herstellt, die dem 
C-Standard entsprechen. Das ist noch zuverlässiger, weil es 
sicherstellt, daß auch künftige Compilerversionen keine Startup-Inits 
rausoptimieren, die sie gemäß C-Standard und as-if-Regel durchaus 
wegoptimieren dürfen.

von H. R. (hacker_r)


Lesenswert?

Hi Danke für die vielen Inputs. Die Lösung:
Mein Firmware geht beim powerup erst durch den bootloader.
Beim austritt von bootloader, hat der bootloader den LWIP und ethernet 
und co nicht ausgeschaltet. So konnte es passieren dass im Bootloader 
ein DMA mit cstartup routine in die quere kam .
Wtf

von Oliver S. (oliverso)


Lesenswert?

W.S. schrieb:
> womit eines sichergestellt ist, daß nämlich genau das, was tatsächlich
> zu initialisieren ist, auch wirklich initialisiert wird - auch dann,
> wenn man eben nicht aus dem µC-Reset herausgekommen ist, sondern z.B.
> nach einem Fault o.ä. eine Teil-Initialisierung vornehmen muß.

Auf einem System, das sich nicht nach bekannten Spezifikationen verhält, 
sondern irgendwie irgendwas macht, kannst du dich auf gar nichts 
verlassen. Da ist es am Ende reiner Zufall, wenn etwas anscheinend 
funktioniert.

Ansonsten ist eine init-Funktion, die alles wichtige initialisiert, 
natürlich das Mittel der Wahl, wenn man das braucht.

Oliver

von W.S. (Gast)


Lesenswert?

Nop schrieb:
> Tja, und ich bevorzuge es, meine Umgebung im Startupcode korrekt so
> aufzusetzen, daß sie vor der main Bedingungen herstellt, die dem
> C-Standard entsprechen.

Jaja, genau DAS hat Chan auch mehrfach betont - und im Ergebnis 
klatscht sein Code gelegentlich nach Wechsel der SD-Karte auf, eben 
weil er felsenfest davon ausgeht, daß ja ein Zeiger auf seine 
Datenstrukturen valid sein muß, wenn er nicht null ist.

Ich schrieb ja schon, daß all die Leute, die sich auf sowas verlassen - 
mit der Begründung, daß ja der C-Standard dies und jenes sagt - viel zu 
blauäugig sind. Was nützt es dir denn, daß du im Kaltstart deinen RAM 
ausnullst, wenn du dich (so wie Chan) drauf verläßt, ohne wirklich 
alle (ALLE!) Rand-Bedingungen berücksichtigt zu haben? Aber wer kann das 
schon? Niemand natürlich. Genau deshalb ist es die einzig sichere Seite, 
grundsätzlich anzunehmen, daß jede Variable erstmal unbestimmten inhalt 
hat, solange man nicht wenigstens ein mal in der zugehörigen 
Initialisierung ihr einen Wert zugewiesen hat.

Nebenbei gesagt, gilt sowas auch für Hardware-Defaults, gelle?

Nochwas: angesichts der Worte des TO "Die meisten Elemente vom
array haben den richtigen wert, einige nicht" ist meine Vermutung, daß 
es irgendwo anders einen nicht oder erstmal falsch initialisierten 
Zeiger gibt, der gelegentlich (je nach Interrupt-Geschehen) dafür sorgt, 
daß mitten in besagtem Array nach dessen Vorbelegung im Startup etwas 
geschrieben wird. Und das macht dann das Problem aus.

W.S.

von W.S. (Gast)


Lesenswert?

Oliver S. schrieb:
> Auf einem System, das sich nicht nach bekannten Spezifikationen verhält,
> sondern irgendwie irgendwas macht, kannst du dich auf gar nichts
> verlassen.

Nee, auch auf einem System, das sich brav an alle Konventionen hält, 
kann man sich auf manches nicht verlassen.

Der Grund dafür ist ganz generell, daß sich die Welt eben nicht an 
Spezifikationen hält.

Bei einem C-Programm auf dem PC kann man sich schon drauf verlassen, daß 
man den benutzten RAM ausgenullt vorfindet - eben weil da ja nix 
dazwischenkommen kann.

Auf einem µC gilt das hingegen nicht mehr. Dort hat man es immer mit der 
Möglichkeit zu tun, daß Dinge auftreten, die man in keiner Spezifikation 
vorsehen kann, wie z.B. Busfaults, Störungen aller Art, Unterschiede im 
Default-Zustand je nach Art des Reset's (HW, SW, Brownout, und was es 
sonst noch so geben mag) und so weiter. Das deckt dir KEIN C-Standard ab 
- und manchmal nicht einmal das RefMan des µC - oder berücksichtigst du 
gewissenhaft wirklich alle Errata-Notes, die es zur verwendeten 
Revision deiner HW gibt?

W.S.

von Nop (Gast)


Lesenswert?

W.S. schrieb:

> Jaja, genau DAS hat Chan auch mehrfach betont - und im Ergebnis
> klatscht sein Code gelegentlich nach Wechsel der SD-Karte auf

Was mal so überhaupt nichts mit startup-Init zu tun hat, sondern damit, 
eine notwendige Änderung im Betrieb auch durchzuführen.

> Genau deshalb ist es die einzig sichere Seite,
> grundsätzlich anzunehmen, daß jede Variable erstmal unbestimmten inhalt
> hat, solange man nicht wenigstens ein mal in der zugehörigen
> Initialisierung ihr einen Wert zugewiesen hat.

Ich sage es gerne nochmal, wieso das in der Form Unsinn ist. Weil der 
C-Compiler annehmen darf, daß globale Variablen, welche genullt 
initialisiert sein sollen, beim Betreten der main bereits genullt sind. 
Eine explizite Nullung darf er daher einfach wegoptimieren, und wenn 
nicht jetzt, dann nach dem nächsten Compiler-Update. Das folgt so aus 
dem C-Standard.

Wer meint, eine nicht-standardkonforme Umgebung mit eigenen Hackarounds 
kompensieren zu können, der irrt schlichtweg, und das auch noch als die 
ultimativ sichere Lösung anzupreisen, ist erheiternd.

von S. R. (svenska)


Lesenswert?

W.S. schrieb:
> Der Grund dafür ist ganz generell, daß sich die Welt eben nicht an
> Spezifikationen hält.

Damit wären Spezifikationen aber grundsätzlich nutzlos und die Realität 
sagt, dass dem nicht so ist.

Ich fürchte, dass du lieber so defensiv programmierst, weil du nicht 
willens bist, deine Umgebung korrekt herzustellen. Oder, im besseren 
Fall, es dir nicht erlaubt ist.

von Nop (Gast)


Lesenswert?

S. R. schrieb:
> Oder, im besseren Fall, es dir nicht erlaubt ist.

Das ist, wenn man hier von embedded redet, doch ohnehin komisch, weil 
man seinen Startupcode doch entweder selber schreibt oder zumindest mal 
überprüft, was zwischen dem Einsprung in den Resetvektor und dem Aufruf 
der main() alles vor sich geht. Da hat man doch im Unterschied zum PC 
sowieso die volle Kontrolle.

von S. R. (svenska)


Lesenswert?

Nicht, wenn ein Bootloader vorgeschrieben ist, der mit heißer Nadel aus 
zwei Stückchen Scheiße und ein paar Hühnerbeinen von einem Praktikanten 
am Wochenende zusammengefrickelt wurde...

Der OP hatte ja genau so ein Problem.

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.