Forum: Compiler & IDEs WINAVR produziert falschen Code


von Harald H. (hhoopi)


Lesenswert?

Hallo Leute,
Ich bin am verzweifeln, da mein WINAVR 20090313 mit Studio 4.16 SP1 
völlig falschen Code für einen AT90CAN32 produziert. Selbst eine 
einfache For-Schleife, die nur wartet, wird falsch codiert mit 
Optimierungsstufe Os und dann nur einmal durchlaufen. Stelle ich keine 
Optimierung ein, läuft sie richtig. Das komplette Programm aber geht mal 
so oder so, aber nie völlig richtig. Jedenfalls ändert sich das 
Verhalten je nach Optimierung. Auch Zeilen, die in C nacheinander 
abgearbeitet werden, sich aber nicht beeinflussen, ändern das verhalten 
des Programmes, wenn man sie vertauscht?!  Man kann das Fehlverhalten am 
Assembler nachvollziehen. Der Code wird falsch übersetzt.
Hat einer von Euch sowas schon erlebt? Ich habe auch einen anderen 
Rechner versucht, andere Einstellungen, etc. Es hat keinen Zweck, die 
ausgespuckten Programme funktionieren nicht, oder nur mangelhaft.
Ist WINAVR nichts für den AT90CAN?
hhoopi

: Verschoben durch User
von André (Gast)


Lesenswert?

Es ist wahrscheinlicher, dass das Problem nicht in Form von WinAVR auf 
der Festplatte hockt, sondern vorm PC sitzt :).

Irgendwas ist falsch. Makefile, Code?

von mr.chip (Gast)


Lesenswert?

> Es ist wahrscheinlicher, dass das Problem nicht in Form von WinAVR auf
> der Festplatte hockt, sondern vorm PC sitzt :).

!!!

Alle Jahre wieder meint man, einen Fehler im Compiler oder im AVR 
gefunden zu haben, und immer ist es dann doch ein Fehler im eigenen 
Programm.

Am besten kommentierst du mal alles aus, und baust das Programm dann 
Stück für Stück wieder zusammen, bis die Fehler auftreten. Dann kann es 
sich oft lohnen, nach Zufall ein paar Änderungen einzubauen und das 
Verhalten zu beobachten.

von Oliver (Gast)


Lesenswert?

>Selbst eine
>einfache For-Schleife, die nur wartet, wird falsch codiert mit
>Optimierungsstufe Os und dann nur einmal durchlaufen.

Eigentlich sollte der Compiler die komplett rauswerfen - das Konzept 
"warten" kennt der gar nicht, der sieht nur unnützen Code. Nicht umsonst 
gibt es in der avrlibc für diesen Zweck _delay-Funktionen. Warten ist 
nämlich gar nicht so einfach.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?


von Harald H. (hhoopi)


Angehängte Dateien:

Lesenswert?

Danke für die aufmunternden Worte,
Das mit dem Wegoptimieren von nichts tuenden Schleifen habe ich 
tatsächlich nicht gewusst. Wenn ein Compiler so eigenwillig ist, muss 
ich ihn wohl überlisten.
Ich bin jetzt den raikalen Weg gegangen und habe alle Funktionen auf die 
Portebene heruntergebrochen, dabei Strukturen und enums entfernt und das 
doch relativ kleine Programm in einer Datei zusammengefasst, um einen 
Überblick zu bekommen.
Jetzt fuktioniert die Sache etwas besser, allerdings zickt der 
Can-Controller noch etwas herum. Die Daten werden richtig angezeigt, 
doch zwischendurch bleiben sie einfach weg, kommen dann aber wieder, als 
wenn der Controller sich "verschluckt".
Ich habe zur Info den Code angehängt.
hhoopi.

von Peter (Gast)


Lesenswert?

ich glaube den kannst da noch ne menge optimieren


ich glaube es sind viel zu viele variablen die volatile sind.

Und was soll das hier

volatile unsigned int  weekunit_low;                // Lowbyte
volatile unsigned int  weekunit_high;              // Highbyte


ein lowbyte ist nun mal kein int

von Peter (Gast)


Lesenswert?

if (waittimer > 0) waittimer--;

Da wird der optimierer wohl einfach

waittimer = 0; draus machen.

Wie lange es dauert kannst du eh nicht wissen.

von Harald H. (hhoopi)


Lesenswert?

Teilweise alter Code, den ich noch rausschmeissen muss. Ich bin dabei, 
gründlich aufzuräumen und zu dokumentieren. Aber dass ist halt nur 
Arbeit, die Spass macht, wenn es grundsätzlich läuft.
Der Can-Controller hat übrigens rumgezickt, weil meine Uhr (Timer-Int.) 
reingeschlagen hat. Uhr weg - Controller ruhig. Jetzt läuft es gut.
Danke für die Tipps.
Ich stelle den Code nochmal ein, wenn er überarbeitet ist.

von Harald H. (hhoopi)


Angehängte Dateien:

Lesenswert?

So, der Code ist ziemlich überarbeitet worden. Eine seltsame Sache geht 
hier immer noch vor: Das Programm läuft nur mit Optimierungsstufe O3 und 
Os, auf den anderen Stufen bekomme ich keine richtige Can-Antwort. 
Außerdem ist (im laufenden Programm) bei Drehschalter-Stellung 0 
(Codierschalter PORTA - erstes Datenbyte=0x40) die Anwort sofort da, bei 
Stellung 8 (erstes Datenbyte=0x48) springt die Antwort von Verögerung 0 
bis 3ms hin und her. Der einzige Unterschied in den Daten ist wie gesagt 
das erste Datenbyte und der errechnete Identifier von 0x201 auf 0x241. 
Ich muss mir wohl die Mühe machen, den Assemblercode unter die Lupe zu 
nehmen.
hhoopi.

von Peter (Gast)


Lesenswert?

warum hast du immer noch so viele variabeln als volatile gekennzeichnet?

z.b. volatile unsigned int idf1

(sollte aber nicht zu dem problem führen)

von Harald H. (hhoopi)


Lesenswert?

Habe ich mir so angewöhnt, weil ich eigentlich viel mit Interrupts 
arbeite, die wiederum mit den globalen Variablen rumspielen. Wenn 
volatile, dann wird die Variable nach Änderung sofort wieder auf den 
Speicherplatz geschrieben und es passiert nicht, dass ein zuschlagender 
Int. diese unbemerkt ändert.
hhoopi

von Peter (Gast)


Lesenswert?

du hast hier aber nicht ein aktuellen High-end Prozessor vor die wo man 
genügend Rechenleistung hat sondern ein µC, da sollte man sich schon 
angewöhnen sparsmam mit Resourcen umzugehen - und das volatile kostet 
sehr viel Zeit.

Vergleiche mal dem ASM-Code von mit volatile und ohne. Auch sollte 
variablen die nicht wirkich Global sein müssen auch nicht global sein - 
das liest sich dann für Fremde wesentlich besser.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Harald H. schrieb:

> Das mit dem Wegoptimieren von nichts tuenden Schleifen habe ich
> tatsächlich nicht gewusst. Wenn ein Compiler so eigenwillig ist, muss
> ich ihn wohl überlisten.

Das ist kein eigenwilliger Compiler, das ist schlicht und einfach C.

Un des bringt nix, nach dem Gieskannenprinzip Zeug volatile zu 
deklarieren... das hülft zB nicht gegen Raise-Conditions durch 
nicht-atomaren Zugriff.

Aber auch das liegt nicht am "eigenwilligen" Compiler, sondern dann an 
der eigenwilligen Interpretation der C-Spezifikation durch 
C-Programmierer ;-)

Johann

von Stefan E. (sternst)


Lesenswert?

Johann L. schrieb:

> das hülft zB nicht gegen Raise-Conditions durch nicht-atomaren Zugriff.

Nicht, dass ich kleinlich wäre (aber vielleicht ja doch ;-)),
aber falls er danach googlen will, sollte er dafür schon den richtigen 
Begriff haben:
race condition

von Harald H. (hhoopi)


Lesenswert?

Ich versuche noch zu optimieren und Eure Ratschläge anzunehmen, trotzdem 
ist das Verhalten, was ich weiter oben beschrieben habe, doch seltsam, 
oder? Der Can-Controller kann sich in anderen Optimierungsstufen doch 
nicht einfach anders verhalten.

von Peter (Gast)


Lesenswert?

diese sieht mir auch nicht sehr sinnvoll aus

idf1 = (((id_hb<<8) | 0x37)>>5);

warum machst du ein oder 0x37 und danach ein >> 5 - da bleibt von der 
0x37 recht wenig übrig (genau 1bit)

von Harald H. (hhoopi)


Lesenswert?

idf1 ist ein int, be dem in das High-Byte der id_hb geschrieben wird. In 
das untere Byte kommt fest 0x37. id_hb benötige ich noch in dem 
Datenstrom, also nicht kaputtmachen. Der Identifier wird jetzt aus der 
Mischung berechnet, indem man idf1 5mal rechts schiebt und damit die 11 
Bit für Standard-Can gewinnt.
Dieses Verfahren hängt mit der alten Schaltung zusammen, die ich jetzt 
modernisiere. Der alte Controller war ein Siemens SAE81C91.
Der feste Wert 0x37 und die Schalterstellung (id_hb) sollen nach Bedarf 
schnell geändert werden können, daher wird die alte Berechnung 
beibehalten. Im alten Controller konnte man High- und Lowbyte so in die 
Descriptor-Register einschreiben, beim AT90CAN muss ich halt anders 
verfahren.
hhoopi

von Oliver (Gast)


Lesenswert?

>Der Can-Controller kann sich in anderen Optimierungsstufen doch
>nicht einfach anders verhalten.

Das Zeitverhalten des Codes ändert sich in den verschiedenen 
Optimierungsstufen. Wenn du also irgendwo zeitkritische IO's hast, 
funktionieren die mal so und mal so.

Oliver

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Peter schrieb:
> if (waittimer > 0) waittimer--;
>
> Da wird der optimierer wohl einfach
>
> waittimer = 0; draus machen.
>
> Wie lange es dauert kannst du eh nicht wissen.

warum?
Ist ja ein if und kein while.

VG,
/th.

von Peter (Gast)


Lesenswert?

> warum?
> Ist ja ein if und kein while.
oh, stimmt nehme es zurück - keine ahnung wie ich auch eine while 
gekommen bein

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Wollt schon sagen **shocked**

So setze ich meist meine prüfungen auf im SRC :-)
if(geht noch) mach;
else  mach überlaufbehandlung;


funzt Erfahrungsgemäss besser als:
mach;
if(geht doch nicht) mach zurück oder überlauf;

**grins**


VG,
/th.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Harald H. schrieb:
> So, der Code ist ziemlich überarbeitet worden. Eine seltsame Sache geht
> hier immer noch vor: Das Programm läuft nur mit Optimierungsstufe O3 und
> Os, auf den anderen Stufen bekomme ich keine richtige Can-Antwort.

Du beachtest immer noch nicht, daß atomar auf Variablen zugegriffen 
werden muss, die sowohl ISRs als auch das Hauptprogramm verwenden.

Johann

von Falk B. (falk)


Lesenswert?

Siehe Interrupt

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> Siehe Interrupt

In dem Artikel steht das leider im Kleingedruckten und m.E. viel zu weit 
unten. Es wird leicht überlesen.

Vor den zentnerweise Code-Listings in dem Artikel wäre ein Hinweis auf 
diesen sehr wichtige Punkt, der ganz oft in Programmen nicht beachtet 
wird,  besser aufgehoben. Am besten am Artikelanfang, weil bis an der 
Stelle, wo's jetzt steht, geht bestimmt vielen die Lese-Puste aus...

Johann

von Falk B. (falk)


Lesenswert?

@Johann L. (gjlayde) Benutzerseite

>Stelle, wo's jetzt steht, geht bestimmt vielen die Lese-Puste aus...

Separates the boys from the men . . .

MfG
Falk

von Harald H. (hhoopi)


Lesenswert?

Hallo Leute,
Vielen Dank für den Hinweis, ich habe mir den Interrupt-Artikel mit 
atomarem Zugriff angeschaut und habe einiges verstanden, bin daher 
ziemlich geschockt, weil ich bis jetzt darüber nie richtig nachgedacht 
hatte. Das Problem ist da, aber wenn ich mir jedesmal den kompletten 
Assembler-Code anschauen muss, um sicher zu gehen, dass kein Code ein 
Opfer des Int. werden kann, sollte ich gleich in Assembler 
programmieren. Ich denke, es ist besser, das Problem im Interrupt zu 
behandeln, denn Interrupts abschalten beendet auch Ihren gewollten 
Zweck. Die Sicherung aller wichtigen Daten (Steuerregister, im Interrupt 
genutzte Variablen) am Anfang des Int. und Rücksicherung am Ende des 
Int. ist doch eine effektive Lösung des Problems?
hhoopi

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Harald H. schrieb:

> denn Interrupts abschalten beendet auch Ihren gewollten
> Zweck.

Nein, du schaltest sie ja nur für sehr kurze Zeit ab.  Die Interrupt-
Latenz vergrößert sich dadurch etwas, aber die ist ohnehin nicht so
klein, wie man sich das zuweilen wünschen würde.

von Peter (Gast)


Lesenswert?

> werden kann, sollte ich gleich in Assembler programmieren
Und wie willst du es in assembler besser machen als der compiler? Sobald 
du 16bit Zahlen hast, hast du das gleiche Problem das du dafür 2befehle 
braucht.

von Oliver (Gast)


Lesenswert?

>Das Problem ist da, aber wenn ich mir jedesmal den kompletten
>Assembler-Code anschauen muss, um sicher zu gehen, dass kein Code ein
>Opfer des Int. werden kann, sollte ich gleich in Assembler
>programmieren.

Brauchst du gar nicht. ALLE Zugriffe auf Variablen größer 8 Bit müssen 
atomar geschützt werden. ALLE globalen Variablen, die im Interrupt und 
im Hauptprogramm gelesen oder geschrieben werden, müssen volatile sein 
(zumindest im Moment des Zugriffs)

Einfach. Immer.

Oliver

von Stefan E. (sternst)


Lesenswert?

> Brauchst du gar nicht. ALLE Zugriffe auf Variablen größer 8 Bit müssen
> atomar geschützt werden.

Das bedeutet aber im Umkehrschluss nicht, dass man sich bei 8-Bit keine 
Gedanken machen müsste. Read-Modify-Write-Zugriffe müssen auch bei 8-Bit 
geschützt werden. Ein "var++;" beispielsweise muss auch dann geschützt 
werden, wenn var nur 8-Bit hat.

von Peter (Gast)


Lesenswert?

nicht immer, wenn du im Interrup auf eine 8bit variabel zugreifst ist 
sie immer vollständig da, etweder ist schon aufaddiert oder noch nicht. 
Probleme kommen erst wenn du im interupt und im hauptprogramm die 
variable änderst - das kommt aber recht selten vor.

von Stefan E. (sternst)


Lesenswert?

Ja ja, ich war mal wieder zu faul und nicht ausführlich genug.
Die eigentliche Kernaussage meines Posts steht aber:
> Das bedeutet aber im Umkehrschluss nicht, dass man sich bei 8-Bit keine
> Gedanken machen müsste.

Peter schrieb:
> Probleme kommen erst wenn du im interupt und im hauptprogramm die
> variable änderst - das kommt aber recht selten vor.

Das "recht selten" würde ich aber nicht unterschreiben.
Es kommt z.B. häufiger vor, dass Main und Interrupt unterschiedliche 
Pins am selben Port benutzen.

von Peter (Gast)


Lesenswert?

> Das "recht selten" würde ich aber nicht unterschreiben.
> Es kommt z.B. häufiger vor, dass Main und Interrupt unterschiedliche
> Pins am selben Port benutzen.
naja ein Port ist bei mir keine Variable.

Bei einem Port gibt es ja extra SetPortBit und ClearPortBit dafür muss 
man ja den Port nicht erst lesen und dann zurückschreiben. (auch wenn 
der C Code das vermuten lässt)

von Stefan E. (sternst)


Lesenswert?

Peter schrieb:
>> Das "recht selten" würde ich aber nicht unterschreiben.
>> Es kommt z.B. häufiger vor, dass Main und Interrupt unterschiedliche
>> Pins am selben Port benutzen.
> naja ein Port ist bei mir keine Variable.

Sorry, aber was es bei dir ist, spielt keine Rolle. Aus Sicht des 
Compilers ist ein SFR eine Variable.

> Bei einem Port gibt es ja extra SetPortBit und ClearPortBit dafür muss
> man ja den Port nicht erst lesen und dann zurückschreiben. (auch wenn
> der C Code das vermuten lässt)

Und wenn der Port nicht im von SBI/CBI erreichbaren Bereich liegt?
Oder wenn es nicht nur ein einzelnes Bit ist?

von Peter (Gast)


Lesenswert?

> Und wenn der Port nicht im von SBI/CBI erreichbaren Bereich liegt?
> Oder wenn es nicht nur ein einzelnes Bit ist?
ja das gleiche Problem habe ich auch mit C und µC. Ich fand den alten 
syntax besser wo man explizit SBI/CBI aufrufen konnte, da wusste man das 
es atomar ist. Wenn der port es nicht unterstützt gibt es einen Fehler. 
Jetzt schreibt man nur noch normalen C Syntax und weiss nicht was der 
compiler draus macht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter schrieb:

> Ich fand den alten
> syntax besser wo man explizit SBI/CBI aufrufen konnte, da wusste man das
> es atomar ist.

Das ist eben der Trugschluss (und einer der Gründe, warum ich dann auch
dafür war, dass diese Makros verschwinden): sbi und cbi haben nichts
anderes gemacht als das, was wir jetzt auch machen.  Die eigentliche
Umsetzung in SBI- und CBI-Befehle blieb dem Optimierer überlassen.

Das von dir beschriebene Verhalten gab es nur ca. 1 Jahr lang in den
Anfangszeiten von AVR-GCC/avr-libc, noch lange vor der Version 1.0
der avr-libc.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:

> Das ist eben der Trugschluss (und einer der Gründe, warum ich dann auch
> dafür war, dass diese Makros verschwinden): sbi und cbi haben nichts
> anderes gemacht als das, was wir jetzt auch machen.  Die eigentliche
> Umsetzung in SBI- und CBI-Befehle blieb dem Optimierer überlassen.

Da gibt es aber böse Fallen wie
   http://www.rn-wissen.de/index.php/Avr-gcc#Bit_7_bei_SFR-Zugriff

Das ist zwar in einer älteren avr-gcc-Version gesehen worden (und zudem 
kein Compilerfehler), ich würd aber kein Deut darauf wetten, daß sowas 
nicht auch in aktuellen GCC-Versionen passieren kann.

Allein die Erklärung zu dem Fehler und wann er u.U. auftreten kann 
zeigt, daß sowas schlicht und einefach nicht sicher an der Quelle 
festzumachen ist, d.h. anhand der C-Quelle ist nicht zu beurteilen wie 
der Code aussehen wird.

Die einzige Möglichkeit dazu ds auf Compiler-Ebene zu machen sind 
Builtins -- jedenfalls fällt mit nix anderes ein. Asm-Makros auf 
SBI/CBI/SBRS/SBRC haben den großen Nachteil, daß sie nicht innerhalb von 
SBRC/S, SBIC/S oder CPSE stehen können, weil der Compiler nix über deren 
Instruktionslängen wissen kann.

Johann

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:

> Da gibt es aber böse Fallen wie
>    http://www.rn-wissen.de/index.php/Avr-gcc#Bit_7_bei_SFR-Zugriff

Der sieht natürlich wirklich interessant aus.

Mit aktuellen Versionen des GCC gelingt es mir aber nicht, das
irgendwie zu reproduzieren.

Im Prinzip müsste man die Optimierung nach SBI/CBI vor der CSE
machen, damit sowas nicht passiert, aber das wird vermutlich von
GCCs Architektur nicht passen, oder?

von Michael G. (linuxgeek) Benutzerseite


Lesenswert?

Harald H. schrieb:
> Hallo Leute,
> Ich bin am verzweifeln, da mein WINAVR 20090313 mit Studio 4.16 SP1
> völlig falschen Code für einen AT90CAN32 produziert. Selbst eine
> einfache For-Schleife, die nur wartet, wird falsch codiert mit
> Optimierungsstufe Os und dann nur einmal durchlaufen.

Warum glaube ich das nicht... mal abgesehen dass Du nicht einmal 
versucht hast, einen Beweis dafuer zu liefern.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Da gibt es aber böse Fallen wie
>>    http://www.rn-wissen.de/index.php/Avr-gcc#Bit_7_bei_SFR-Zugriff
>
> Der sieht natürlich wirklich interessant aus.
>
> Mit aktuellen Versionen des GCC gelingt es mir aber nicht, das
> irgendwie zu reproduzieren.

Wie gesagt, wirde gesehen in einer älteren Version, aber daß sowas bei 
neueren Versionen nicht auch passier bei komplexen oder brain-dead 
Quellen (zB aus Codegenerator) kann nicht garantiert werden.

> Im Prinzip müsste man die Optimierung nach SBI/CBI vor der CSE
> machen, damit sowas nicht passiert, aber das wird vermutlich von
> GCCs Architektur nicht passen, oder?

CSE ist eine der ersten RTL-Optimizer. Die einzige Möglichkeit, sowas 
definitiv auszuschliessen, ist dedizierte RTL-Konstrukte dafür zu 
verwenden. So ziemlich das einzige, wo GCC keine algebraische 
Vorstellung von hat sind UNSPEC und UNSPEC_VOLATILE. Das würde solchen 
Optimierungen verhindern. Es müsste aber jemand geben, der solche 
Konstrukte erst erzeugt, und das kann nur in Expand oder per RTL-Builtin 
passieren.

In Expand geht es nicht, weil man nicht die Insns bekommt, wie sie in 
Final aussehen (sollen), also kann man an der Stelle keine andere 
Darstellung ausgeben, weil man den algebraischen Zusammenhang nicht hat. 
Builtins für UNSPEC müsste jeman schreiben und testen wie das auf die 
Performance wirkt. Combine tut sich naturgemäß schwer mit UNSPEC, aber 
wahrscheinlich braucht man Combine garnicht für guten Code wenn man 
schon adäquates RTL ausgibt.

Momentan verlässt sich avr-Backend ja auf CSE, Combine, Peep2 und 
RTX_COSTS. Per Design liefert das also keine Sicherheit vor dem 
geschilderten Problem.

Johann

von Harald H. (hhoopi)


Lesenswert?

Hallo Michael G.,
Es ist schon so, dass der Compiler in höheren Optimierungsstufen 
einfache "Warteschleifen" wegoptimiert, wenn die nichts anderes tun, als 
eine lokale Variable hoch zu zählen. Man muss nur einen globalen 
Arbeitsschritt in die Schleife einbauen, dann bleibt der Code erhalten. 
Habe ich übrigens hier im Forum gelernt und auf meiner Schaltung 
nachvollzogen. Danke nochmal für die Hinweise.

An Alle,
Scheint ja ein interessantes Problem zu sein, wenn ich mir die 
Reaktionen anschaue. Ich habe es jedenfalls bis jetzt nicht sonderlich 
zur Kenntnis genommen, bis ich es am eigenen Leib... äh Code zu spüren 
bekommen habe. Ich werde jedenfalls versuchen, im Interrupt all das zu 
retten, was gefährdet sein könnte. So etwas muss doch funktionieren, 
oder? Finde ich jedenfalls einfacher, als im Allgemein-Code alles 
abzusichern. Innerhalb des Int. hat man doch eine übersichtlichere 
Auswahl der Variablen/Register, die verändert werden könnten?
hhoopi

von Peter D. (peda)


Lesenswert?

Harald H. schrieb:
> Ich werde jedenfalls versuchen, im Interrupt all das zu
> retten, was gefährdet sein könnte.

Das ist Quatsch. Der Interrupt kann nicht unterbrochen werden, d.h. ihm 
kann keiner die Variablen unterm Hintern wegändern.
Dem Main dagegen schon.

> So etwas muss doch funktionieren,
> oder?

Natürlich nicht.


Aber es gibt ne Möglichkeit, das sauber zu machen: Man nimmt keine 
globalen Variablen.

Wenn das Main auf Interruptvariablen zugreifen muß, geschieht das nur 
über Zugriffsfunktionen. Die Variablen sind dann nicht mehr global, 
sondern statisch in dem Objekt, wo der Interrupthandler und die 
Zugriffsfunktionen sind.
Dann müssen Variablen auch nichtmal mehr volatile sein, da ja die 
Zugriffsfunktionen sie nur einmal einlesen bzw. schreiben.
Am besten auch dem Compiler sagen, daß er die Zugriffsfunktionen nicht 
inlinen darf:
_attribute_ ((noinline));
Falls man die Optimierung "-fwhole-program" verwenden will.


Ein Beispiel dafür ist meine Entprellroutine:
http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29


Peter

von Stefan E. (sternst)


Lesenswert?

Peter Dannegger schrieb:

> Wenn das Main auf Interruptvariablen zugreifen muß, geschieht das nur
> über Zugriffsfunktionen. Die Variablen sind dann nicht mehr global,
> sondern statisch in dem Objekt, wo der Interrupthandler und die
> Zugriffsfunktionen sind.

Das ändert aber nichts bezüglich der Atomar-Thematik.

Mir ist klar, dass du das weißt, aber dein Post könnte vom OP 
dahingehend missverstanden werden, dass er mit dieser Vorgehensweise 
alle hier besprochenen Probleme los ist.

von Rolf Magnus (Gast)


Lesenswert?

Mir ist nicht so recht klar, was das für einen Vorteil haben soll. Durch 
den zusätzlichen Funktionsaufruf wird etwas mehr Rechenzeit verbraucht, 
und wirklich gewonnen hat man gegenüber einer volatile-Variable doch 
eigentlich nichts.

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Mir ist nicht so recht klar, was das für einen Vorteil haben soll. Durch
> den zusätzlichen Funktionsaufruf wird etwas mehr Rechenzeit verbraucht,
> und wirklich gewonnen hat man gegenüber einer volatile-Variable doch
> eigentlich nichts.

Es geht nur um das Atomic. Die Zugriffsfunktion macht die nötige atomare 
Klammerung und dann muß sich das Main nicht mehr drum kümmern (der 
Softwareschreiber nicht mehr dran denken).
Das mit dem volatile ist nur ein Nebeneffekt.

Hier noch ein Beispiel einer Zugriffsfunktion, um immmer einen gültigen 
Zeitstempel zu erhalten:
Beitrag "AVR Timer mit 32 Bit"


Die Problematik des Atomic wird selbst von Profis oft nicht verstanden.
Z.B. hatte mein erstes Notebook (468SX/25MHz) eine Treiber, um nach 
einem Sleep den Timerinterrupt mit der CMOS-Uhr zu synchronisieren.
Hatte ich dann die Windows-Zeigeruhr laufen, fror der PC immer um 
0:00Uhr ein.

Ich hab dann mal die Timer-Funktion geloggt und folgende Zeiten 
erhalten:
23:59
23:60
24:60
24:00
00:00

Es haben also 2 Programmierer gepennt. Der von dem Treiber und der von 
der Windows-Uhr.


Die Atomic-Funktionen sind auch erst seit neuestem (2008) in dem WINAVR 
drin.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Mir ist nicht so recht klar, was das für einen Vorteil haben soll. Durch
> den zusätzlichen Funktionsaufruf wird etwas mehr Rechenzeit verbraucht,
> und wirklich gewonnen hat man gegenüber einer volatile-Variable doch
> eigentlich nichts.

Im Gegentum: man verliert mächtig, da der Compiler dann alle Register
in der ISR retten muss, die die Funktion zerstören dürfte (sofern
er sie nicht inlinen kann).

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Im Gegentum: man verliert mächtig, da der Compiler dann alle Register
> in der ISR retten muss, die die Funktion zerstören dürfte (sofern
> er sie nicht inlinen kann).

Ja, das ist ein Schwachpunkt des AVR-GCC, er analysiert nicht die 
Registerbelegung und verschenkt dadurch viel Optimierungspotential.

Ob man durch ne Zugriffsfunktion was verschenkt, hängt von der Anwendung 
ab. Wenn ich in nem Zählerinterrupt durch das nicht volatile spare und 
dieser aber 1000-mal häufiger aufgerufen wird als die Zugriffsfunktion, 
gewinnt die Zugriffsfunktion in jedem Fall CPU-Zeit.


Aber wie gesagt, daß ist nicht das hüpfende Komma. Volatile Fehler 
fallen sehr schnell auf, da dann garnichts passiert. Sie sind einfach 
nur lästig.

Das Gefährliche sind die Atomic-Fehler, da sie einen Interrupt zum 
ungünstigen Zeitpunkt benötigen, um aufzufallen.
Sie fallen daher typisch wärend der Entwicklung nicht auf, sondern erst 
später in der Praxis unter Vollast.


Peter

von Oliver (Gast)


Lesenswert?

Ganz so schlimm ist es dann doch nicht. Die Variablen sind static für 
das ISR-Modul, die ISR greift direkt zu. Das Hauptprogramm hat über 
Zugriffsfunktionen zugriff, die dann alles regeln.

Dieses Kapselung ist durchaus guter Programmierstil.

Oliver

von Rolf Magnus (Gast)


Lesenswert?

> Es geht nur um das Atomic. Die Zugriffsfunktion macht die nötige
> atomare Klammerung und dann muß sich das Main nicht mehr drum kümmern
> (der Softwareschreiber nicht mehr dran denken).

Ach so. Du kapselst einfach das cli/sei hinter der Funktion weg. Da muß 
man dann nur darauf achten, die Funktion nicht versehentlich in der ISR 
zu verwenden, sonst schaltet man dabei mittendrin die Interrupts ein.

von Peter D. (peda)


Lesenswert?

Rolf Magnus schrieb:
> Ach so. Du kapselst einfach das cli/sei hinter der Funktion weg. Da muß
> man dann nur darauf achten, die Funktion nicht versehentlich in der ISR
> zu verwenden, sonst schaltet man dabei mittendrin die Interrupts ein.

Dafür gibt auch ne Lösung:
1
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
2
{
3
4
}
Allerdings würde ich Interrupts, die die selbe Variable bearbeiten, auch 
ins selbe Objekt schreiben und dann brauchen sie die Zugriffsfunktion 
nicht.

Generell mag es der AVR-GCC ja nicht, wenn man Unterfunktionen in 
Interrupts aufruft (PUSH/POP-Orgie).


Peter

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.