Hallo zusammen,
ich könnte hier einmal die Hilfe der Compilerexperten gebrauchen. Ich
wollte heute meinen digitalen Müllkalender ein wenig erweitern. Um die
Farbentöne besser anpassen zu können habe ich eine hsv2rgb-Funktion
eingebaut (stammt hier irgendwo aus dem Forum), die ich so auch schon
erfolgreich an anderer Stelle verwendet habe. Leider kam es beim Testen
zu seltsamen Farbsprüngen. Nach mehreren Stunden erfolglosem testen viel
mir ein, das ich kürzlich die AVR-Toolchain aktualisiert hatte.
Also habe ich die AVR-Toolchain 3.4.2-1573 deinstalliert und wieder die
AVR-Toolchain 3.4.1-1195 installiert, anschließend das Projekt neu
kompiliert, und alles lief Fehlerfrei. Laut dem Artikel hier im Forum
unterscheiden sich lediglich die AVR-GCC Versionen der beiden Toolchains
(http://www.mikrocontroller.net/articles/Atmel_Studio)
3.4.2-1573 AVR-GCC 4.7.2
3.4.1-1195 AVR-GCC 4.6.2
Um das ganze nochmal zu überprüfen, habe ich ein kleines Testprogramm
aufgesetzt:
Dann habe ich es nochmal mit jeder Toolchain compiliert und auf einen
ATmega168 getestet und die Ausgaben mit putty in Log-Files schreiben
lassen.
Hier zeigt sich, das mit dem AVR-GCC 4.6.2 die Ausgaben völlig korrekt
sind und mit dem AVR-GCC 4.7.2 in einem Abschnitt falsche Werte erzeugt
werden.
Hab mir zwar mal die beiden erzeugten lss-files angeschaut, aber meine
Assemblerkentnisse sind gleich 0 und die Unterschiede viel zu zahlreich
:-) Prinzipiell gehe ich erst mal davon aus, das der Fehler vor der
Tastatur sitzt aber in diesem Fall bin ich mir nicht sicher. Vielleicht
kann sich das ja mal jemand anschauen, der da deutlich mehr Ahnung von
hat als ich. Ich werde mal das AVR-Studio Projekt anhängen und die
jeweils erzeugten lss-files sowie die log-files.
Abschließend noch Infos zum Testaufbau:
Hardware:
ATmega168 auf STK500, int. RC @ 8MHz
Software:
AVR-Studio 4.19 Build 730
einmal mit AVR-Toolchain 3.4.2-1573
und einmal mit AVR-Toolchain 3.4.1-1195
Hoffe ich habe im Eifer des Gefechts keine Infos vergessen.
Cecky
Ja, da gebe ich dir 100%ig recht. War ja klar, das ich mir so nen Patzer
einbrocke :-) Hatte kurz vorher i noch mit in die Ausgabe aufgenommen
und vergessen den buffer zu vergrößern. Aber auch wenn ich buffer[60]
verwende ändert das nichts am Ergebnis. 4.6.2 richtig, 4.7.2 falsch. Und
da in der Ausgabe keine Zeile mehr als 36 Zeichen lang ist, sollte der
buffer doch ausreichend groß sein, oder bin ich hier irgendwie
Betriebsblind?
Davon mal abgesehen, wenn ich das ganze an eine RGB-LED hänge, und mir
einfach mal die Farbe Grün Ein und Ausfaden lasse (wie in diesem
Beispiel), dann ist mit 4.6.2 alles wunderbar und mit 4.7.2 kommt es zum
Farbsprung, der dem Logfile eindeutig entspricht. Im ursprünglichen
Müllkalender gibt es keine Uart-Ausgabe, also kann da ja auch kein
buffer zu klein sein.
Cecky
Daniel C. schrieb:> void setColorHSV(uint8_t h, uint8_t s, uint8_t v)> {> uint16_t vs=v*s, h6=6*h, f;
Typecasted das überhaupt richtig? Ich bin mir bei sowas nie sicher. v
ist 8 Bit, s auch. v*s passt da nicht rein. Wird da vom Compiler vorher
erweitert? Oder erst bei der Zuweisung nach vs? Sind ja noch mehr so
Fälle drin.
Heini schrieb:> Daniel C. schrieb:>> void setColorHSV(uint8_t h, uint8_t s, uint8_t v)>> {>> uint16_t vs=v*s, h6=6*h, f;>> Typecasted das überhaupt richtig?
OK.
Faustregel:
gerechnet wird IMMER mindestens im Datentyp int. (*)
In dem Fall eines AVR also 16 Bit.
Aber .... (nächstes Posting)
(*) Es sei denn der Compiler kann beweisen, dass es keinen Unterschied
macht, wenn er auf 8 Bit abkürzt. Das dürft ihm allerdings bei einer
Multiplikation und Zuweisung an einen 16-Bit int schwerfallen.
Daniel C. schrieb:> Ja, da gebe ich dir 100%ig recht. War ja klar, das ich mir so nen Patzer> einbrocke :-) Hatte kurz vorher i noch mit in die Ausgabe aufgenommen> und vergessen den buffer zu vergrößern. Aber auch wenn ich buffer[60]> verwende ändert das nichts am Ergebnis. 4.6.2 richtig, 4.7.2 falsch. Und> da in der Ausgabe keine Zeile mehr als 36 Zeichen lang ist, sollte der> buffer doch ausreichend groß sein, oder bin ich hier irgendwie> Betriebsblind?
Nö. Passt schon.
Lass dir mal innerhalb der Funktion die Zwischenwerte ausgeben.
Fang damit an
uint16_t vs=v*s
Auch wenn v und s zuerst auf 16 Bit erweitert werden, bin ich mir jetzt
nicht sicher, ob sie auf int oder unsigned int erweitert werden. Bei int
hast du hier ein Problem, denn 128*255 wäre grade an der Grenze zum
Überlaufen. Und du siehst ja auch deinen Sprung bei einem Wert für i von
128
So, hab jetzt mal die Zwischenwerte mit eingebaut. Die Ergebnisse für
uint16_t vs=v*s
sind mit beiden Compilern identisch.
Allerdings sind hier differernzen festzustellen wenn die Zählvariable
größer als 128 wird:
u = (((uint32_t)v<<16) - (uint32_t)vs*f) >> 16;
Ich trenne die Berechnungen bei solchen Problemen immer weiter auf.
Hilfsvariablen, einzelne Rechenschritte, etc. So ist dann auch der
Assemblercode lesbar. Manchmal kann man durch Umstellen sogar noch ein
paar Bytes rausholen, obwohl der Compiler da schon ziemlich gut geworden
ist (versuche auch mal den 4.8).
Daniel C. schrieb:> Hier zeigt sich, das mit dem AVR-GCC 4.6.2 die Ausgaben völlig korrekt> sind und mit dem AVR-GCC 4.7.2 in einem Abschnitt falsche Werte erzeugt> werden.
Gibt's dazu einen Testfall? etwa
1
#include<stdlib.h>
2
3
intmain(void)
4
{
5
/* Code */
6
if(bedingung)
7
exit(0);
8
else
9
abort();
10
}
Wo der code wider erwarten auf abort() läuft.
Neben dem Testfall braucht man auch die Compilerschalter und -version.
Zwischenergebnisse sind nicht so hilfreich. Wenn der Code irgendwo
rechnet 1+1 = 3 dann ist das immer noch nicht für andere
nachvollziehbar. Wenn du hingegen einen Testfall hast wie
1
#include<stdlib.h>
2
3
inti=1;
4
5
intmain(void)
6
{
7
inta=i+1;
8
9
if(a==2)
10
exit(0);
11
else
12
abort();
13
}
und der Code läuft auf abort — etwa im Simulator — dann können andere
das nachvollziehen und das Problem dingfest machen.
So, hab die fragliche Zeile mal zerlegt. Fakt ist, wenn der
Schleifenzähler einen wert von 128 hat rechnen beide Compiler das
Zwischenergebnis u2 gleich aus. Wenn der Schleifenzähler einen Wert von
129 hat rechnen die Compiler unterschiedlich (Siehe anhang)!!
Witzigerweise, wenn ich die Berechnung mal aus der Funktion ausgliedere
und die Berechnung einfach per Hand mache wie im folgenden Code:
1
#include<avr/io.h>
2
#include<stdio.h>
3
#include"uart.h"
4
5
intmain(void)
6
{
7
charbuffer[90];
8
uart_init();
9
uint16_tvs,f;
10
uint32_tu2;
11
uint8_tv,s;
12
s=255;
13
f=254;
14
// 1:
15
vs=32640;
16
u2=(uint32_t)vs*f;
17
sprintf(buffer,"1: u2= %lu\r\n",u2);
18
uart_puts(buffer);
19
20
// 2:
21
vs=-32641;
22
u2=(uint32_t)vs*f;
23
sprintf(buffer,"2: u2= %lu\r\n",u2);
24
uart_puts(buffer);
25
26
// 3:
27
v=128;
28
vs=v*s;
29
u2=(uint32_t)vs*f;
30
sprintf(buffer,"3: u2= %lu\r\n",u2);
31
uart_puts(buffer);
32
33
// 4:
34
v=129;
35
vs=v*s;
36
u2=(uint32_t)vs*f;
37
sprintf(buffer,"4: u2= %lu\r\n",u2);
38
uart_puts(buffer);
39
40
while(1)
41
{
42
}
43
}
dann geben beide Compiler das gleiche Ergebnis aus ?!?:
1: u2= 8290560
2: u2= 8355330
3: u2= 8290560
4: u2= 8355330
Irgendwie will mir das nicht in den Schädel, oder ich sehe den Wald vor
lauter Bäumen nicht ?!?!?!?
@Johann
Leider funktioniert bei mir unter Win7 64bit der Simulator nicht. Da
kackt mir immer das AVR-Studio ab. Deswegen mache ich das ganze ja per
UART, damit ich überhaupt mal was debuggen kann. Leider kann ich das
ganze nicht weiter Eingrenzen wie in meinem letzten Post.
@Henni
Ich wollte halt die Werte zur Berechnung von u2 einfach mal aus der
Funktion rauslösen um die Suche etwas einzugrenzen. Die Werte die sonst
an die Funktion übergeben werden, bzw darin berechnet wurden, hab ich
soweit möglich per Hand vorgegeben.
Zumindestens hab ich es mittlerweile mal geschafft sowohl das Ergebnis
8355330 für u2 (beim 4.6.2) als auch das Ergebnis 4286676482 für u2
(beim 4.7.2) auf dem Papier nach zu vollziehen.
uint16_t vs = -32641 (0x807F)
uint16_t f = 254 (0x00FE)
(Die werte von vs und f wurden einem älteren log-file entnommen bei
Schleifenzähler == 129)
Vermutung 4.6.2
u2 = (uint32_t)vs*f
0x0000807f * 0x00FE = 0x007F7E02 (8355330) wie in 4.6.2.txt
Vermutung 4.7.2
u2 = (uint32_t)vs*f
0xFFFF807f * 0x00FE = 0xFF817E02 (4286676482) wie in 4.7.2.txt
Nur was davon ist wirklich richtig? Mir platzt gleich die Rübe :-)
@Johann
Dann will ich mir mal den robusten Simulator anschauen :-)
Walter S. schrieb:> schreib doch Mal statt:> uint16_t vs=v*s, h6=6*h, f;>> uint16_t vs=(uint16_t)v*(uint16_t)s, h6=6*h, f;
Bleibt leider beim selben Ergebnis. 4.7.2 rechnet anders als 4.6.2. Hab
inzwischen in einem Simulator mal die Variableninhalte prüfen können.
Diese sind identisch mit den sprintf-Ausgaben. Habe auch mal meinen
Laptop angeschmissen, da war noch ein 4.5.1 installiert. Der erzeugt
aber die selben Ausgaben wie der 4.6.2.
Ändere ich die Deklaration
uint16_t vs=v*s
in
volatile uint16_t vs=v*s
dann kommt auch mit dem 4.7.2 die gleiche Ausgabe wie mit den anderen
Versionen.
Ebenso ist die Ausgabe beim 4.7.2 identisch mit den anderen Versionen,
wenn ich f nicht berechnen lasse, sondern direkt den sonst berechneten
Wert(254) zuweise.
exit-atmega168.0 ist wie von avrtest erzeugt für einen ATmega168.
Beachte, daß einige Atmel-Patches zu einer fehlerhaften 32 = 16x16
Multiplikation führen, siehe z.B. http://gcc.gnu.org/PR52474 und die
dortige Diskussion.
Im 4.8 Dump sieht __umulhisi3 so aus:
1
0000012e <__umulhisi3>:
2
12e: a2 9f mul r26, r18
3
130: b0 01 movw r22, r0
4
132: b3 9f mul r27, r19
5
134: c0 01 movw r24, r0
6
136: a3 9f mul r26, r19
7
138: 70 0d add r23, r0
8
13a: 81 1d adc r24, r1
9
13c: 11 24 eor r1, r1
10
13e: 91 1d adc r25, r1
11
140: b2 9f mul r27, r18
12
142: 70 0d add r23, r0
13
144: 81 1d adc r24, r1
14
146: 11 24 eor r1, r1
15
148: 91 1d adc r25, r1
16
14a: 08 95 ret
Und in den älteren Versionen:
1
0000012e <__umulhisi3>:
2
12e: a2 9f mul r26, r18
3
130: b0 01 movw r22, r0
4
132: b3 9f mul r27, r19
5
134: c0 01 movw r24, r0
6
136: a3 9f mul r26, r19
7
138: 01 d0 rcall .+2 ; 0x13c <__umulhisi3+0xe>
8
13a: b2 9f mul r27, r18
9
13c: 70 0d add r23, r0
10
13e: 81 1d adc r24, r1
11
140: 11 24 eor r1, r1
12
142: 91 1d adc r25, r1
13
144: 08 95 ret
Beachte, daß __umulhisi3 von der Standard-ABI abweicht.
@johann
Mit deinem Code komm ich zum selben Ergebnis. Wenn ich bei mir f direkt
mit einem Wert vorbelege, dann funktioniert das auch unter beiden
Compilern.
@Walter S.
Mag sein, das das Käse ist. Wie oben schon erwähnt habe ich den Code
hier aus dem Forum, und der werkelt bei mir in 4 Projekten ohne Probleme
(zumindest machen die RGB-LED's das was sie sollen). Aber selbst wenn
das Käse ist sollte es doch gleich gut oder gleich schlecht
funktionieren.
Wie dem auch sei, ich werde mir mal die Diskussion durchlesen aber so
langsam verlässt mich auch die Motivation da noch mehr Zeit zu
investieren. Auf meinem System ist einfach Fakt, das es mit der
aktuellen AVR-Toolchain einfach nicht funktioniert, und mit den beiden
verhergehenden Versionen das gewünschte Ergebnis erreicht wird. Somit
warte ich einfach bis zum nächsten Toolchain release und probier es dann
noch mal :-)
Na dann viel Glück. Bugs lösen sich nicht in Wohlgefallen auf sondern
dadurch, daß sie samt Testfall reportiert und dann behoben werden.
Mementan ist kein Fehler bekannt, der annähern die genannten Artfakte
erzeugt. Siehe die aktuelle Fehlerliste in
http://www.mikrocontroller.net/articles/Avr-gcc_Bugs
Daniel C. schrieb:> Aber selbst wenn> das Käse ist sollte es doch gleich gut oder gleich schlecht> funktionieren.
das ist nicht richtig, ein fehlerhaftes Programm kann unter Umständen
auch funktionieren (oder auch nicht)
aber das mit dem Käse nehme ich zurück, - von einer unsigned Variable
ist schon ein erlaubter Ausdruck
Johann L. schrieb:> Bugs lösen sich nicht in Wohlgefallen auf
Natürlich lösen sie sich nicht von allein. Leider hab ich es nicht
geschafft einen besseren Testfall zu erzeugen wie mit der uart-ausgabe.
Und da meine Assembler und Compilerkentnisse annährend 0 sind, bin ich
vielleicht einfach der falsche für diese Aufgabe. Vielleicht stößt ja
jemand kompetenteres auf ein ähnliches Problem und kann das dann
entsprechend besser diagnostizieren.
Ich bin jedenfalls mit meinem Latein am Ende.
Johann L. schrieb:> übersetzt mit avr-gcc-4.7.2 funktioniert im avrtest Simulator> einwandfrei, siehe Anhang.
Nein, tut es nicht(vorrausgesetzt dein angehängtes Logfile wurde auch
von dir erzeugt). Wenn du dir das Log-file näher ansiehst wirst du
feststellen, das bei i=129 "rot" einen wert von 255 hat. Sollte aber 1
sein. Vergleiche das mal mit den beiden Logfiles von meinen ersten
posting. Ab i>128 beginnen die unterschiede.
Mit 4.7 und 4.8 läuft das auf abort und mit 4.6 auf exit. Grund ist,
daß __usmulhisi3 verwendet wird, d.h. bei der widening mul wird ein
Operand sign-extended. Die korrekte Multiplikationsroutine ist hingegen
__umulhisi3. Im 4.6 gibt es diese Routine noch nicht; statt dessen
werden die Werte auf 32 Bits expandiert und ein __mulsi3 ausgeführt.
Das s-dump von 4.7.2 ist:
Erst mal ein großes dankeschön, das du dich dem Problem weiterhin
annimmst. So weit hätte ich das auf die schnelle jedenfalls nicht
reduzieren können.
Also wenn ich dich jetzt richtig verstanden habe, dann verwendet der
4.7.2 (um beim s-dump zu bleiben) fälschlicherweise die Funktion
__usmulhisi3?
Bliebe noch die Frage, is it a bug or a feature?
Daniel C. schrieb:> Und da meine Assembler und Compilerkentnisse annährend 0 sind, bin ich..
aber du versuchst dich trotzdem am Programmeschreiben. Wäre es nicht
besser, zunächst die nötigen Kenntnisse zu erwerben?
Johann L. schrieb:> rot (uint8_t v)> {> uint16_t vs = v * 255;>> return (0x810000 - (uint32_t) vs*254) >> 16;> }
Jaja. Wenn man sowas mal heraus-seziert anschaut, dann kommt einem schon
die Galle hoch. Immer wieder die besonders sparsamen C-Programmierer,
die mit jeder Zeile sparen. C wurde offensichtlich von Schwaben
schottischer Abstammung im Exil entworfen. Immer feste die
Variablendeklaration mit der Wertzuweisung zusammenschmeißen und dann
auch noch ne 8 Bit Variable dabei mit 255 multiplizieren.
int rot (unsigned char v)
{ long vs;
vs = v;
vs = (vs<<8) - vs;
return (0x810000 - ((vs*254)) >> 16; // was das auch immer ergeben
soll..
}
Typecasts sind m.E. keine Typumwandlungen!
Abgesehen davon frag ich mich, was
(0x810000 - argument 254 255)/65536
ergeben soll.
129 - argument - (argument>>6) - (argument>>7) würde es doch auch tun -
oder?
W.S.
W.S. schrieb:> aber du versuchst dich trotzdem am Programmeschreiben. Wäre es nicht> besser, zunächst die nötigen Kenntnisse zu erwerben?
Hochsprachen wurden entwickelt, damit man sich eben genau damit nicht
herumplagen muss.
>...und dann> auch noch ne 8 Bit Variable dabei mit 255 multiplizieren.
Zugegeben unsauber, aber hier egal. Die Rechnung wird in int (hier 16
Bit) durchgeführt, das Ergebniss mit 16 Bit gespeichert.
W.S. schrieb:> aber du versuchst dich trotzdem am Programmeschreiben.
Yupp, und das funktioniert auch wunderbar.
Ich fahre sogar Auto ohne eine abgeschlossene Ausbildung als
KFZ-Mechaniker.
Du bist offensichtlich kein Freund von C -> Es sei dir gegönnt. Du bist
also der große Assembler-Guru -> Glückwunsch. Du weißt mehr als andere
-> 100 Gummipunkte. Du programmierst besser, schneller, schöner,
effektiver (weitere Adjektive sind frei zu ergänzen).. -> 1 mit
Sternchen.
Ich konnte jedenfalls der Community einen kleinen Anstoß geben, der im
Endeffekt zur Aufdeckung eines Bugs geführt hat, und damit bin ich
persönlich sehr zufrieden. Und mehr will und werde ich dazu auch nicht
sagen.
Cecky
Daniel C. schrieb:> Du bist offensichtlich kein Freund von C
Nein, er wollte einfach nur auch mal was anmerken. Natürlich, ohne
sich dabei die Mühe zu machen, vorher noch den Rest des Threads zu
lesen.
Hallo,
ich bin auch erst dabei C zu lernen und werde gerade ebenfalls mit einem
Problem konfrontiert das nach einem GCC 4.7.2 Bug aussieht.
Das angehängte C-Programm kompiliert unvollständig!
Wie man in dem Objektdump sehen kann werden die Subroutinen
"check_inputs" und "print_inputs" überhaupt nicht richtig kompiliert !?
Karsten M. schrieb:> Wie man in dem Objektdump sehen kann werden die Subroutinen> "check_inputs" und "print_inputs" überhaupt nicht richtig kompiliert !?
sie werden weg optimiert, der code ist doch noch vorhanden.
Wo ist denn dein Problem überhaupt?
Peter II schrieb:> Wo ist denn dein Problem überhaupt?
Sein Problem ist offenbar, meinen zu müssen, eine ABBRUCH-Bedingung als
2. Statement im for() schreiben zu müssen - genau das umgekehrte ist der
Fall.
Also:
Statt: i >= 2
Korrekt: i < 2
Und:
Statt: i == 0
Korrekt: i != 0
O.K. schon überzeugt - DAU Fehler! :-)
Kaum macht man es richtig und schon funktionierts!
Ist immer wieder erstaunlich ...
Trotzdem sieht man den Wald vor lauter Bäumen nicht.
Das schlimme ist das der Compiler sonst über jede Sinnlosigkeit meckert.
Aber bei einer sinnlosen Schleife nicht.
Dann kompiliert er frecher Weise aus sinnlosem Quellcode auch noch
sinnlosen Output. :-)
Danke für das öffnen der geschlossenen Augen.
Karsten M. schrieb:> Der Titel passte perfekt - also warum nicht?
Weil davon letztlich überhaupt nichts passte.
Insbesondere wenn der ursprüngliche Inhalt tatsächlich ein Bug ist (habs
nicht gelesen), dann ist das garantiert nicht genau der, den du bei dir
erwartest.
A. K. schrieb:> Karsten M. schrieb:>> Der Titel passte perfekt - also warum nicht?>> Weil davon letztlich überhaupt nichts passte.
Wie konnte ich das vorher wissen?
> Insbesondere wenn der ursprüngliche Inhalt tatsächlich ein Bug ist (habs> nicht gelesen), dann ist das garantiert nicht genau der, den du bei dir> erwartest.
Gutes Argument - aber ganz schön kleinlich ...
Versprochen - nächstes Mal mache ich einen neuen Thread auf.
Dann lösche doch einfach alle Beiträge vom heutigen Tag.
Diese sind sowieso nicht besonders lehrreich für andere Leser.
Ausser vielleicht das man zuerst nach dem einfachsten Fehler suchen
sollte ...