AVR-gcc. Optimierung ignoriert Vergleich mit volatile Variable

Gast #1681461
Lesenswert?

Habe ein Problem mit einer Routine, welche eine volatile Variable mit 
einer anderen vergleicht. Das ganze sollte eine FIFO für den USART1 
darstellen.
Hier das problematische Codesegment
(vollständiger Teil und generiertes Assemblerlisting im Anhang) :
1
#define BT_RXBUFSIZE  64
2
volatile unsigned char bt_rxbuffer[BT_RXBUFSIZE];  // Bluetooth-FIFO-Empfangspuffer
3
volatile unsigned char *bt_rx_wrptr;      // Zeiger auf nächste freie Schreibposition
4
unsigned char *bt_rx_rdptr;        // Zeiger auf nächste Leseposition. 
5

6
// Ein Zeichen aus dem RX-Puffer lesen.
7
// Return 0 : OK, 1 : Timeout (in 50usec Schritten)
8
uint8_t get_btrx(uint8_t *data, uint16_t timeout)
9
{
10
  do {
11
    if(bt_rx_rdptr!=bt_rx_wrptr) {  // Daten vorhanden
12
      *data=*bt_rx_rdptr++;
13
      if(bt_rx_rdptr==bt_rxbuffer+BT_RXBUFSIZE)
14
        bt_rx_rdptr=(unsigned char *)bt_rxbuffer;
15
      return 0;
16
    }      
17
    _delay_us(50);
18
  } while(--timeout);
19
  return 1;
20
}
21

22
/* Zeichen vom BT-Modul empfangen */
23
ISR(USART1_RX_vect)
24
{
25
  if((UCSR1A & ((1 << RXC1) | (1 << FE1) | (1 << DOR1) | (1 << UPE1))) != (1 << RXC1)) {
26
    UDR1;        // Lesen und wegwerfen ;-)
27
    *bt_rx_wrptr++=0;    // Bei Fehler 0-Zeichen
28
  } else
29
    *bt_rx_wrptr++=UDR1;
30
  if(bt_rx_wrptr==bt_rxbuffer+BT_RXBUFSIZE)
31
    bt_rx_wrptr=bt_rxbuffer;
32
}

Das Problem liegt in der Funktion get_btrx(). Wenn ich den mit -Os 
Optimierung erzeugten Assembler-Code richtig interpretiere, wird der 
if(bt_rx_rdptr!=bt_rx_wrptr) - Vergleich nur einmal aufgerufen und die
do-while-Schleife springt wieder zum Anfang des _delay_us(50) statt
zurück zum if-Vergleich. Bei der Optimierung -O0 ist alles OK,
bei -O2 funktioniert es auch nicht.
Die Deklaration des bt_rx_rdptr Zeigers als volatile bringt keine
Änderung (ist auch nicht sinnvoll, da der USART-Rx-Interrupt sie auch
nicht ändert).

Die USART-Konfiguration und die ISR für den USART1_RX_vect habe ich 
getestet. Das Zeichen wird korrekt empfangen und auch im Puffer 
gespeichert. Der erste Aufruf von get_btrx() ergibt ein Timeout (obwohl
vor Ablauf des Timeouts ein Zeichen über die USART empfangen wurde). 
Nachfolgende Aufrufe liefern das vorherige Zeichen aus dem Ringpuffer.

Ich finde keinen Fehler im C-Code und vermute ein Bug im gcc.
Kann ich als Workaround die Optimierung für diesen Bereich gezielt 
abschalten ?


AVR-GCC-Version : 4.4.3 , avr-libc 1.6.8
Zielprozessor : ATMega128A
Host: Ubuntu-Linux 8.04


Gruesse aus Mülheim ;-)

Burkhard
Angehängte Dateien:
Gast #1681585
Lesenswert?

Danke für die schnelle Antwort. Werde ich morgen direkt probieren und 
berichten :-)

Kleine Verständnisfrage an 'der mechatroniker' bzw. Oliver :

Was bedeutet denn

volatile unsigned char *ptr; ?

Ich dachte immer es bedeutet, daß sich die Adresse des Zeigers 
bt_rx_wrptr durch einen Interrupt ändern kann (unabhängig vom Zeichen 
auf welches er zeigt).

Und daß

unsigned char volatile *ptr;

bedeutet, daß sich das Zeichen, auf welches der Pointer zeigt, durch 
einen Interrupt ändern kann.

Ist das korrekt ? Oder ist es genau anders herum ?

Danke auch an Peter Dannegger. Im Prinzip richtig, daß ich den 16 Bit- 
Vergleich als 'atomic' machen könnte. Da ich aber nur auf Gleichheit 
teste und nicht mit dem Wert rechne, spielt das in diesem Fall (denke 
ich) keine Rolle.


Hallo Ernst !
Den Trick kenne ich auch noch nicht. Werde ich mal ausprobieren :-)

Gruss
Burkhard
#1681592
Lesenswert?

Burkhard schrieb:

> Ist das korrekt ? Oder ist es genau anders herum ?

Weder noch. Ob du
  volatile type * name;
oder
  type volatile * name;
schreibst ist egal. In beiden Fällen ist das worauf der Zeiger zeigt 
volatil. Wenn der Zeiger selbst volatil sein soll, dann heisst es
  type * volatile name;

Das sieht nicht wirklich elegant aus, aber als C nachträglich die 
const/volatile qualifier verpasst bekam war die Syntax nicht mehr zu 
retten.
#1681610
Lesenswert?

Burkhard schrieb:

> Ist das korrekt ? Oder ist es genau anders herum ?

const und volatile beziehen sich immer auf das was links von ihnen 
steht. Es sei denn, diese Qualifier sind bereits ganz links, dann 
beziehen sie sich ausnahmsweise auf den Teil des Datentyps rechts von 
ihnen (was aber aufs gleiche rauskommt)

Also:
1
  char volatile i;
  i                    da ist ein i
  volatile i           dieses i ist volatile
  char volatile i      dieses i ist ein volatile char

(Und das ist identisch zu   volatile char i;
 i ist ein char. Und dieser char ist volatile)

1
  char volatile * i;
 i                      es gibt ein i
 * i                    dieses i ist ein Pointer.
 volatile * i           dieser Pointer zeigt auf etwas was volatile ist
 char volatile * i      Nämlich einen char

also ist i ein Pointer der auf einen volatile char zeigt

1
  char * volatile i;
 i                      es gibt ein i
 volatile i             dieses i ist volatile
 * volatile i           dieses i ist ein volatiler Pointer
 char * volatile i      und dieser volatile Pointer zeigt auf einen char
                        (der selbst aber nicht volatile ist)

1
  char volatile * volatile i
 i                             es gibt ein i
 volatile i                    dieses i ist volatile
 * volatile i                  und dieses i ist ein volatile Pointer
 volatile * volatile i         und dieser volatile Pointer zeigt auf 
etwas,
                               was selbst auch volatile ist
 char volatile * volatile i    nämlich einen char

i ist also ein volatiler Pointer, der auf einen volatilen char zeigt.

immer beim Variablennamen anfangen und sich nach aussen durcharbeiten!
Gast #1681613
Lesenswert?

Entschuldigung. Flüchtigkeitsfehler: 'unsigned char volatile *ptr;' 
sollte auch 'unsigned char * volatile ptr;' heissen.

Wenn ich Dich also richtig verstehe bezieht sich alles NACH dem '*' auf 
den Pointer selbst und das VOR dem '*' auf das Element, auf welches er 
zeigt ?!

Gab es zu meiner Kernighan-Ritchie-Zeit irgendwie noch nicht da ;-).

Aber nochmals besten Dank !


Burkhard
#1681628
Lesenswert?

Burkhard schrieb:
> Da ich aber nur auf Gleichheit
> teste und nicht mit dem Wert rechne, spielt das in diesem Fall (denke
> ich) keine Rolle.

In diesem Fall könnte man es weglassen.
Es müßte grad das 256 Byte eintrudeln, damit eine falsche Gleichheit 
entsteht. Und dann verlierst Du auch nur 50µs bis zum nächsten richtigen 
Vergleich, ist also nicht tragisch.

Allerdings könnte es sich lohnen statt volatile atomic zu nehmen, 
immerhin macht der Interrupt 6 Zugriffe auf diese Variable.
Oder man nimmt eine Tempvariable im Interrupt.


Peter
Gast #1682278
Lesenswert?

Danke nochmal an alle C-Gurus

Beide Lösungsvorschläge funktionieren :

- Das Kapseln als 'Atomic' klappt :

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  if(bt_rx_rdptr!=bt_rx_wrptr) {  // Daten vorhanden
    *data=*bt_rx_rdptr++;
    if(bt_rx_rdptr==bt_rxbuffer+BT_RXBUFSIZE)
      bt_rx_rdptr=(unsigned char *)bt_rxbuffer;
    return 0;
  }
}

und bei korrekter Deklaration des Pointers ist auch alles OK :


volatile unsigned char * volatile bt_rx_wrptr;

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren