Forum: Mikrocontroller und Digitale Elektronik Atmel Studio probleme wenn optimization aktiv


von Andreas W. (andy_w)


Lesenswert?

Hallo,
ich habe eine Funktion, die ohne Optimierung funktioniert, wird mit 
Optimierung compiliert, macht sie definitiv was falsch.

Hier der Code der Funktion:
1
/////////////////////////////////////////////////////////////////////////
2
//
3
// file function:
4
// read device names from RC.TXT
5
//
6
//  parameters:
7
//
8
// return:
9
// unit8_t                          0 when OK, 1 when EOF or error
10
//
11
/////////////////////////////////////////////////////////////////////////
12
uint8_t aw_readDeviceNames(void)
13
{
14
   FIL         fp1;
15
   FRESULT     res;
16
   uint8_t     c;
17
   uint16_t    i, j, repeat;
18
   uint32_t    error;
19
20
   device_name[0][0] = 'A';
21
   device_name[0][1] = 'M';
22
   device_name[0][2] = 'P';
23
   device_name[0][3] = 0;
24
   device_name[0][4] = 0;
25
   device_name[0][5] = 0;
26
   device_name[0][6] = 0;
27
   device_name[0][7] = 0;
28
   no_devices = 1;
29
30
   repeat = 0;
31
   do 
32
   {
33
      filename[0]  = 'R';
34
      filename[1]  = 'C';
35
      filename[2]  = '.';
36
      filename[3]  = 'T';
37
      filename[4]  = 'X';
38
      filename[5]  = 'T';
39
      filename[6]  = 0;
40
      res = f_open(&fp1, (char*)filename, FA_OPEN_EXISTING | FA_READ);
41
      if (res != FR_OK)
42
      {
43
         error = 1;
44
      }
45
      filesize = (uint32_t)fp1.fsize;
46
      i = 1;
47
      error = 0;
48
      while ((error == 0) && (i < 16))
49
      {
50
         error = aw_readLine(&fp1, buf);
51
         if (error == 0)
52
         {
53
            for (j = 0; j < 8; j++)
54
               device_name[i][j] = 0;
55
            j = 0;
56
            c = buf[j];
57
            while ((c >= 0x20) && (j < 7))     // max 7 characters
58
            {
59
               device_name[i][j++] = c;
60
               c = buf[j];
61
            }
62
            if (j != 0)
63
               i++;
64
         }
65
      }
66
      res = f_close(&fp1);
67
      error = (filesize != 0);
68
      no_devices = i;
69
      repeat++;
70
   } while ((error != 0) && (repeat < 10));
71
72
   return error;
73
}

Die Funktion liest die Datei RC.TXT ein und speichert Namen (je ein Name 
pro Zeile in der Datei) im Feld device_name[16][8] ab. Der erste Eintrag 
ist immer gleich, erst ab dem zweiten werden die Namen von der Datei 
aufgefüllt. Wenn das Dateilesen nicht klappt, wird das bis zu 10-mal 
wiederholt, da ich immer noch Probleme mit den Funktionen für den 
SD-Kartenzugriff habe. Zwar nicht beim Lesen der kleinen Textdateien, 
wohl aber beim Lesen größerer Dateien. Schreiben versuche ich momentan 
gar nicht mehr, das funktioniert momentan gar nicht...

Das Problem betrifft die Variable i. Sie wird nach dem f_open auf 1 
gesetzt, nach dem Aufruf von aw_readLine(&fp1, buf) ist i = 0, wie ich 
mit dem Debugger sehe. Als Folge schreibt die Funktion alle Namen ins 
erste Feld, alle anderen bleiben unberührt. Und so passiert das auch, 
wenn es ohne Debugger läuft. Ohne Optimierung funktioniert die Funktion 
aber normal. Ich wüßte auch nicht, warum ich die Variable i volatile 
machen sollte, die Funktion aw_readLine() kann doch nicht die lokale 
Variable i verändern. Mache ich i, j und repeat static, wird i richtig 
gezählt, dafür wird die Zeile device_name[i][j++] = c; nicht mehr 
ausgeführt. Das sieht man daran, daß j auf Null bleibt und nun gar keine 
Namen eingetragen werden, auch das Verhalten bleibt ohne Debugger. 
device_name ist eine globale Variable.

Hat jemand schon ähnliche Probleme gehabt?

Gruß
Andy

von Peter II (Gast)


Lesenswert?

Andreas W. schrieb:
> Das Problem betrifft die Variable i. Sie wird nach dem f_open auf 1
> gesetzt, nach dem Aufruf von aw_readLine(&fp1, buf) ist i = 0, wie ich
> mit dem Debugger sehe.

dann debugge doch einfach mal aw_readLine, irgendwo muss dort dann der 
fehler sein.

Vermutlich ist buf aber zu klein, aber dafür fehlt uns der Rest von code 
für eine sinnvolle Aussage.

von klausr (Gast)


Lesenswert?

Klingt für mich nach RAM Überlauf. Welchen uC verwendest du? Lokale 
Variablen liegen auf dem stack, static Variablen im bss Segement. 
Möglicherweise braucht dein Programm (inzwischen) zu viel RAM und 
deswegen kracht es. Wie optimierst du? -Os oder -O2?

von Andreas W. (andy_w)


Lesenswert?

Hallo,
ich vermute auch, daß es ein RAM oder auch Stacküberlauf sein könnte. 
Aber wie kann man das feststellen? Der Prozessor ist ein SAM3X8E 
(Arduino Due). Bisher habe ich noch keine Möglichkeit gefunden, z.B. die 
Stackgröße irgendwo zu definieren. buf selbst ist ziemlich groß, 
momentan mit int8_t buf[4096] definiert. Ich kann ja mal versuchen, das 
Feld kleiner zu machen, momentan brauche ich es nicht so groß. Wo kann 
man denn die Speicherbelegung sehen, die der Compiler wählt? Sollter der 
nicht meckern, wenn das RAM nicht reicht, statt die Daten sich 
überlappen zu lassen?

Besonders habe ich auch den Stack in Verdacht, daß der zu klein ist, 
aber die Hilfe von Atmel erzählt dazu fast nichts. Besonders nichts, wie 
man die reservierte Stackgröße anpassen kann. Weiß da jemand was? 
Vielleicht ist das auch der Grund, daß die SD-Kartenfunktionen nicht 
einwandfrei laufen.

aw_readLine() funktioniert einwandfrei, auch im optimierten Code und hat 
nichts mit der lokalen Variable i zu tun, die danach verändert wurde.

Gruß
Andy

von c-hater (Gast)


Lesenswert?

Andreas W. schrieb:

> ich habe eine Funktion, die ohne Optimierung funktioniert, wird mit
> Optimierung compiliert, macht sie definitiv was falsch.

Dann ist in aller Regel irgendwas in der Funktion falsch. Möglicherweise 
ist aber auch außerhalb der Funktion etwas falsch, wirkt sich aber (mehr 
oder weniger zufällig) genau nur in der konkreten Funktion aus.

Anhand deiner Beschreibung und der Tatsache, dass in der geposteten 
Funktion kein offensichtlicher Fehler zu erkennen ist (vielleicht habe 
ich ihn aber auch nur übersehen, C ist ja so dermaßen Scheiße zu lesen) 
würde ich letzteres vermuten.

Genau könnte ich sagen, was passiert, wenn du das Hexfile des 
Gesamtwerkes postest und zwar in beiden Varianten, mit und ohne 
Optimierung.

Ich würde aber mit 99,9%iger Sicherheit auf schlampigen Code tippen. Nur 
das winzige verbleibende Promille gäbe ich auf einen tatsächliche 
Compilerfehler.

Da wäre dann ggf. zu klären, welchen Compiler in welcher Version du 
benutzt. Möglicherweise hast du ja auch "nur" einen bereits bekannten 
Optimizer-Fehler getriggert...

von Tom (Gast)


Lesenswert?

c-hater schrieb:
> Sicherheit auf schlampigen Code tippen

Sowas
1
uint8_t aw_readDeviceNames(void)
2
uint32_t    error;
3
return error;
spricht sehr dafür. Zusammen mit Variablennamen i und j, die keine 
einfachen Schleifenzähler sind, hat das dazu geführt, gar nicht erst 
weiter zu lesen. Wer Compilerwarnungen ignoriert, hat offensichtlich 
sowieso kein Interesse an funktionierendem Code.

von Peter II (Gast)


Lesenswert?

Tom schrieb:
> Sowasuint8_t aw_readDeviceNames(void)
> uint32_t    error;
> return error;
> spricht sehr dafür.

wo hast du ein Problem mit error? Nur weil es am Anfang nicht 
Initalisiert ist? Es wird später in jedem Fall auf einen wert gesetzt, 
es gibt keine Möglichkeit das error am ende nicht definiert ist.

von Kaj (Gast)


Angehängte Dateien:

Lesenswert?

Andreas W. schrieb:
> Bisher habe ich noch keine Möglichkeit gefunden, z.B. die
> Stackgröße irgendwo zu definieren.
Die größen Definitionen von Stack und Heap findest du in den 
LinkerScripten.

von Georg G. (df2au)


Lesenswert?

Peter II schrieb:
> ein Problem mit error?

error ist 32 Bit, laut Definition sollen aber 8 Bit zurück gegeben 
werden. Da kommt der Compiler ins schwitzen. Welcher Compiler ist es, 
der da keine Warnung ausgibt?

von Peter II (Gast)


Lesenswert?

Georg G. schrieb:
> error ist 32 Bit, laut Definition sollen aber 8 Bit zurück gegeben
> werden. Da kommt der Compiler ins schwitzen. Welcher Compiler ist es,
> der da keine Warnung ausgibt?

Warnung ist doch ok, hier kann es aber zu keinen Fehler kommen, weil in 
Error nur 0 oder 1 steht, damit gehen beim ändern vom Datentype NIE 
Daten verloren.

von Georg G. (df2au)


Lesenswert?

Peter II schrieb:
> hier kann es aber zu keinen Fehler kommen

Wie werden Rückgabe Werte übergeben?
Im Register? Der Aufrufer erwartet in R1 die Rückgabe und glaubt, dass 
R2 bis R5 unbeschadet bleiben. Die Rückgabe erfolgt aber in R1 bis R4 
und schon ist eine lokale Variable im Eimer.

Auf dem Heap? 4 Bytes werden belegt und nur eines wird abgeräumt. Peng.

Dein Vertrauen in den Compiler ehrt dich.

von Peter II (Gast)


Lesenswert?

Georg G. schrieb:
> Dein Vertrauen in den Compiler ehrt dich.

wenn das ein Compiler hin hinbekommt dann kann man ihn nur entsorgen.
1
uint32_t b = 42;
2
uint8_t a = b;

muss jeder Compiler ordentlich umsetzen, er darf dabei eine Warnung 
werfen das eventuell Daten verloren gehen mehr aber nicht.

von c-hater (Gast)


Lesenswert?

Georg G. schrieb:

> Dein Vertrauen in den Compiler ehrt dich.

Ehren?

Wer nicht begreift, dass ein Compiler auch nur ein Stück Software mit 
(stark beschränkter) Maschinenintelligenz ist, der braucht wohl keine 
Ehrung, sondern eher einen kraftigen Tritt in den verlängerten Rücken.

Oder darf man noch "Arsch" sagen?

von Andreas W. (andy_w)


Lesenswert?

Hallo,
das Verkleinern von buf[4096] auf buf[128] hat zumindest die Funktion 
auch mit Optimierung wieder richtig laufen lassen. Eine Warnung, daß RAM 
zu knapp wird, bekomme ich aber nicht einmal, wenn ich sämtliche 
Warnungen incl. "pedantic" aktiviere. Den Stack-size habe ich irgendwann 
auch bei mir gefunden, bei mir ist der Pfad aber etwas anders und die 
Datei heißt nur flash.ld. Da war die Size auf 0x2000, die habe ich auf 
0x3000 heraufgesetzt. Ein Heapsize ist in meiner Datei flash.ld aber 
nicht definiert.

Seltsam war, daß das Lesen von Dateien von der SD-Karte mit optimiertem 
Code recht gut geht, nur manchmal muß ich bei längeren Dateien (auch nur 
wenige kB, aber mehr als ein Sektor) mehrere Versuche machen. Ohne 
Optimierung klappte das Lesen fast nie, nur ganz selten. Eigentlich 
sollte langsamer doch eher funktionieren? SPI wird allerdings mit der 
prozessoreigenen Hardware erledigt, d.h. der SPI-Clock hat die gleiche 
Frequenz. Schreiben funktioniert immer noch nicht, aber das scheint auch 
ein Timingproblem zu sein, denn ein einzigesmal hat es doch geklappt.

Ich habe auch eine Interruptfunktion, hier der vereinfachte Code:
1
void SysTick_Handler(void)
2
{
3
   uint8_t     n;
4
5
   n = Timer0;
6
   if (n) Timer0 = --n;
7
8
   return;
9
}

Timer0 ist eine globale Variable und soll für Delays usw. dienen. Die 
wird auch von ff.c für die SD-Funktionen genutzt.

Im Hauptprogramm wird sie folgendermaßen initialisiert, ich bin mir aber 
nicht sicher, ob das so richtig ist:
1
int main(void)
2
{
3
   uint32_t    udata, rc_unit, error, color;
4
   int32_t     i, j, k, idata, turned_local;
5
   uint8_t     c;
6
   int16_t     yn;
7
   uint16_t    xx, yy;
8
   uint8_t     spi_tx[64];
9
   uint8_t     spi_rx[64];
10
11
   SystemInit();                      // Initialize the SAM system
12
   WDT->WDT_MR = 0x00008000;          // disable Watchdog
13
   if (SysTick_Config(sysclk_get_cpu_hz() / 1000))
14
      while (1) {}  //Capture error
15
   NVIC_EnableIRQ(SysTick_IRQn);
16
   __enable_irq();
17
.....

Die Interruptroutine springt aber nicht sofort an, sondern erst viel 
später, nachdem mehrere, eigene Funktionen schon aufgerufen wurden. Ein 
Delay, das Timer0 benutzt und früh aufgerufen wird, wird daher nie 
beendet, da Timer0 noch nicht herunter gezählt wird.

Obigen Code hatte ich in einem Beispielcode gefunden, die  zwei Zeilen 
mit NVIC_EnableIRQ() und __enable_irq() habe ich hier mal in einem 
Posting gefunden. Scheint aber nicht das richtige zu sein.

Gruß
Andy

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.