Forum: Compiler & IDEs AVR-GCC: Compilerfehler?


von Werner Hoch (Gast)


Lesenswert?

Hallo erstmal,

ich hab jetzt mit dem AVR-GCC angefangen, aber wenn ich mir so das 
Ergebnis anschaue, dann kommen mir Zweifel.

Ich hab mir 2 Warteschleifen geschrieben, in der .lst-Datei sehen die 
dann so aus:

void
lcd_delay_5ms() {
  uint16_t i;
  /* t = i * loop / UC_CLOCK
     i = t / loop * UC_CLOCK
     the fastest way the compiler can do that loop takes 4 clocks:
     2 clocks for decrementing and 2 clocks to branch
     take care that i does not exceed max(uint16_t) */
  for (i=UC_CLOCK*0.005/4; i; i--)
 1be:  80 e0         ldi  r24, 0x00  ; 0
 1c0:  94 e2         ldi  r25, 0x24  ; 36
 1c2:  42 97         sbiw  r24, 0x12  ; 18
 1c4:  f1 f7         brne  .-4        ; 0x1c2
  ;
}
 1c6:  08 95         ret

UC_CLOCK ist 7372800.
r24 und r25 werden richtig geladen, aber in der nächsten Zeile wird 0x12 
subtrahiert.


inline void
lcd_delay_50us() {
  uint8_t i;
  /* t = i * loop / UC_CLOCK
     i = t / loop * UC_CLOCK
     the fastest way the compiler can do that loop takes 3 clocks:
     1 clocks for decrementing, 2 clocks to branch
     take care that i does not exceed max(uint8_t) */
  for (i=UC_CLOCK*50e-6/3; i; i--)
    ;
}

dieser Block wird so übersetzt:
 1fe:  8a e7         ldi  r24, 0x7A  ; 122
 200:  82 50         subi  r24, 0x02  ; 2
 202:  f1 f7         brne  .-4        ; 0x200

r24 wird wieder richtig geladen, aber dann mit 2 decrementiert.

Mach ich nur einen Denkfehler oder ist tatsächlich was faul?

mfg
werner

von Joerg Wunsch (Gast)


Lesenswert?

Warteschleifen werden gnadenlos wegoptimiert.  Entweder die
Zählvariable "volatile" deklarieren (siehe FAQ, 1. Punkt) oder
in (inline) assembler schreiben -- für letzteres gibt es in
#include <avr/delay.h> schon bißchen was Vorgefertigtes.

von Werner Hoch (Gast)


Lesenswert?

Danke.

Solche "Optimierungen" hätte ich nicht erwartet.

mfg
werner

von Werner Hoch (Gast)


Lesenswert?

Nachtrag: Autsch

Das Einfügen von volatile hatte unerwartete Auswirkungen, ich werd mir 
jetzt die vorgefertigten Routinen anschauen.

Der Assembler Code is dadurch "etwas" länger geworden:

lcd_delay_5ms() {
 1be:  cf 93         push  r28
 1c0:  df 93         push  r29
 1c2:  cd b7         in  r28, 0x3d  ; 61
 1c4:  de b7         in  r29, 0x3e  ; 62
 1c6:  22 97         sbiw  r28, 0x02  ; 2
 1c8:  0f b6         in  r0, 0x3f  ; 63
 1ca:  f8 94         cli
 1cc:  de bf         out  0x3e, r29  ; 62
 1ce:  0f be         out  0x3f, r0  ; 63
 1d0:  cd bf         out  0x3d, r28  ; 61
  volatile uint16_t i;
  /* t = i * loop / UC_CLOCK
     i = t / loop * UC_CLOCK
     the fastest way the compiler can do that loop takes 4 clocks:
     2 clocks for decrementing and 2 clocks to branch
     take care that i does not exceed max(uint16_t) */
  for (i=UC_CLOCK*0.005/4; i; i--)
 1d2:  80 e0         ldi  r24, 0x00  ; 0
 1d4:  94 e2         ldi  r25, 0x24  ; 36
 1d6:  89 83         std  Y+1, r24  ; 0x01
 1d8:  9a 83         std  Y+2, r25  ; 0x02
 1da:  89 81         ldd  r24, Y+1  ; 0x01
 1dc:  9a 81         ldd  r25, Y+2  ; 0x02
 1de:  89 2b         or  r24, r25
 1e0:  49 f0         breq  .+18       ; 0x1f4
 1e2:  89 81         ldd  r24, Y+1  ; 0x01
 1e4:  9a 81         ldd  r25, Y+2  ; 0x02
 1e6:  01 97         sbiw  r24, 0x01  ; 1
 1e8:  89 83         std  Y+1, r24  ; 0x01
 1ea:  9a 83         std  Y+2, r25  ; 0x02
 1ec:  89 81         ldd  r24, Y+1  ; 0x01
 1ee:  9a 81         ldd  r25, Y+2  ; 0x02
 1f0:  89 2b         or  r24, r25
 1f2:  b9 f7         brne  .-18       ; 0x1e2
  ;
}
 1f4:  22 96         adiw  r28, 0x02  ; 2
 1f6:  0f b6         in  r0, 0x3f  ; 63
 1f8:  f8 94         cli
 1fa:  de bf         out  0x3e, r29  ; 62
 1fc:  0f be         out  0x3f, r0  ; 63
 1fe:  cd bf         out  0x3d, r28  ; 61
 200:  df 91         pop  r29
 202:  cf 91         pop  r28
 204:  08 95         ret


mfg
werner

von Joerg Wunsch (Gast)


Lesenswert?

Ja, logisch, volatile verbietet die Optimierung auf der
genannten Variablen, d. h. es ist jedesmal ein Speicherzugriff
durchzuführen.  Ist vor allem gedacht, wenn man eine Variable
als shared memory zwischen einer Interruptroutine und dem
Hauptprogramm benutzen will, oder aber für memory mapped IO,
bei dem eine Adresse nicht auf eine Speicherstelle sondern ein
Peripherie-Steuerregister zeigt (gibt's ja auch beim AVR, und
bei den extended IO registers des ATmega128 muß man das auch
benutzen, die sind nicht mehr über IN oder OUT erreichbar).

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.