Hallo zusammen,
seit tagen pople ich an einem winzigen Stück Debugcode herum. Eigentlich
wollte ich nur mal schnell über den USART eines ATmega32 ein wenig
Debugtext ausgeben. Dabei bin ich anscheinend auf einen Bug im GCC
gestoßen.
In einer Schleife warte ich auf das Ende der Übertragung des letzten
Zeichens im String. Mein Sendepufferzeiger wird daraufhin auf NULL
gesetzt. In Optimierungsstufe -O0 klappt das auch einwandfrei. Ab -O1
geht es in die Hoste und Wait() wartet für immer.
Ich habe ein Testprogramm welches das gleiche Verhalten zeigt einmal
angehägt.
Hier nur die Highlights:
1 | volatile const char* g_pszCurrentString;
|
2 |
|
3 | ISR(USART_TXC_vect)
|
4 | {
|
5 | //-- Den Puffer auf das nächste Zeichen weiterrücken
|
6 | g_pszCurrentString++;
|
7 |
|
8 | //-- Wenn noch Daten im Puffer sind, das nächte Byte senden - sonst den Puffer auf NULL setzen damit Wait ausgelöst wird
|
9 | if (*g_pszCurrentString)
|
10 | UDR=*g_pszCurrentString;
|
11 | else
|
12 | g_pszCurrentString=NULL;
|
13 | }
|
14 |
|
15 | [...]
|
16 |
|
17 | void Wait()
|
18 | {
|
19 | while (g_pszCurrentString!=NULL);
|
20 | }
|
21 |
|
22 | [...]
|
Der Compiler macht aus der Wait-Funktion ab Optimierungsstufe -O1:
1 | while (g_pszCurrentString!=NULL);
|
2 | 108: 80 91 68 00 lds r24, 0x0068
|
3 | 10c: 90 91 69 00 lds r25, 0x0069
|
4 | 110: 89 2b or r24, r25
|
5 | 112: 09 f0 breq .+2 ; 0x116 <_Z4Waitv+0xe>
|
6 | 114: ff cf rjmp .-2 ; 0x114 <_Z4Waitv+0xc>
|
7 | 116: 08 95 ret
|
Was natürlich Quatsch mit Soße ist, da der rjmp um -2 ja in einer
Endlosschleife endet. D.h. der Compiler ignoriert nicht nur das volatile
völlig, sondern er optimiert so gut, dass er auch die Register nicht
noch einmal lesen möchte :)
Ich verwende aktuell winavr (20090313) mit avr-gcc 4.3.2.
Kann jemand das Verhalten bestätigen? Ist es wirklich ein Bug oder bin
ich nur zu dämlich?
Wenn jemandem ein einigermaßen eleganter Workaround einfällt, nur her
damit. Wenn nicht, vielleicht hilft's jemandem mit einem ähnlichen
Problem, nicht auch zwei Tage zu versenken ;)