http://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff
Ich lese in diesem Artikel dass man bei Interupts sehr vosichtig sein
soll da atomare Datenzugriffe bei einem auftretenden Interupt nicht
das gewünschte Ergebnis bringen können. Anderseits ist es doch die
Aufgabe einer ISR möglichst am Prozesserstatus und an den Registern
nichts zu verändern. Kann also eigentlich nicht sein.
Wenn ich mir ein .lss File anschaue wird dort in der ISR auf
Maschinenebene doch einiges an Registern gepusht und gepoppt, und
zwar offensichtlich abhängig davon welche Optimierungsstufe
eingeschaltet ist und (bei Optimierung) bei welchen Registern es
sich überhaupt lohnt (wenn sie anderweitig zum Einsatz kommen).
Genaueres weiss ich leider nicht da ich in Assembler nichts verstehe.
Das würde bei kleinerem Code der wenig Register braucht auch auf eine
schnellere ISR hinauslaufen.
Einfach ausgedrückt: ein AVR, mit Interrupt betrieben, würde ja bei
beliebigen einfachen Code c = a + b potentiell Fehler liefern.
Anders herum müsste ich ja für jede C-Zeile die Interrupt-geschützt
ausgeführt werden soll mit einem cli() / sei() einhüllen. Ein Ding
der Unmöglichkeit. Kann also eigentlich nicht sein, oder doch?
Vielleicht kann sich ein Wissender (oder auch mehr) dazu äussern?
isidor schrieb:> Einfach ausgedrückt: ein AVR, mit Interrupt betrieben, würde ja bei> beliebigen einfachen Code c = a + b potentiell Fehler liefern.
nein, nur wenn a oder b > 8bit sind um in der ISR geändert werden.
An alle:
Der Autor signalisiert ja ein seiner Code-Beispielzeile dass es
sich nicht um den (nicht-)Schutz von Variablen handelt sondern
eben um Registerinhalte.
A. K. schrieb:> Wenn du sämtliche Variablen des Hauptprogramms in der ISR anzusprechen> gedenkst, dann gibts viel Arbeit. Aber warum hast du das vor?
Davon war nicht die Rede. Siehe Variable vs Register.
isidor schrieb:> Ich lese in diesem Artikel dass man bei Interupts sehr vosichtig sein> soll da atomare Datenzugriffe bei einem auftretenden Interupt nicht> das gewünschte Ergebnis bringen können.
Ich nicht, kannst Du die Passage mal zitieren?
Nur, wenn man sich nicht sicher ist, sollte man den Zugriff auf
Interruptvariablen im Main atomar kapseln.
isidor schrieb:> Der Autor signalisiert ja ein seiner Code-Beispielzeile dass es> sich nicht um den (nicht-)Schutz von Variablen handelt sondern> eben um Registerinhalte.
C kennt keine Register. Man es gibt auch keine Grund sich um Register zu
kümmern, wenn man C programmiert.
isidor schrieb:> Davon war nicht die Rede.
Du hattest Sorgen, jede Zeile deines Programs schützen zu müssen.
Nochmal: weshalb? Teilt jede Zeile deines Programms Variablen mit der
ISR?
> Siehe Variable vs Register.
Der Begriff "Variable" kommt bei dir überhaupt nicht vor.
Ich vermute mal, dass du etwas missverstanden hast. Wie schon skizziert
wurde geht es nur um Variablen und I/O-Register, die in Hauptprogramm
und ISR angesprochen werden.
Die ISR wird kein Prozessorregister verändern. Daten, die nicht geteilt
werden, sind sicher.
Peter II schrieb:> isidor schrieb:>> Einfach ausgedrückt: ein AVR, mit Interrupt betrieben, würde ja bei>> beliebigen einfachen Code c = a + b potentiell Fehler liefern.>> nein, nur wenn a oder b > 8bit sind um in der ISR geändert werden.
Auch dann wenn sie in der ISR gelesen werden und in einer niedrigeren
ISR-Ebene verändert werden.
isidor schrieb:> Ich lese in diesem Artikel dass man bei Interupts sehr vosichtig sein> soll da atomare Datenzugriffe bei einem auftretenden Interupt nicht> das gewünschte Ergebnis bringen können.
Du meinst wohl nicht-atomar? Bzw. Nicht-atomare Operationen?
> Anderseits ist es doch die Aufgabe einer ISR möglichst am> Prozesserstatus und an den Registern nichts zu verändern.
Streiche das "möglichst".
Die Aufgabe eine ISR definiert du. Es ist Aufgabe des Compilers (oder
von dir im Falle einer (Inline)-Assember-ISR) den Prozessor-Status und
nicht-globale Register effektiv nicht zu ändern, d.h. zu sichern und
wieder herzustellen.
Globale Register darf der Compiler nicht restaurieren, denn sonst
wären sie nicht global. In der Standard-Konfiguration bzw. ABI von
avr-gcc gibt es übrigens keinen globalen Register.
> Wenn ich mir ein .lss File anschaue wird dort in der ISR auf> Maschinenebene doch einiges an Registern gepusht und gepoppt, und> zwar offensichtlich abhängig davon welche Optimierungsstufe> eingeschaltet ist und (bei Optimierung) bei welchen Registern es> sich überhaupt lohnt (wenn sie anderweitig zum Einsatz kommen).
Evtl. speichert avr-gcc mehr als nötig, was aber ein Problem der
Effizienz ist und nicht der Korrektheit.
> Einfach ausgedrückt: ein AVR, mit Interrupt betrieben, würde ja bei> beliebigen einfachen Code c = a + b potentiell Fehler liefern.
Es kommt auf die Verwendung und die Speicherklasse von a, b und c an.
Generell gilt folgende Regel: Falls
1) Auf mindestens eine ISR-Ebene das Datum geschrieben wird und
2) Das Datum auf einer anderen Ebene gelesen wird als geschrieben
dann muss die niedrigere ISR-Ebene ihre Zugriffe atomar machen.
Dies gilt auch für mehr als eine ISR-Ebene (dann für alle Paare an
ISR-Ebenen) oder auch ohne Interrupts in Multi-Core Controllern. Dem
Hauptprogramm ist in diesem Sinne auch eine ISR-Ebene zugeordnet,
nämlich die niedrigste. In Multi-Core Umgebung spielt die
Interrupte-Ebene keine Rolle, es sei denn, es handelt sich um
Core-lokale Daten.
Auf einem 8-Bit µC sind 8-Bit Lese- und Schreiboperationen i.d.R atomar,
es gibt jedoch auch Ausnahmen wie ein 8-Bit Bitfeld welches eine
8-Bit-Grenze überschreitet.
Arithmetische Operationen auf den Daten sind i.d.R nicht atomar, d.h.
auch wenn die Operanden von +, -, *, /, &, |, ^, ==, etc. atomar gelesen
und geschrieben werden können, ist die Gesamtoperation i.d.R.
nicht-atomar.
Beispiel zur Umsetzung von atomaren Speicher-Operationen sind die bei
manchen AVR Xmega existierenden Befehle LAC, LAS, LAT mit denen sich
8-bittige, atomare AND, OR und XOR implementieren lassen.
Peter Dannegger schrieb:> Ich nicht, kannst Du die Passage mal zitieren?
Hier steht es doch:
-------------------Start Zitat------------------------------------
Beispiel:
port |= 0x03;
übersetzt sich auf AVR-Prozessoren in
IN r16,port
ORI r16,0x03
OUT port,r16
Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise
Bit 7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren,
da der OUT-Befehl den alten Zustand vor dem Interrupt wiederherstellt.
Gefährlich ist das insbesondere deshalb, weil der Fall nur selten
auftritt und dieses Verhalten sehr schlecht reproduzierbar ist.
-------------------Ende Zitat------------------------------------
Nimmt man dies Aussage wörtlich dann findet sich in praktisch jeder
C-Zeile die Chance dass die Operation bei Interrupt nicht korrekt
ausgeführt wird. Und zwar nicht dadurch dass eine Variable verändert
wird sondern dass durch "geschrottete" Registerinhalte die Operation
fehlerhaft wird.
isidor schrieb:> port |= 0x03;>> übersetzt sich auf AVR-Prozessoren in>> IN r16,port> ORI r16,0x03> OUT port,r16
zum glück kann man das ja auch anders schreiben, was in den meisten
Fälle auch geht.
isidor schrieb:> Einfach ausgedrückt: ein AVR, mit Interrupt betrieben, würde ja bei> beliebigen einfachen Code c = a + b potentiell Fehler liefern.
Nur wenn a, b oder c sowohl in der ISR, als auch außerhalb davon benutzt
wird und der Zugriff darauf außerhalb der ISR nicht atomar erfolgt.
Zitat aus dem von dir verlinkten Artikel:
**********************************
Alle Variablen, Steuerregister und I/O-Ports, die sowohl im
Hauptprogramm als auch in Interrupts verwendet werden, sind mit viel
Sorgfalt zu behandeln.
**********************************
> Anders herum müsste ich ja für jede C-Zeile die Interrupt-geschützt> ausgeführt werden soll mit einem cli() / sei() einhüllen.
Ja.
> Ein Ding der Unmöglichkeit.
Es gibt meist nur wenige Zeilen, die tatsächlich Interrupt-geschützt
sein müssen.
isidor schrieb:> -------------------Start Zitat------------------------------------> Beispiel:>> port |= 0x03;>> übersetzt sich auf AVR-Prozessoren in>> IN r16,port> ORI r16,0x03> OUT port,r16>> Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise> Bit 7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren,> da der OUT-Befehl den alten Zustand vor dem Interrupt wiederherstellt.
Es handelt sich hier also um ein Register, das sowhohl in der ISR, als
auch außerhalb verwendet wird.
> Nimmt man dies Aussage wörtlich dann findet sich in praktisch jeder> C-Zeile die Chance dass die Operation bei Interrupt nicht korrekt> ausgeführt wird.
Nein. Nochmal: Nur bei Zugriff auf die I/O-Register und Variablen, die
sowohl in der ISR, als auch außerhalb verwendet werden.
> Und zwar nicht dadurch dass eine Variable verändert wird sondern dass> durch "geschrottete" Registerinhalte die Operation fehlerhaft wird.
Das Register ist letztendlich auch nichts anderes, als eine Variable.
isidor schrieb:> Und zwar nicht dadurch dass eine Variable verändert> wird sondern dass durch "geschrottete" Registerinhalte die Operation> fehlerhaft wird.>> Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise>> Bit 7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren,>> da der OUT-Befehl den alten Zustand vor dem Interrupt wiederherstellt.
Es geht um die Veränderung von Bit 7 des dort "port" genannten
I/O-Registers. Nicht eines Prozessorregisters.
Rolf Magnus schrieb:> Nein. Nochmal: Nur bei Zugriff auf die I/O-Register und Variablen, die> sowohl in der ISR, als auch außerhalb verwendet werden.
ja Danke! Das ist der Punkt bzw die Auflösung. Darauf kommt es an. Da
ich solche Fehler nie gemacht habe ist mir dieser feine Unterschied
nicht aufgefallen.
Also stimmt ihr mit mir überein das der GCC in ISRs alle in einem
Programm benutzen Register sichert und bei Verlassen wieder
restauriert (so "wie es sich gehört")?
isidor schrieb:> Also stimmt ihr mit mir überein das der GCC in ISRs alle in einem> Programm benutzen Register sichert und bei Verlassen wieder> restauriert (so "wie es sich gehört")?
Alle Prozessorregister. Deine Verwirrung kommt möglicherweise davon,
dass der Artikel auch I/O- und Steuer-Register erwähnt.
isidor schrieb:> Also stimmt ihr mit mir überein das der GCC in ISRs alle in einem> Programm benutzen Register sichert und bei Verlassen wieder> restauriert (so "wie es sich gehört")?
Ja. Du mußt hier eben sauber unterscheiden zwischen den
Prozessorregistern der ALU und den I/O-Register. Die ersteren existieren
auf C-Ebene nicht, und der Compiler kümmert sich automatisch darum. Die
I/O-Register dagegen sind Ressourcen, die letztendlich wie globale
Variablen zu handhaben sind.
Rolf Magnus schrieb:> Ja. Du mußt hier eben sauber unterscheiden zwischen den> Prozessorregistern der ALU und den I/O-Register. Die ersteren existieren> auf C-Ebene nicht,
So habe ich das schon gemeint, nur die passende, allumfassende
Formulierung dafür auf die Schnelle nicht gefunden. Natürlich
sind die Prozessor-Register gemeint ....
isidor schrieb:> Also stimmt ihr mit mir überein das der GCC in ISRs alle in einem> Programm benutzen Register sichert und bei Verlassen wieder> restauriert (so "wie es sich gehört")?
Das hab ich oben bereits geschrieben. Hilft aber nix wenn du es nicht
liest.
Ich verstehe die ganze Diskussion nicht. Es gehört doch zu den
Grundlagen der ISR Programmierung das man als Programmierer
berücksichtigen muss das Zugriffe auf Variablen (was letzten Endes
Speicherzellen sind) die sowohl in der ISR als auch im Hauptprogramm
manipuliert werden besonders behandelt werden müssen. SFR's sind wie
hier ja bereits angesprochen nichts anderes als Variablen mit speziellem
HW Bezug.
isidor schrieb:> Peter II schrieb:>> das ist dann sogar schneller.>> Themaverfehlung. Setzen. Sechs.
wenn Du mal etwas älter bist und etwas mehr Ahnung hast kannst du dir
solche Ausdrücke erlauben. Dann macht man es aber nicht mehr weil man
a) weiß dass Peter recht hat und
b) man es nicht mehr nötig hat (haben sollte) Sprüche zu klopfen