Forum: Compiler & IDEs volatile zerschiesst Speicher


von Tim (Gast)


Lesenswert?

Hallo!
Kann jemand folgendes Verhalten reproduzieren.

In meinem aktuellen Projekt zerstört das Hinzufügen von "volatile" bei 
häufigen Aufrufen den Variableninhalt.
Das Problem tritt mit folgenden Code auf:
Ich bekomme Fehlermeldungen nach 1 bis 5 Schleifendurchläufen.
1
#define TEST_VAL 12345
2
3
void verify(const unsigend int value)
4
{
5
  if(value==TEST_VAL)
6
   printf("O");  //OK - kein RW/Fehler aufgetreten
7
  else
8
   printf("F");  //fail - RW/Fehler aufgetreten
9
  return;
10
}
11
12
int main()
13
{
14
  volatile val=TEST_VAL;
15
  unsigned char i;
16
17
  for(i;i<100;i++)
18
  {
19
    verify(val);
20
  }
21
}

von Peter II (Gast)


Lesenswert?

Tim schrieb:
> Das Problem tritt mit folgenden Code auf:
> Ich bekomme Fehlermeldungen nach 1 bis 5 Schleifendurchläufen.

ein Programm macht immer das gleiche (hier auf jeden Fall bei Threads 
nicht immer). Damit muss immer das gleiche Ergebiss rauskommen. Es kann 
also gar nicht sein das es mal nach 1 oder mal nach 5 Schleifen zu einem 
Fehler kommt.

Bist du sicher das deine Hardware OK ist?

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Tim schrieb:
> void verify(const unsigend int value)

wohl eher void verify(const unsigned int value)

Tim schrieb:
> volatile val=TEST_VAL;

WAS ist val? int, double, .. Kartoffelbrei?

von Michael D. (etzen_michi)


Lesenswert?

War zu langsam ...

von Krapao (Gast)


Lesenswert?

> Kann jemand folgendes Verhalten reproduzieren.

Ich nicht. Der Code kompiliert nicht ohne Syntaxfehler.

von Martin M. (capiman)


Lesenswert?

Welcher Compiler ? Welche Version ?

Fehlt da nicht der Typ der Variable val in main ?

von Peter II (Gast)


Lesenswert?

M. K. schrieb:
> AS ist val? int, double, .. Kartoffelbrei?

nein das ist C und da ist es int.

von Krapao (Gast)


Lesenswert?

Nach Fehlerkorrektur ist die Ausgabe bei meiner "Toolchain" (tcc cersion 
0.9.25)
1
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

von Rolf Magnus (Gast)


Lesenswert?

Tim schrieb:
>   unsigned char i;
>
>   for(i;i<100;i++)

Du hast vergessen, i zu initialisieren.

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Peter II schrieb:
> M. K. schrieb:
>> AS ist val? int, double, .. Kartoffelbrei?
>
> nein das ist C und da ist es int.

Mag sein trotzdem sollte man den Typ schon dabei schreiben. Wenn val nun 
int ist und verify(..) ein unsigned int verlangt sollte der Compiler 
zumindest eine Warnung raus schmeißen.

Aber letztendlich ist das sowieso egal, weil das Konstrukt nicht den 
wahren Code zeigt, sondern nur das zeigt was der Programmierer meint was 
der wahre Code machen sollte.
Das Problem liegt wahrscheinlich mal wieder ganz wo anders.

von Tim (Gast)


Lesenswert?

Hängt euch bitte nicht an meiner Unfähigkeit auf Pseudocode zu tippen. 
Ich werde, wenn ich ihn etwas aufgeräumt habe mal den Originalcode 
posten.

zu den angesprochenen Punkten.
# ja es soll natürlich zweimal "unsigned int" heißen
# und das i soll natürlich mit 0 initialisiert sein

Mir ist aufgefallen, das diese Art von Fehler nur an bestimmten Stellen 
im Projekt auftritt. Rufe ich die Fkt verify am Anfang der main 
funktioniert diese ohne Fehler. Wird sie im späteren Programmverlauf in 
einer Unterfunktion gerufen gibt es Fehler.

von Peter II (Gast)


Lesenswert?

Tim schrieb:
> ir ist aufgefallen, das diese Art von Fehler nur an bestimmten Stellen
> im Projekt auftritt. Rufe ich die Fkt verify am Anfang der main
> funktioniert diese ohne Fehler. Wird sie im späteren Programmverlauf in
> einer Unterfunktion gerufen gibt es Fehler.

gut dann warten wir bis wir etwas mehr code sehen, aber es klingt doch 
sehr nach einem Speicher überschreiber.

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Tim schrieb:
> Hängt euch bitte nicht an meiner Unfähigkeit auf Pseudocode zu tippen.

Tim schrieb:
> int main()
> {
>   volatile val=TEST_VAL;

Steht die Deklaration von val tatsächlich in der main-Funktion deines 
Originalcodes oder ist sie außerhalb einer Funktion und somit Global?

Wird val irgendwo in deinem Originalcode manipuliert, z.B. in einem 
Interrupt oder in einer Funktion in der annimmst das val Global 
deklariert ist?

von Krapao (Gast)


Lesenswert?

Tim schrieb:
> Hängt euch bitte nicht an meiner Unfähigkeit auf Pseudocode zu tippen.

Herr Doktor hier hier und hier tut's furchtbar weh!
Tut mir leid Herr Tim, ich finde da keine Ursache. Sie sind 100% gesund.
Ach nee, Herr Doktor, das weiss ich. Weh tut's meiner Schwester!

von Rolf Magnus (Gast)


Lesenswert?

Tim schrieb:
> Hängt euch bitte nicht an meiner Unfähigkeit auf Pseudocode zu tippen.

Doch, denn du sollst hier keinen Pseudocode zeigen, sondern den, der 
nicht funktioniert. Es ist nämlich ziemlich frustrierend, wenn man 
Fehler im Code sucht und erklärt, nur um dann als Antwort zu bekommen, 
daß der eigentliche Code diese Fehler natürlich gar nicht enthält und 
das Problem dafür im geposteten Code gar nicht vorkommt.

> Mir ist aufgefallen, das diese Art von Fehler nur an bestimmten Stellen
> im Projekt auftritt. Rufe ich die Fkt verify am Anfang der main
> funktioniert diese ohne Fehler. Wird sie im späteren Programmverlauf in
> einer Unterfunktion gerufen gibt es Fehler.

Dann kann es sein, daß der Fehler eigentlich wo ganz anders liegt. Wenn 
ein anderer Programmteil z.B. über einen falschen Zeiger irgendwo was 
reinschreibt, kann der dir deine Variable auch überschrieben. Das von 
dir beobachtete Phänomen paßt exakt zu so einem Fehler.

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Krapao schrieb:
> Tim schrieb:
>> Hängt euch bitte nicht an meiner Unfähigkeit auf Pseudocode zu tippen.
>
> Herr Doktor hier hier und hier tut's furchtbar weh!
> Tut mir leid Herr Tim, ich finde da keine Ursache. Sie sind 100% gesund.
> Ach nee, Herr Doktor, das weiss ich. Weh tut's meiner Schwester!

Tim schrieb:
> Ich werde, wenn ich ihn etwas aufgeräumt habe mal den Originalcode
> posten.

Mit etwas Glück haben wir dann die verbesserte Schwester 2.0 und der 
Fehler ist weg oder wir bekommen es mit der Zwillingsschwester zu tun 
mit selben Symptom nur an anderer Stelle.

von Martin M. (capiman)


Lesenswert?

Tim schrieb:
> Hängt euch bitte nicht an meiner Unfähigkeit auf Pseudocode zu tippen.
> Ich werde, wenn ich ihn etwas aufgeräumt habe mal den Originalcode
> posten.

Hast Du schon versucht, ob der obige Code in deiner großen Umgebung
auch den Fehler erzeugt ?

Hast Du z.B. die Interrupt-Routinen (falls vorhanden, falls möglich)
ausgehängt und tritt der Fehler dann trotzdem auf ?

Eine Frage, die immer noch offen ist:
Welcher Compiler ? Welche Version ?

Was auch noch interessant ist:
Auf welchem Chip ?
Sind Optimierungen eingeschaltet ?
Falls ja: Tritt der Fehler auch auf, wenn die Optimierungen 
ausgeschaltet
sind ?

von Εrnst B. (ernst)


Lesenswert?

Glaskugel:
der TE hat nicht so ganz verstanden, was volatile eigentlich bewirkt, 
und einfach so ein "volatile uint8_t"-Beispiel aus dem Forum für seinen 
16Bit-integer umgemünzt.
Was er eigentlich will ist kein "volatile", sondern atomarer Zugriff. 
dafür gibts #include <util/atomic.h> mit passenden Makros.

von Tim (Gast)


Lesenswert?

Ich konnte den Fehler weiter eingrenzen.
Zum Hintergrund und dem echten Code. Ich benutze als Interruptquellen: 
Timer0 overflow und Timer1 compare match B. Compiler ist der ICCAVR 
V7.13
Ein Auskommentieren der OC1B ISR behebt den Fehler ebenso wie das 
löschen von volatile.

in der main wird zyklisch folgendes aufgrufen:
1
for(j=0;j<50;j++)
2
{
3
  xprintf("%u %u %u %u\r\n", j, test1, test2, test3);
4
  WDR();
5
}

test1, test2, test3 sind vom Typ (volatile) unsigned int und lokal 
deklariert

hier die Hilfsfunktionen dazu:
1
void xprintf(const char* string, ...)
2
{
3
  va_list args;
4
  char c_cmd;
5
  int i_cmd;
6
  unsigned int ui_cmd;
7
8
  va_start( args, string );
9
  while(*string)
10
  {
11
    if(*string == '%')
12
  {
13
     *string++;
14
     if(*string == 'c')
15
     {
16
     c_cmd = va_arg(args, char);
17
     PrintInt(c_cmd);
18
     } 
19
     else if(*string == 'i')
20
     {
21
       i_cmd = va_arg(args, int);
22
     PrintInt(i_cmd);
23
     }
24
     else if(*string == 'u')
25
     {
26
       ui_cmd = va_arg(args, unsigned int);
27
     PrintUInt(ui_cmd);
28
     }
29
     else
30
     {
31
       USART_Transmit_Char('%');
32
     USART_Transmit_Char(*string);
33
     }
34
     *string++;
35
  }
36
  else
37
  {
38
      USART_Transmit_Char(*string++);
39
  }
40
  }
41
  va_end(args);
42
}
43
44
void PrintUInt(unsigned int a)
45
{
46
  
47
  char buf[6];
48
  char i=0;
49
50
  do
51
  {
52
    buf[i]=a%10+'0';
53
    a=a/10;
54
    i++;
55
  }
56
  while (a);
57
  
58
  do
59
    USART_Transmit_Char(buf[--i]);
60
  while (i>0); 
61
}

und hier die besagte ISR, die wenn ich ihren Inhalt lösche den Fehler 
behebt:
1
#pragma interrupt_handler OC1B_isr:iv_TIMER1_COMPB
2
void OC1B_isr(void)
3
{
4
  static unsigned char cnt;
5
  static unsigned int current_offset;
6
  unsigned int fresh_data;
7
  unsigned long tmp;
8
9
  switch (cnt%8)
10
  {
11
  case 4:
12
    fresh_data = ADC;
13
    measured.current.acc += fresh_data;
14
15
    //Phasenverschiebung wegen ADC Multiplexing mit lin Interpolation ausgleichen
16
    //Spannungsmessung liegt zeitlich genau in der Mitte zw. 2 Strommessungen
17
    if(measured.current.previous <= fresh_data)//prüfen weil keine signed Variable
18
      measured.current.acc += (fresh_data - measured.current.previous)>>1 + measured.current.previous;
19
      else
20
      measured.current.acc += (measured.current.previous - fresh_data)>>1 + fresh_data;
21
22
    measured.current.previous = fresh_data;
23
    ADMUX = (1<<REFS0) | (0<<MUX0);        //Spannungsmesskanal auswählen
24
    ADCSRA |= (1<<ADSC);                //AD-Wandlung starten  
25
    break;
26
    case 0:
27
    fresh_data = ADC;
28
    measured.voltage.acc += fresh_data;     //Spannung messen
29
    measured.voltage.previous = fresh_data;
30
    ADMUX = (1<<REFS0) | (1<<MUX0);        //Strommesskanal auswählen
31
    ADCSRA |= (1<<ADSC);                //AD-Wandlung starten
32
    break;
33
  }
34
35
  if(cnt == 199)
36
  {
37
    if(measured.current.acc > CURRENT_NOISE)
38
  {
39
    if(control.out == 0) 
40
    {
41
      current_offset = measured.current.acc;
42
      }
43
44
      if(current_offset < measured.current.acc)
45
    {
46
        tmp = (unsigned long)(measured.current.acc-current_offset)<<4;
47
        tmp = tmp/SLOPE_CURRENT_REG_INV;
48
    measured.current.avg = (unsigned int)tmp + CONST_CURRENT_REG;
49
      }
50
    else
51
      measured.current.avg = 0;
52
    }
53
    else
54
    measured.current.avg = 0;
55
56
  if(measured.voltage.acc > VOLTAGE_NOISE)
57
  {
58
    tmp = (unsigned long)measured.voltage.acc<<4;
59
    tmp = tmp/SLOPE_VOLTAGE_REG_INV;
60
    measured.voltage.avg = (unsigned int)tmp + CONST_VOLTAGE_REG;
61
    }
62
    else
63
    measured.voltage.avg = 0;
64
65
  cnt = 0;
66
    measured.current.acc = 0;
67
  measured.voltage.acc = 0;
68
  }
69
  else
70
    cnt++;
71
}

und zu guter letzt die Ausgabe auf dem Terminal:
0 12345 5432 23456
1 12345 5432 23456
2 12345 5432 23456
3 12345 5432 23456
4 12345 5432 2720
5 12345 5432 2720
6 12345 5432 2720
7 12345 5432 2720
8 12345 5432 2720
9 12345 5432 2720
10 12345 2616 2729
11 12345 2616 2729
12 12345 2616 2729
13 12345 2616 2729
...
49 12345 2616 2729

von Peter II (Gast)


Lesenswert?

wo kommt measured her und was ist es ganau?

von Martin M. (capiman)


Lesenswert?

Tim schrieb:
> und hier die besagte ISR, die wenn ich ihren Inhalt lösche den Fehler
> behebt

Dann klammere den 2. Teil der ISR aus und schau ob der Fehler immer noch
auftritt. Falls ja noch weiter ausklammern...
Wenn der Fehler leicht reproduzierbar ist, solltest Du schnell
auf die Stelle kommen, die den Fehler verursacht.

Es scheint irgendwie ein 0x0A ("\n") in den Speicher geschrieben
zu werden, weiter hinten dann ein 0x09. Gib die Werte mal statt
mit "%u" mit "%x" aus, dann siehst du bei den Zählern eher,
wo was mit welchem Wert überschrieben wird.

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Tim blickst du durch deine Klammerung bei deinem IF-Konstrukt in der ISR 
überhaupt durch?
Ich jedenfalls nicht, deshalb habe ich den Teil mal etwas leserlicher 
gemacht:
1
  if(cnt == 199) {
2
    if(measured.current.acc > CURRENT_NOISE) {
3
      if(control.out == 0) {
4
        current_offset = measured.current.acc;
5
      }
6
      if(current_offset < measured.current.acc) {
7
        tmp = (unsigned long)(measured.current.acc-current_offset)<<4;
8
        tmp = tmp/SLOPE_CURRENT_REG_INV;
9
        measured.current.avg = (unsigned int)tmp + CONST_CURRENT_REG;
10
      } else
11
        measured.current.avg = 0;
12
    } else
13
      measured.current.avg = 0;
14
  
15
    if(measured.voltage.acc > VOLTAGE_NOISE) {
16
      tmp = (unsigned long)measured.voltage.acc<<4;
17
      tmp = tmp/SLOPE_VOLTAGE_REG_INV;
18
      measured.voltage.avg = (unsigned int)tmp + CONST_VOLTAGE_REG;
19
    } else
20
      measured.voltage.avg = 0;
21
22
    cnt = 0;
23
    measured.current.acc = 0;
24
    measured.voltage.acc = 0;
25
  } else
26
    cnt++;

Sieht vorerst sinnvoll aus, ABER der Code wird NIEMALS ausgeführt, weil
1
void OC1B_isr(void)
2
{
3
  static unsigned char cnt;           << neue Variable cnt deklariert
4
  static unsigned int current_offset;
5
  unsigned int fresh_data;
6
  unsigned long tmp;
7
8
  switch (cnt%8)                      << cnt nicht initialisiert, evtl. 0?

Streicht man alles raus was nicht ausgeführt wird, weil cnt immer 0 ist 
kommt folgendes dabei raus
1
#pragma interrupt_handler OC1B_isr:iv_TIMER1_COMPB
2
void OC1B_isr(void)
3
{
4
  static unsigned char cnt;
5
  static unsigned int current_offset;
6
  unsigned int fresh_data;
7
  unsigned long tmp;
8
9
  fresh_data = ADC;
10
  measured.voltage.acc += fresh_data;    //Spannung messen
11
  measured.voltage.previous = fresh_data;
12
  ADMUX = (1<<REFS0) | (1<<MUX0);        //Strommesskanal auswählen
13
  ADCSRA |= (1<<ADSC);                   //AD-Wandlung starten
14
15
  cnt++;
16
}

Vielleicht wird cnt aber auch anders initialisiert dann aber auch mehr 
oder weniger mit einem zufälligem Wert.
Die anderen Funktionen sehen soweit ganz ok aus.

Tim schrieb:
> test1, test2, test3 sind vom Typ (volatile) unsigned int und lokal
> deklariert

Warum test1, test2, test3 volatile wenn du sie nur lokal verwendest, was 
versprichst du dir davon?

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Ändere in deiner xprintf-Funktion mal die Ausdrücke *string++ in 
*(string++) vielleicht hilft das deinem Compiler etwas auf die Sprünge.

von Oliver (Gast)


Lesenswert?

M. K. schrieb:
> Ändere in deiner xprintf-Funktion mal die Ausdrücke *string++ in
> *(string++) vielleicht hilft das deinem Compiler etwas auf die Sprünge.

Na ja, da die Funktion augenscheinlich das tut, was sie soll, und die 
erste Schreibweise eindeutig im Sinnne des C-Standards ist, sollte das 
nicht das Problem sein.

In der ISR passiert eigentlich nichts, was andere Speicherbereiche in 
Mitleidenschaft ziehen könnte. Selbst das nicht initialisierte cnt stört 
da nicht. Insofern liegt das Problem ziemlich sicher ganz woanders.

Ich weiß jetzt nicht, was das IAR-System für debug- und 
Simulationsmöglichkeiten bietet. Ein Data-breakpoint auf den 
korrumpierten Datenspeicheradressen sollte über das Problem schnell 
Aufschluss geben.

Oliver

von Peter D. (peda)


Lesenswert?

Tim schrieb:
> In meinem aktuellen Projekt zerstört das Hinzufügen von "volatile" bei
> häufigen Aufrufen den Variableninhalt.

Nö.
Volatile Speicher wird an einer anderen Adresse angelegt, an der es 
zufälliger Weise auffällt, daß jemand Speicher überschreibt.
Volatile ist also nicht der Sündenbock, es macht die Sünde nur sichtbar.


Peter

von (prx) A. K. (prx)


Lesenswert?

M. K. schrieb:

> Streicht man alles raus was nicht ausgeführt wird, weil cnt immer 0 ist
> kommt folgendes dabei raus

Nur ist cnt nicht immer 0.

Hier mal das was "indent" draus macht (wozu hat man Maschinen ;-), 
passend eingedampft:
1
void
2
OC1B_isr (void)
3
{
4
    static unsigned char cnt;
5
6
    switch (cnt % 8)
7
      {
8
      case 4:
9
          ...
10
          break;
11
      case 0:
12
          ...
13
          break;
14
      }
15
16
    if (cnt == 199)
17
      {
18
          ...
19
          cnt = 0;
20
          ...
21
      }
22
    else
23
        cnt++;
24
}

Vielleicht hat er Tabs verwendet, das ist bei Inline-Code im Forum 
absolut tödlich.

Lokale als "static" deklarierte Variablen sind mit 0 initialisiert.

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

A. K. schrieb:
> M. K. schrieb:
>
>> Streicht man alles raus was nicht ausgeführt wird, weil cnt immer 0 ist
>> kommt folgendes dabei raus
>
> Nur ist cnt nicht immer 0.

A. K. schrieb:
> Lokale als "static" deklarierte Variablen sind mit 0 initialisiert.

Hier widersprichst du dir gerade selbst, schaust du dir die Deklaration 
von cnt an, ist diese in der ISR lokal und static.

Da 0 % 8 = 0 ist wird immer der Block für 0 ausgeführt, also kann man 
den kompletten Switch-Block entfernen und nur noch den Inhalt des case 0 
behlaten.

Die If-Abfrage wird auch nie true liefern weil cnt nie 199 wird, bis auf 
den letzten else-Zweig kann also alles weg.

A. K. schrieb:
> Vielleicht hat er Tabs verwendet, das ist bei Inline-Code im Forum
> absolut tödlich.
Das weiß ich auch, erklärt aber nicht das man sich Klammern einspart und 
so mehrdeutige if-else Verschachtelungen hervorbringt.

von Oliver (Gast)


Lesenswert?

M. K. schrieb:
> Die If-Abfrage wird auch nie true liefern weil cnt nie 199 wird, bis auf
> den letzten else-Zweig kann also alles weg.

Natürlich wird cnt irgendwann auch mal 199 (wenn die ISR nicht vor dem 
199zigsten Durchlauf disabled wird). Das ist eine zyklisch aufgerufene 
Timer-ISR, und bei jedem Aufruf wird cnt inkrementiert.

M. K. schrieb:
> und
> so mehrdeutige if-else Verschachtelungen hervorbringt.

Da ist nichts mehrdeutig.

Das alles hat aber gar nichts mit dem Problem zu tun. Das steckt ganz 
woanders.

Oliver

von (prx) A. K. (prx)


Lesenswert?

M. K. schrieb:

> Hier widersprichst du dir gerade selbst, schaust du dir die Deklaration
> von cnt an, ist diese in der ISR lokal und static.

Ja und? Sie ist mit Null initialisiert, einmalig beim Programmstart. 
Jede spätere Veränderung bleibt erhalten.

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

A. K. schrieb:
> Ja und? Sie ist mit Null initialisiert, einmalig beim Programmstart.
> Jede spätere Veränderung bleibt erhalten.

Ahh ok das ist schon eine ganz andere Aussage, dann revidiere ich meine 
Aussage!

Oliver schrieb:
> Das alles hat aber gar nichts mit dem Problem zu tun. Das steckt ganz
> woanders.

Sehe ich genau so.

von Tim (Gast)


Lesenswert?

Guten Abend an alle!
Meines Wissens nach werden lokale static Variable immer mit 0 
initialisiert. OC1B_isr() macht an sich das was es soll recht gut und 
zwar PWM synchron Messwerte aufnehmen.

Ich habe noch vergessen zu erwähnen, dass das Schreiben auf das Terminal 
interruptgesteuert abläuft. Meine Vermutung ist, dass sich die 
Interruptaufrufe gegenseitig negativ beeinflussen. Ich habe keine 
Möglichkeit in Hardware zu debuggen aber ich werde mal versuchen etwas 
mit breakpoints zu spielen und längere Passagen durch den Debugger 
laufen zu lassen. Als Programmierumgebung ist mir das AVR Studio auf dem 
ICCAVR vorgegeben. Das heißt ich kann den Debugger aus dem AVR Studio 
nutzen. Allerdings muss ich mich in Sachen Debugging erst ein wenig 
einarbeiten

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.