hallo leute
ich hatte im laufe der woche schon eine umfrage gestartet wegen eines
vermuteten compilerfehlers. hier nochmal die eckdaten:
avrgcc3.3, pn2, avrstudio4.11 und jtag-ice
compilereinstellungen: richtiger controller, optimierung s
programmiert wird in c.
folgendes detail:
123: dac_set_value(URDM_1,-1.5); // AP für MOSFET UGS=-3V
+00000FC2: E040 LDI R20,0x00 Load immediate
+00000FC3: E050 LDI R21,0x00 Load immediate
+00000FC4: EC60 LDI R22,0xC0 Load immediate
+00000FC5: EB7F LDI R23,0xBF Load immediate
+00000FC6: E081 LDI R24,0x01 Load immediate
123: dac_set_value(URDM_1,-1.5); // AP für MOSFET UGS=-3V
+00000FC7: 940E0000 CALL 0x00000000 Call subroutine
127: dac_set_value(US_1,1.5); // Startpunkte setzen
+00000FC9: E040 LDI R20,0x00 Load immediate
---- No Source
------------------------------------------------------------------------
------------
+00000FCA: E050 LDI R21,0x00 Load immediate
+00000FCB: EC60 LDI R22,0xC0 Load immediate
+00000FCC: E37F LDI R23,0x3F Load immediate
+00000FCD: E080 LDI R24,0x00 Load immediate
+00000FCE: CFF8 RJMP PC-0x0007 Relative jump
hinter diesem asm-code verbergen sich folgende zeilen im pn
dac_set_value(URDM_1,-1.5);
for(;;)
{
dac_set_value(US_1,1.5);
}
effekt1:
der pc wird vom compiler aus der schleife herausgeschickt
in die zeile vor der schleife!
+00000FCE: CFF8 RJMP PC-0x0007 Relative jump
wer hat infos woran das liegen kann?
m. gerlach
Gib mal bitte ein Stückchen minimalen C-Code, der das
reproduziert, statt eines Schnipsels aus dem Disassembler
von AVR Studio (der noch dazu bekanntermaßen mit negativen
Offsets Probleme hat).
Naja, ich wollte etwas compilierfähiges haben, damit ich mir
den Compiler-Output mal angucken kann.
Aber eigentlich hat sich das schon erledigt, mir ist auf dem
Heimweg beim Radeln eingefallen, dass der Compiler eben einfach
nur genial compiliert hat.
Der Code ist komplett korrekt. Der Compiler hat nur festgestellt,
dass er die beiden Funktionsaufrufe in einen vereinigen kann.
Das ist ein typisches Beispiel, an dem man sieht, dass es eben
einfach nicht immer eine 1:1-Zuordnung vom Objektcode zurück zum
C-Code geben kann: der Funktionsaufruf auf Adresse 0xFC7 gehört
sowohl zu Zeile 123 als auch Zeile 127 im Quelltext.
Wo ist nun dein Problem? Der RJMP spring nach 0FC7 und genau das soll er
doch auch. Nur ist der Compiler ein bischen schlauer als Du angenommen
hast, denn er verwendet den selben CALL-Befehl gleich für beide
Funktionsaufrufe. Nur eben mit verschiedenen Parametern. Genau wie er
soll.
Nein. Den Fehler gibt's nur in deinem Kopf. Du hast der
Schrittverfolgung im Debugger (die dich ja nur auf der
Ebene von C-Codezeilen arbeiten lässt) mehr vertraut als
der realen Funktion des generierten Codes.
Wieso? Es stimmt doch alles.
Ausserhalb und innerhalb der Schleife wird doch die gleiche Funktion
aufgerufen. Also passiert bei Ablauf des Codes folgendes:
1.) Werte für den ersten Funktionsausruf laden.
2.) Funktion aufrufen.
3.) Werte für den Funktionsaufruf in der Endlosschleife laden.
4.) Goto zu Schritt 2.
Alternativ könntest Du das natürlich auch so schreiben:
1.) Werte für den ersten Funktionsausruf laden.
2.) Funktion aufrufen.
3.) Werte für den Funktionsaufruf in der Endlosschleife laden.
4.) Funktion aufrufen.
5.) Goto zu Schritt 3.
Das nun hat aber einen unnötigen Schritt zuviel. Also kann man den auch
wegoptimieren...
Und noch lustiger wird es beim gcc-4.x mit Tail-Call-Optimierungen,
besonders in rekursiven Funktionen.
Da kann man dann in C fast schon so wie in LISP programmieren, und
trotzdem gibt's kein Stack-Overflow. ;-)
OK das leuchtet mir soweit sogar ein. Aber die funktionen werden auch
sozusagen vertauscht ausgeführt.
ich habe einen haltepunkt in die endlosschleife gesetzt. springt der
debugger da hin und lasse ich den nächsten schritt ausführen springt
der debugger aus der schleife führt dabei aber nicht die funktion in
der schleife aus. erst wenn der debugger wieder über die "aussere"
funktion gelaufen ist passiert hardwaremässig das was passieren soll.
und warum ist das kein fehler wenn ich will, dass die "aussere"
funktion nur einmal ausgeführt wird jedoch die "innere" im anschluss
daran ständig und er faktisch beide immerwieder ausführt? dann genau so
läuft das programm ab. beide funktionen werden immerwieder ausgeführt!
ok jetzt hab ichs tatsächlich verstanden. alles klar. kein fehler! sieht
eben nur so aus. ich finds nutürlich ein wenig bescheurt vom studio,
dass da nicht der sinn des asm-codes verstanden wird und dem benutzter
wenigstens vorgegaukelt wird, dass alles so läuft wie gewünscht.
aber noch was anderes
habt ihr irgendeine idee welche ursachen in frage kommen, wenn
code-teile mal richtig und mal falsch ausgeführt werden
bsp:
variante a: 999938/1000000=0.999938
variante b: 999938/1000000=0.499969
Der Debugger hat keine Chance. Wo technisch nur ein Funktionsaufruf ist,
hat er naturgemäss Probleme, diesen Aufruf mehreren Quellecodezeilen
zuzuordnen. Das ist der wohlbekannte Pferdefuss bei eingeschalteter
Code-Optimierung des Compilers.
Weshalb in solchem Kontext auch immer der Tip gegeben wird, bei Nutzung
des Debuggers die Code-Optimierung durch den Compiler abzuschalten. Was
bei Controllern freilich bisweilen Grenzen hat, wenn dabei das ROM
platzt.
Die Situation ist also nicht ideal, sie kann es nicht sein. Es kann
sogar das Paradox entstehen, dass ein mies optimierender Compiler im
praktischen Betrieb mit Debuggern angenehmer wirkt als ein gut
optimierender.
ich hab da noch ein Problem:
"aber noch was anderes
habt ihr irgendeine idee welche ursachen in frage kommen, wenn
code-teile mal richtig und mal falsch ausgeführt werden
bsp:
variante a: 999938/1000000=0.999938
variante b: 999938/1000000=0.499969"
Eine eigene Frage?
Dieser Thread ist über 21 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.