Forum: Compiler & IDEs Macht GCC 4.2.1 fuer AVR hier einen Fehler?


von Juergen B. (jbeisert)


Lesenswert?

Ich habe mich schon dusselig gesucht, woran es liegen kann, daß bei 
diesem einfachen Ausdruck etwas schiefgeht:

---------
static const prog_uint32_t stimul_rates[] = {
  ((10000000000UL / F_CPU) + 5) / 10,
  (((10000000000UL / F_CPU) << 3) + 5) / 10,
  (((10000000000UL / F_CPU) << 5) + 5) / 10,
  (((10000000000UL / F_CPU) << 6) + 5) / 10,
  (((10000000000UL / F_CPU) << 7) + 5) / 10,
  (((10000000000UL / F_CPU) << 8) + 5) / 10,
  (((10000000000UL / F_CPU) << 10) + 5) / 10
};

int8_t bla(uint32_t *length)
{
uint32_t count;
int i;

[...]
/* Berechnung von i */
[...]
count = (*length + (pgm_read_dword(&stimul_rates[i]) >> 1)) /
      pgm_read_dword(&stimul_rates[i]);
[...]
}
----------------------

Letztenendes versuche ich hier, eine PWM zu programmieren und "count" 
wird dazu benutzt, den Zeitpunkt festzulegen, bei dem das Ausgangssignal 
den Pegel wechseln soll.
Aber: "count" wird immer zu 0, es kommt nie etwas anderes raus. Der 
Zugriff auf die Tabelle funktioniert, weil an anderen Stellen in der 
gleichen Funktion ebenfalls auf die gleiche Art zugegriffen wird und 
dabei korrekte Ergebnisse entstehen.

Ich habe die Funktion testweise auf meinem Host übersetzt, und es kommen 
auch die Ergebnisse dabei raus (abhängig von *length und i), die ich 
erwartet habe.

Eben hatte ich noch die Idee, statt dem GCC 4.2.1, mal den älteren GCC 
3.4.2 zu versuchen. Und siehe da, nun kommen auch brauchbare Werte bei 
der Berechnung raus!

Wie grenzt man so einen Fehler ein, um entweder eine Lösung zu finden 
oder zumindest einen Bug-Report zu erstellen? Oder wie sucht man nach so 
einem Fehler, ob das nicht schon längst gelöst ist? Der erzeugte 
Assembler-Code an der Stelle ruft __udivmodsi4 für die Division auf. 
Kommt das aus dem Compiler oder aus der avr-libc? Denn wenn ich den 
älteren GCC verwende, kommt auch eine ältere avr-libc zum Einsatz. Somit 
kann ich das derzeit nicht eindeutig zuordnen.

Für ein paar Tipps wäre ich dankbar.

Jürgen

von Karl H. (kbuchegg)


Lesenswert?

Diese Division hier

  (pgm_read_dword(&stimul_rates[i]) >> 1) /
        pgm_read_dword(&stimul_rates[i]);

wird immer 0 ergeben

es hängt als von *length ab, ob da jemals etwas größeres
las 0 entstehen wird. Solange *length nicht größer als
die Hälfte von pgm_read_dword(&stimul_rates[i]) ist, wird
das aber nichts

von Peter (Gast)


Lesenswert?

Ist 10'000'000'000  nicht etwas zu gross für "UL"? Wie wärs mit "ULL"

=> 10000000000ULL

static const prog_uint32_t stimul_rates[] =
{
  (( 10000000000ULL / F_CPU) + 5) / 10,
  (((10000000000ULL / F_CPU) << 3) + 5) / 10,
  (((10000000000ULL / F_CPU) << 5) + 5) / 10,
  (((10000000000ULL / F_CPU) << 6) + 5) / 10,
  (((10000000000ULL / F_CPU) << 7) + 5) / 10,
  (((10000000000ULL / F_CPU) << 8) + 5) / 10,
  (((10000000000ULL / F_CPU) << 10) + 5) / 10
};

von Juergen B. (jbeisert)


Lesenswert?

Karl heinz Buchegger wrote:
> Diese Division hier
>
>   (pgm_read_dword(&stimul_rates[i]) >> 1) /
>         pgm_read_dword(&stimul_rates[i]);
>
> wird immer 0 ergeben
>
> es hängt als von *length ab, ob da jemals etwas größeres
> las 0 entstehen wird. Solange *length nicht größer als
> die Hälfte von pgm_read_dword(&stimul_rates[i]) ist, wird
> das aber nichts

So soll es ja auch sein. Die Funktionen, die hier das "*lengt"
durchreichen, stellen sicher, daß diese Randbedingung eingehalten wird.

Allerdings sehen GCC 4.2.1 und GCC 3.4.2 den ganzen Ausdruck 
unterschiedlich. Zumindest sind sie unterschiedlicher Meinung was am 
Ende rauskommt. Bug oder Feature?

Gruß
Jürgen

von Juergen B. (jbeisert)


Lesenswert?

Peter wrote:
> Ist 10'000'000'000  nicht etwas zu gross für "UL"? Wie wärs mit "ULL"
>
> => 10000000000ULL
>
> static const prog_uint32_t stimul_rates[] =
> {
>   (( 10000000000ULL / F_CPU) + 5) / 10,
>   (((10000000000ULL / F_CPU) << 3) + 5) / 10,
>   (((10000000000ULL / F_CPU) << 5) + 5) / 10,
>   (((10000000000ULL / F_CPU) << 6) + 5) / 10,
>   (((10000000000ULL / F_CPU) << 7) + 5) / 10,
>   (((10000000000ULL / F_CPU) << 8) + 5) / 10,
>   (((10000000000ULL / F_CPU) << 10) + 5) / 10
> };

Hmm, gute Frage. Bei meinen Tests stehen in der Tabelle aber die 
richtigen Werte (hier für 16MHz Grundtakt):

Entry 0: 0000003F (63)
Entry 1: 000001F4 (500)
Entry 2: 000007D0 (2000)
Entry 3: 00000FA0 (4000)
Entry 4: 00001F40 (8000)
Entry 5: 00003E80 (16000)
Entry 6: 0000FA00 (64000)

Das Gleiche entsteht, wenn ich stattdessen "ULL" verwende.

Gruß
Jürgen

von Luther Blissett (Gast)


Lesenswert?

__udivmodsi4 kommt vom compiler, steht in libgcc.

Revisions-Log siehe hier:
http://gcc.gnu.org/viewcvs/trunk/gcc/config/avr/libgcc.S

von Juergen B. (jbeisert)


Lesenswert?

Juergen Beisert wrote:
>[...]
> Eben hatte ich noch die Idee, statt dem GCC 4.2.1, mal den älteren GCC
> 3.4.2 zu versuchen. Und siehe da, nun kommen auch brauchbare Werte bei
> der Berechnung raus!
>
> Wie grenzt man so einen Fehler ein, um entweder eine Lösung zu finden
> oder zumindest einen Bug-Report zu erstellen? Oder wie sucht man nach
> so einem Fehler, ob das nicht schon längst gelöst ist? Der erzeugte
> Assembler-Code an der Stelle ruft __udivmodsi4 für die Division auf.
> Kommt das aus dem Compiler oder aus der avr-libc? Denn wenn ich den
> älteren GCC verwende, kommt auch eine ältere avr-libc zum Einsatz.
> Somit kann ich das derzeit nicht eindeutig zuordnen.

Wenn ich die Funktion in ein eigenständiges Programm auslagere, liefert 
auch der GCC 4.2.1 ein richtiges Ergebnis auf dem AVR. Die Funktion 
fällt also nur im Verbund der gesamten Applikation und GCC 4.2.1 auf die 
Nase.
Was kann denn sowas sein? Problem mit dem Stack? Zu viel Optimierung von 
Seiten GCC?

Gruß
Jürgen

von Luther Blissett (Gast)


Lesenswert?

> Was kann denn sowas sein? Problem mit dem Stack? Zu viel Optimierung von
Seiten GCC?

gcc 4.2's Verhalten bei undefined behavior (UB) ist anders als bei der 
3er Serie. So werden signed int operation jetzt defaultmässig so 
optimiert als ob es keinen Überlauf gäbe. Falls dein code dann irgendwo 
davon ausgeht, daß 2er Komplement Überläufe stattfinden, dann läuft er 
idR. nicht mehr.

Schau mal ob -Wall etwas sagt. Probier mal mit -fwrapv zu kompilieren.

von Juergen B. (jbeisert)


Lesenswert?

Arrrrghhh, war gestern eine besondere Mondstellung? Heute kommt die 
gleiche Routine auf dem AVR auch mit dem GCC 4.2.1 zum richtigen 
Ergebnis. Aber Subversion sagt mir, ich hätte an den Rahmenbedingungen 
und den Quellen nichts verändert (nicht mal den Entwicklungshost neu 
gestartet!). Gna, gna, gna, ich geh' am Stock. Offenbar einen ganzen Tag 
Phantome gesucht.

Danke aber erst mal für die Tipps und Korrekturen. Die baue ich jetzt 
dauerhaft ein. Mal sehen ob dieser komische Fehler wieder auftritt.

Jürgen

von Peter (Gast)


Lesenswert?

Vergessenes make clean => rebuild all..?

Möglicherweise wurden Object-Dateien der verschidene GCC Versionen 
gelinkt.

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.