Forum: Compiler & IDEs avr-gcc -O3 optimiert Variablen und If-else statements weg


von Mark (Gast)


Lesenswert?

Hallo,

ich habe ein Problem, das mit den letzten Nerv kostet...

Ich verwende den avr-gcc mit dem -O3 Schalter, also hoechster 
Code-Optimierung. Das moechte ich prinzipiell auch dabei belassen, 
jedoch habe ich Stellen im Code, die vom Compiler wegoptimiert werden 
und somit das Programm falsch laeuft. Verschiedene Kombinationen mit 
volatile haben bisher noch nicht zum Erfolg gefuehrt. Durch die 
Verwendung des Inline Assembler tauchten einige Teile wieder auf...

Hier ein Beispiel:
1
#define MAX_COUNT 7936 /* =0x1F00 */
2
uint8_t global_counter;
3
4
uint16_t counts() {
5
    uint16_t retvalue;
6
    asm volatile( "in %A0, %1\n\t"
7
                   "lds %B0, %2\n\t"
8
                   : "=r" (retvalue)
9
                   : "M" (_SFR_IO_ADDR (TCNT0)), "m" (global_counter)
10
                   );
11
    return retvalue;
12
}
13
14
ISR(TIMER0_OVF_vect) {
15
    if(counts() >= MAX_COUNT)
16
        TCCR0 &= ~(_BV(CS02) | _BV(CS00));
17
    else
18
        ++global_counter;
19
}

Ich moechte den T0 interrupt eine bestimmte anzahl aufrufen und dann 
z.B. den Timer anhalten. global_count wird bei den normalen aufrufen der 
ISR incrementiert und ist quasi das obere byte vom Gesamt-Timerwert. Um 
auf die gesamtzahl an takten zu kommen kombiniere ich die einzelwerte 
aus TCNT0 und global_counter und gebe das ergebnis zurueck. Den inline 
assembler nehme ich nur desshalb, da es schneller geht, als das, was der 
avr-gcc selbst generieren wuerde (ich will die ISR kurz halten).

Wenn ich mir die lss-file anschaue, wird der else-Teil ueberhaupt nicht 
umgesetzt, sondern wegoptimiert. Das will ich aber nicht. Simultan dazu 
passiert dasselbe mit dem if-Teil, wenn ich zB. das >= durch ein < 
ersetze.

ich schliesse daraus, dass avr-gcc den obigen ausdruck bei >= immer fuer 
true haelt und den else-teil weglaesst.

dass die routine in der ISR eingebettet wird ist fuer mich ok und 
verstaendlich, da sie im selben modul nicht woanders aufgerufen wird.

hier noch der lss-ausschnitt:
1
0000014a <__vector_9>:
2
3
ISR(TIMER0_OVF_vect) {
4
 14a:   1f 92           push    r1
5
 14c:   0f 92           push    r0
6
 14e:   0f b6           in      r0, 0x3f        ; 63
7
 150:   0f 92           push    r0
8
 152:   11 24           eor     r1, r1
9
 154:   8f 93           push    r24
10
 156:   9f 93           push    r25
11
12
uint16_t counts() {
13
        uint16_t retvalue;
14
15
        asm volatile (  "in %A0, %1\n\t"
16
 158:   82 b7           in      r24, 0x32       ; 50
17
 15a:   90 91 61 00     lds     r25, 0x0061
18
19
        if(counts() >= MAX_COUNT) {
20
                TCCR0 &= ~(_BV(CS02) | _BV(CS00));
21
 15e:   83 b7           in      r24, 0x33       ; 51
22
 160:   8a 7f           andi    r24, 0xFA       ; 250
23
 162:   83 bf           out     0x33, r24       ; 51
24
25
        } else {
26
                ++t0ov_count;
27
        }
28
29
}
30
 164:   9f 91           pop     r25
31
 166:   8f 91           pop     r24
32
 168:   0f 90           pop     r0
33
 16a:   0f be           out     0x3f, r0        ; 63
34
 16c:   0f 90           pop     r0
35
 16e:   1f 90           pop     r1
36
 170:   18 95           reti

Ich hoffe jemand weiss, woran es hakt...

sollte noch etwas unklar sein, fragt einfach.

PS: MCU ist ATmega8

von Karl H. (kbuchegg)


Lesenswert?

Mark schrieb:

> sollte noch etwas unklar sein, fragt einfach.

Ja. Ich

Was soll der Unsinn.
Du bist im Overflow Interrupt. Den Wert vom TCNT0 kann ich dir auch so 
sagen. Der ist 0.

von Mark (Gast)


Lesenswert?

"Der ist 0."

Wird der Timer fuer die Dauer der ISR gestoppt? Dachte der laeuft 
weiter, sodass ich den exakten wert, wie oben beschrieben, extra 
bestimmen muss...

von Karl H. (kbuchegg)


Lesenswert?

Mark schrieb:
> "Der ist 0."
>
> Wird der Timer fuer die Dauer der ISR gestoppt? Dachte der laeuft
> weiter, sodass ich den exakten wert, wie oben beschrieben, extra
> bestimmen muss...

Natürlich läuft der weiter (welchen Vorteiler verwendest du?). Nur: Was 
hilft dir dann deine Abfrage?
Du hast sowieso keine Kontrolle darüber wieviele Taktzyklen zum Beispiel 
exakt vergehen, bis dein ISR Handler zum Zuge kommt. Du hast keine 
Kontrolle darüber wieviele Takte benötigt werden um den ISR-Einstieg 
(sichern auf den Stack) abzuwickeln.

Wenn du so auf jeden einzelnen Taktzyklus aus bist, dann ist die 
Methodik sowieso Quatsch. Schalte den Timer in den CTC Modus und lass 
die Hardware Takte zählen.

Mit deiner Systematik wirst du sowieso keine 7936 Timer-Takte abzählen 
können. Schon alleine deswegen, weil deine ISR nur alle 256 Timer-Takte 
aufgerufen wird.

Dann brauchst du auch keinen Inline Assembler (von dem ich sowieso nicht 
überzeugt bin, dass der hier irgendetwas bringt)

von Karl H. (kbuchegg)


Lesenswert?

Das ist zwar keine direkte Lösung für dein Assembler Problem. Ich bin 
allerdings davon überzeugt

* das dir hier der Assembler keinen Vorteil bringt
* du das Pferd grundsätzlich falsch aufzäumst (kein CTC Modus benutzt)


(Ich denke aber, dass du dich bei den Constraint Markierungen vertan 
hast. Der Compiler behandelt deine count() Funktion wie eine Funktion 
die ein bekanntes konstantes Ergebnis liefert. Eines, welches er 
offenbar größer als MAXCOUNT ansieht)

von Mark (Gast)


Lesenswert?

DANKE! Es geht jetzt; keine "Geistercodeteile" mehr. An CTC hatte ich 
noch gar nicht gedacht, aber so ist es auch in Ordnung.

Vielen Dank und gute Nacht,

Mark

von (prx) A. K. (prx)


Lesenswert?

Der Klassiker: global_counter nicht volatile.

von Mark (Gast)


Lesenswert?

"Der Klassiker: global_counter nicht volatile."

das hatte ich probiert und hat im erzeugten code nichts veraendert.
(hatte retvalue zwischenzeitlich auch schon volatile, war dann durch den 
Inline Assembler aber nicht mehr notwendig.)

von Peter D. (peda)


Lesenswert?

Unabhängig davon, ob ein Code sinnvoll ist, muß er trotzdem richtig 
compilieren.

Bei mir tut er das (AVR_GCC 4.3.3):
1
ISR(TIMER0_OVF_vect) {
2
  4a:   1f 92           push    r1
3
  4c:   0f 92           push    r0
4
  4e:   0f b6           in      r0, 0x3f        ; 63
5
  50:   0f 92           push    r0
6
  52:   11 24           eor     r1, r1
7
  54:   2f 93           push    r18
8
  56:   8f 93           push    r24
9
  58:   9f 93           push    r25
10
#define MAX_COUNT 7936 /* =0x1F00 */
11
uint8_t global_counter;
12
13
uint16_t counts() {
14
    uint16_t retvalue;
15
    asm volatile( "in %A0, %1\n\t"
16
  5a:   82 b7           in      r24, 0x32       ; 50
17
  5c:   90 91 60 00     lds     r25, 0x0060
18
                   );
19
    return retvalue;
20
}
21
22
ISR(TIMER0_OVF_vect) {
23
    if(counts() >= MAX_COUNT)
24
  60:   80 50           subi    r24, 0x00       ; 0
25
  62:   9f 41           sbci    r25, 0x1F       ; 31
26
  64:   68 f4           brcc    .+26            ; 0x80 <__vector_9+0x36>
27
        TCCR0 &= ~(_BV(CS02) | _BV(CS00));
28
    else
29
        ++global_counter;
30
  66:   80 91 60 00     lds     r24, 0x0060
31
  6a:   8f 5f           subi    r24, 0xFF       ; 255
32
  6c:   80 93 60 00     sts     0x0060, r24
33
}
34
  70:   9f 91           pop     r25
35
  72:   8f 91           pop     r24
36
  74:   2f 91           pop     r18
37
  76:   0f 90           pop     r0
38
  78:   0f be           out     0x3f, r0        ; 63
39
  7a:   0f 90           pop     r0
40
  7c:   1f 90           pop     r1
41
  7e:   18 95           reti
42
    return retvalue;
43
}
44
45
ISR(TIMER0_OVF_vect) {
46
    if(counts() >= MAX_COUNT)
47
        TCCR0 &= ~(_BV(CS02) | _BV(CS00));
48
  80:   83 b7           in      r24, 0x33       ; 51
49
  82:   8a 7f           andi    r24, 0xFA       ; 250
50
  84:   83 bf           out     0x33, r24       ; 51
51
    else
52
        ++global_counter;
53
}
54
  86:   9f 91           pop     r25
55
  88:   8f 91           pop     r24
56
  8a:   2f 91           pop     r18
57
  8c:   0f 90           pop     r0
58
  8e:   0f be           out     0x3f, r0        ; 63
59
  90:   0f 90           pop     r0
60
  92:   1f 90           pop     r1
61
  94:   18 95           reti

Ich würde auch dazu raten, immer Optimierung -Os zu nehmen.
Andere Optimierungen erzeugen deutlich größeren Code ohne merkbar 
schneller zu sein.
Hier sieht man z.B., daß der Epilog (16 Byte) verdoppelt wird, nur um 
ein RJMP (2 Zyklen) zu sparen.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Hier sieht man z.B., daß der Epilog (16 Byte) verdoppelt wird, nur um
> ein RJMP (2 Zyklen) zu sparen.

Aber schneller ist es. :-)  Ob der Aufwand in einem Verhältnis zum
Nutzen steht, muss natürlich jeder selbst für sich entscheiden.
Wenn am Ende des Projekts noch 43 % Flash ungenutzt sind, stört es
auch nicht, wenn man mit -O3 dann halt danach nur noch 23 %
ungenutzt hat, Geld gibt dir Atmel in beiden Fällen nicht zurück ;-),
und vielleicht spart die schnellere Implementierung ja noch etwas
Strom in einem batteriebetriebenen Gerät, weil sie sich schneller
wieder schlafen legen kann...

Ich stimme dir aber zu, dass der Code korrekt compiliert werden muss,
auch wenn er reichlich sinnfrei ist.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> Der Klassiker: global_counter nicht volatile.

Warum soll man eine globale Variable, auf die ausschließlich aus einer 
ISR (meinetwegen auch über eine aufgerufene Sub-Funktion) zugegriffen 
wird, volatile machen? Mache ich nie. Und warum auch? Da eine ISR (und 
alle darin aufgerufenen Unterfunktionen) niemals unterbrochen werden 
kann (jedenfalls bei ATTiny/ATMEGA), gibt es auch keine Notwendigkeit, 
so eine Variable volatile zu deklarieren. Kostet nur Code + Zeit ;-)

Gruß,

Frank

von (prx) A. K. (prx)


Lesenswert?

Frank M. schrieb:

> Warum soll man eine globale Variable, auf die ausschließlich aus einer
> ISR (meinetwegen auch über eine aufgerufene Sub-Funktion) zugegriffen
> wird, volatile machen?

Dann nicht. Aber das war ja auch nicht das ganze Programm, daher war das 
allenfalls zu vermuten, jedoch nicht sicher.

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.