www.mikrocontroller.net

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


Autor: Mark (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
#define MAX_COUNT 7936 /* =0x1F00 */
uint8_t global_counter;

uint16_t counts() {
    uint16_t retvalue;
    asm volatile( "in %A0, %1\n\t"
                   "lds %B0, %2\n\t"
                   : "=r" (retvalue)
                   : "M" (_SFR_IO_ADDR (TCNT0)), "m" (global_counter)
                   );
    return retvalue;
}

ISR(TIMER0_OVF_vect) {
    if(counts() >= MAX_COUNT)
        TCCR0 &= ~(_BV(CS02) | _BV(CS00));
    else
        ++global_counter;
}

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:
0000014a <__vector_9>:

ISR(TIMER0_OVF_vect) {
 14a:   1f 92           push    r1
 14c:   0f 92           push    r0
 14e:   0f b6           in      r0, 0x3f        ; 63
 150:   0f 92           push    r0
 152:   11 24           eor     r1, r1
 154:   8f 93           push    r24
 156:   9f 93           push    r25

uint16_t counts() {
        uint16_t retvalue;

        asm volatile (  "in %A0, %1\n\t"
 158:   82 b7           in      r24, 0x32       ; 50
 15a:   90 91 61 00     lds     r25, 0x0061

        if(counts() >= MAX_COUNT) {
                TCCR0 &= ~(_BV(CS02) | _BV(CS00));
 15e:   83 b7           in      r24, 0x33       ; 51
 160:   8a 7f           andi    r24, 0xFA       ; 250
 162:   83 bf           out     0x33, r24       ; 51

        } else {
                ++t0ov_count;
        }

}
 164:   9f 91           pop     r25
 166:   8f 91           pop     r24
 168:   0f 90           pop     r0
 16a:   0f be           out     0x3f, r0        ; 63
 16c:   0f 90           pop     r0
 16e:   1f 90           pop     r1
 170:   18 95           reti

Ich hoffe jemand weiss, woran es hakt...

sollte noch etwas unklar sein, fragt einfach.

PS: MCU ist ATmega8

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Mark (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Mark (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Klassiker: global_counter nicht volatile.

Autor: Mark (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.)

Autor: Peter Dannegger (peda)
Datum:

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

Bei mir tut er das (AVR_GCC 4.3.3):
ISR(TIMER0_OVF_vect) {
  4a:   1f 92           push    r1
  4c:   0f 92           push    r0
  4e:   0f b6           in      r0, 0x3f        ; 63
  50:   0f 92           push    r0
  52:   11 24           eor     r1, r1
  54:   2f 93           push    r18
  56:   8f 93           push    r24
  58:   9f 93           push    r25
#define MAX_COUNT 7936 /* =0x1F00 */
uint8_t global_counter;

uint16_t counts() {
    uint16_t retvalue;
    asm volatile( "in %A0, %1\n\t"
  5a:   82 b7           in      r24, 0x32       ; 50
  5c:   90 91 60 00     lds     r25, 0x0060
                   );
    return retvalue;
}

ISR(TIMER0_OVF_vect) {
    if(counts() >= MAX_COUNT)
  60:   80 50           subi    r24, 0x00       ; 0
  62:   9f 41           sbci    r25, 0x1F       ; 31
  64:   68 f4           brcc    .+26            ; 0x80 <__vector_9+0x36>
        TCCR0 &= ~(_BV(CS02) | _BV(CS00));
    else
        ++global_counter;
  66:   80 91 60 00     lds     r24, 0x0060
  6a:   8f 5f           subi    r24, 0xFF       ; 255
  6c:   80 93 60 00     sts     0x0060, r24
}
  70:   9f 91           pop     r25
  72:   8f 91           pop     r24
  74:   2f 91           pop     r18
  76:   0f 90           pop     r0
  78:   0f be           out     0x3f, r0        ; 63
  7a:   0f 90           pop     r0
  7c:   1f 90           pop     r1
  7e:   18 95           reti
    return retvalue;
}

ISR(TIMER0_OVF_vect) {
    if(counts() >= MAX_COUNT)
        TCCR0 &= ~(_BV(CS02) | _BV(CS00));
  80:   83 b7           in      r24, 0x33       ; 51
  82:   8a 7f           andi    r24, 0xFA       ; 250
  84:   83 bf           out     0x33, r24       ; 51
    else
        ++global_counter;
}
  86:   9f 91           pop     r25
  88:   8f 91           pop     r24
  8a:   2f 91           pop     r18
  8c:   0f 90           pop     r0
  8e:   0f be           out     0x3f, r0        ; 63
  90:   0f 90           pop     r0
  92:   1f 90           pop     r1
  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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frank M. (ukw) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.