Forum: Compiler & IDEs Problem mit static initialisierung, MPLAB X, PIC18F


von Enrico K. (ekoeck)


Angehängte Dateien:

Lesenswert?

Hallo liebes Forum!

Ich habe ein Problem, wo ich mir die Haare ausreißen könnte und hoffe, 
dass mir jemand einen Tip geben kann, warum dem so ist bzw. was ich 
missachte (das Problem sitzt ja i.A. vor dem Rechner).

Hard Facts:
MPLAB X IDE v3.05
xc8 v1.33
PIC18F252

Zu meinem Problem:
Ich habe eine Funktion, in welcher beim ersten Aufruf ein Array geladen 
werden soll (aus externem EEPROM). Da es aber bei späteren Aufrufen 
genau so weiter benötigt wird habe ich mich dafür entschieden es a) als 
lokal static zu definieren (1) und b) das Array um ein Feld länger 
gemacht als benötigt, worin ich speichere, ob es schon geladen wurde. 
Wenn ich jetzt beim ersten Aufruf der Funktion mir im Debugger die Werte 
anschaue (siehe Screenshot) sind sie alles, aber nicht richtig 
initialisiert (2). So schließe ich daraus, dass das Array schon 
bestehenden RAM überschreibt und quasi Amok läuft. Warum wird es nicht 
richtig initialisiert? Ich verstehe es nicht.

MPLAB XC8 C Compiler User's Guide (DS0002053E) Seite 175:
(1) "All static variables have permanent storage duration, even those 
defindes inside a function which are "local static" variables."
(2) "Variables which are static and which are initialized only have 
their initial value assigned once during the program's execution."

Anmerkungen:
- selber Effekt, wenn das Array global definiert wird (was ich aber 
gerne
umschiffen würde)
- Länge des Arrays 242 x 1 Byte

Quellcode:
1
uint8_t pearson(uint8_t dataY[])
2
{
3
    // TODO: check for better readable variables if sufficient RAM
4
5
    static uint16_t sumX;
6
    static uint32_t NsumXX;
7
    static uint8_t refSpec[SPECPOINTS + 1] = {0};
8
    
9
    uint32_t NsumXY = 0;
10
    uint32_t NsumYY = 0;
11
    uint16_t sumY = 0;
12
    uint16_t factorX, factorY;
13
    uint16_t result;
14
15
    uint8_t i;
16
17
    LEDPEARSON = 1;
18
    
19
    if (refSpec[SPECPOINTS] == FIRSTRUN)
20
    {
21
        loadSpectrum(&sumX, &NsumXX, &refSpec[0]);
22
    }

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wenn ich das richtig lese, hat der PIC18F252 1,5KB RAM. Kann es sein, 
dass Du zu diesem Zeitpunkt so viele aktive Auto-Variablen(-Arrays) auf 
dem Stack hast, dass der Stack bereits in den Heap (in dem auch Dein 
static-Array steckt) gelaufen ist?

Ich habe keine Ahnung, wie das RAM beim PIC aufgeteilt wird in Heap und 
Stack.

P.S.
Die Initialisierung

  static uint8_t refSpec[SPECPOINTS + 1] = {0};

kannst Du Dir sparen. statics sind per Definition mit 0 initialisiert.

: Bearbeitet durch Moderator
von Enrico K. (ekoeck)


Angehängte Dateien:

Lesenswert?

So etwas in die Richtung vermute ich auch, kann es aber an nichts 
speziellem fest machen da:
a) Mein Programm empfängt nur über USART einen Befehl und führt 
anschließend oben genannten Code aus.
b) Der PIC hat (soweit ich das beurteilen kann) keinen Heap. Der Stack 
liegt extra.
c) Das Array wird vom Linker an die Adresse 0x200 geschoben, was der 
zweiten (von 5) Bank entspricht. Bank 4 und 5 sind laut Linker-Map leer, 
woraus ich schließe, dass es kein Speicherproblem im Allgemeinen sondern 
eine komische Art Speicherverwaltungsproblem ist. Was ich aber nicht 
verstehe. Also garnicht. Kein bisschen. :(

PS: die Initialisierung ist zur besseren Lesbarkeit des Quellcodes da, 
da ja auf == 0 überprüft wird (und wird vom Compiler herausoptimiert).

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Du könntest natürlich in
1
    if (refSpec[SPECPOINTS] == FIRSTRUN)
2
    {
3
        loadSpectrum(&sumX, &NsumXX, &refSpec[0]);
4
    }

einbauen:
1
    if (refSpec[SPECPOINTS] == FIRSTRUN)
2
    {
3
        for (i = 0; i < SPECPOINTS - 1; i++)
4
        {
5
            refSpec[i] = 0;
6
        }
7
        loadSpectrum(&sumX, &NsumXX, &refSpec[0]);
8
    }

Aber das würde nur ein Doktern an den Symptomen sein. Ausserdem ist 
refSpec[SPECPOINTS] ja schon laut Debugger != 0 und damit wird dieses if 
überhaupt nicht zutreffend.

Kannst Du möglichst viel auskommentieren, bevor diese Funktion 
aufgerufen wird? Besteht dann irgendwann dieses Array doch aus vielen 
Nullen, könntest Du durch sukzessives Einkommentieren den Übeltäter, 
welcher Dir das RAM vollschreibt, finden.

Oder der XC8 hat einen Bug, nämlich dass er statics nicht 
initialisiert...

P.S.
Nimm versuchsweise trotzdem mal die Initialisierung raus. Vielleicht 
schaltest Du mit dem expliziten Init die implizite Initialisierung ab. 
Ich weiß nicht, ob der XC8 das wegoptimiert, der gcc konnte das früher 
jedenfalls nicht - neuere Versionen schon.

: Bearbeitet durch Moderator
von X4U (Gast)


Lesenswert?

Frank M. schrieb:
> Die Initialisierung
>
>   static uint8_t refSpec[SPECPOINTS + 1] = {0};
>
> kannst Du Dir sparen. statics sind per Definition mit 0 initialisiert.

Würde ich mich nicht drauf verlassen. Da die Controller klein sind kann 
es sein das die aus Performancegründen nicht auf 0 gesetzt werden. Bei 
MikroE C ist das z.B. definit so.

> "Variables which are static and which are initialized only have
> their initial value assigned once during the program's execution."

deutet darauf hin das du ihr init value explizit angeben sollst.

> if (refSpec[SPECPOINTS] == FIRSTRUN)
>     {
>         loadSpectrum(&sumX, &NsumXX, &refSpec[0]);
>     }

kannst du doch einfach beim Initialisieren laden. Pack die Routinen in 
die gleiche unit dann haben Sie "file scope".

von Enrico K. (ekoeck)


Lesenswert?

Ini drin oder draußen macht keinen Unterschied. Ich habe testweise das 
Array mit
1
static uint8_t refSpec[SPECPOINTS + 1] __at(0x400) = {0};
meiner Meinung nach mehrfach gezwungen endlich mit Null initialisiert zu 
werden, da es nun auch in einem Speicherbereich ist, wo laut linker weit 
und breit nix passiert. Und es wird trotzdem nicht korrekt 
initialisiert. Ist mein PIC vielleicht im Eimer? Aber dann würde ja der 
Rest auch nicht funktionieren, vermute ich. Das mit Übeltäter finden 
probiere ich gerade, gestaltet sich bloß etwas kompliziert mit nur 
Variablen im Scope beobachten und sagenhaften einem Breakpoint....


>> if (refSpec[SPECPOINTS] == FIRSTRUN)
>>     {
>>         loadSpectrum(&sumX, &NsumXX, &refSpec[0]);
>>     }
>
> kannst du doch einfach beim Initialisieren laden. Pack die Routinen in
> die gleiche unit dann haben Sie "file scope".

Eben das ist ja das Problem, dass die Werte nach dem initialisieren 
Überschrieben werden... von wo auch immer. Herumvegetierende Zeiger habe 
ich nicht zu bieten ...

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Enrico K. schrieb:
> Das mit Übeltäter finden
> probiere ich gerade, gestaltet sich bloß etwas kompliziert mit nur
> Variablen im Scope beobachten und sagenhaften einem Breakpoint....

Hat das Ding echt nur einen Breakpoint ?
Benutze ich schon sooo lange nicht mehr, dass ich mich gar nicht 
erinnern kann.
Kannst du einen Datenbreakpoint setzen ?

<edit>sieht so aus als gäbe es keinen Datenhaltepunkt
"breakpoints (numhwbp=1 datacapture=false idbyte=p)"
sorry</edit>

Als Alternative gäbe es noch TRAP
-> http://www.microchip.com/forums/FindPost/434323

: Bearbeitet durch User
von X4U (Gast)


Lesenswert?

Enrico K. schrieb:
> Ini drin oder draußen macht keinen Unterschied. Ich habe testweise
> das Array mit static uint8_t refSpec[SPECPOINTS + 1] __at(0x400) = {0};
> meiner Meinung nach mehrfach gezwungen endlich mit Null initialisiert zu
> werden,

widerspricht sich mit


> Eben das ist ja das Problem, dass die Werte nach dem initialisieren
> Überschrieben werden... von wo auch immer. Herumvegetierende Zeiger habe
> ich nicht zu bieten ...

oder ist es keine Initialisierung und danach wird das auch noch 
überschrieben?



static uint8_t refSpec[SPECPOINTS + 1] __at(0x400) = {0};

grenzt das Problem ja nicht ein.  Ist internes flash ist und ich weiß 
nicht wie du dem compiler sagst das es für deine Daten reserviert sein 
soll.

Lade deine Daten hart (ohne condition) aus deinen EEPROM und dann schau 
nach ob die auch da sind.

> Da es aber bei späteren Aufrufen
> genau so weiter benötigt wird habe ich mich dafür entschieden es a) als
> lokal static zu definieren (1) und b) das Array um ein Feld länger
> gemacht als benötigt, worin ich speichere, ob es schon geladen wurde

static vars sind ja reserviert. Warum willst du da jedes mal beim Aufruf 
der routine prüfen ob die schon geladen sind statt Sie gleich zu laden?

von Wolfgang R. (portside)


Lesenswert?

Versuch mal
volatile static uint8_t refSpec[SPECPOINTS + 1] = {0};

Eventuell macht der Optimierer was er will.

Im generierten Assembler listing  sollte man alles finden, da stehen 
auch die C Code Zeilen als Kommentar drin. Mit einem ordentlichen Editor 
nach refSpec suchen.

von Volker S. (vloki)


Lesenswert?

Volker S. schrieb:
> Kannst du einen Datenbreakpoint setzen ?
>
> <edit>sieht so aus als gäbe es keinen Datenhaltepunkt
> "breakpoints (numhwbp=1 datacapture=false idbyte=p)"
> sorry</edit>

Mist, muss mich gleich nochmal entschuldigen.
"Datacapture" ist anscheinend was anderes.
Du solltest also einen Datenbreakpoint auf die Adresse des Arrays setzen 
können. Rechtsklick zu Öffnen des Kontextmenüs in einem Sourcefile oder 
[Strg][Umschalt][F11] und beim Schreiben ...



Volker S. schrieb:
> Als Alternative gäbe es noch TRAP
> -> http://www.microchip.com/forums/FindPost/434323

Scheint beim XC8 __debug_break() zu heißen (noch nie getestet ;-)
-> http://www.microchip.com/forums/FindPost/779388

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

X4U schrieb:
> Frank M. schrieb:
>> Die Initialisierung
>>
>>   static uint8_t refSpec[SPECPOINTS + 1] = {0};
>>
>> kannst Du Dir sparen. statics sind per Definition mit 0 initialisiert.
>
> Würde ich mich nicht drauf verlassen. Da die Controller klein sind kann
> es sein das die aus Performancegründen nicht auf 0 gesetzt werden. Bei
> MikroE C ist das z.B. definit so.

Dann darf der sich nicht "C Compiler" nennen. Die Initialisierung von 
statics und anderen globalen Variablen mit 0 - wenn nicht anders 
angegeben - ist schon seit 1970 Standard, also so lange, wie es C gibt.

von X4U (Gast)


Lesenswert?

Frank M. schrieb:
> Dann darf der sich nicht "C Compiler" nennen. Die Initialisierung von
> statics und anderen globalen Variablen mit 0 - wenn nicht anders
> angegeben - ist schon seit 1970 Standard, also so lange, wie es C gibt.

Falscher Thread.

von Enrico K. (ekoeck)


Lesenswert?

Rätsel gelöst. In einer anderen Prozedur ca. 2853 Codezeilen entfernt 
habe ich ein Array selber größe (lokal), welches zu Berechnungszwecken 
populiert wird. Dieses war nicht als static definiert, da nach Ende der 
Prozedur die Werte keine Relevanz mehr hatten. Nur hat der liebe Linker 
diese Werte (für mich nicht nachvollziehbar, warum) an den Platz meines 
static-Arrays geschrieben. Für mich klingt das nach einem Linker-Fehler, 
da ja (siehe oben) eine static-Variable eine feste Adresse hat, welche 
für andere Daten tabu ist... falls jemand dazu noch ein paar 
Sachdienliche Hinweise hat wäre ich sehr erfreut diese zu hören (um 
solche Fehler in Zukunft zu vermeiden).
Sonst danke ich allen Teilnehmern für die sachdienlichen Hinweise, 
wieder was gelernt ;)

von Volker S. (vloki)


Lesenswert?

Enrico K. schrieb:
> Nur hat der liebe Linker
> diese Werte (für mich nicht nachvollziehbar, warum)

Manchmal dauert es ein bisschen ...



Enrico K. schrieb:
> an den Platz meines
> static-Arrays geschrieben. Für mich klingt das nach einem Linker-Fehler,
> da ja (siehe oben) eine static-Variable eine feste Adresse hat, welche
> für andere Daten tabu ist... falls jemand dazu noch ein paar
> Sachdienliche Hinweise hat

Wenn du ein gepacktes Minimal-Projekt posten würdest, welches das 
Verhalten zeigt, könnte man ja mal schauen ;-)

: Bearbeitet durch User
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.