Forum: Compiler & IDEs ISR und Atomarer Datenzugriff - ich bin entsetzt !


von isidor (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

Wenn du sämtliche Variablen des Hauptprogramms in der ISR anzusprechen 
gedenkst, dann gibts viel Arbeit. Aber warum hast du das vor?

von Peter II (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Oder wenn c > 8bit ist und in der ISR mindestens gelesen wird.

von isidor (Gast)


Lesenswert?

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.

von isidor (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von isidor (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.
1
 port |= 0x02;
2
 port |= 0x01;

das ist dann sogar schneller.

von isidor (Gast)


Lesenswert?

Peter II schrieb:
> das ist dann sogar schneller.

Themaverfehlung. Setzen. Sechs.

von Rolf M. (rmagnus)


Lesenswert?

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.

von holger (Gast)


Lesenswert?

>> das ist dann sogar schneller.
>
>Themaverfehlung. Setzen. Sechs.

Nein, du hast nur nicht verstanden wieso;)

von (prx) A. K. (prx)


Lesenswert?

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.

von isidor (Gast)


Lesenswert?

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")?

von (prx) A. K. (prx)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von isidor (Gast)


Lesenswert?

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 ....

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

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.

von Walter S. (avatar)


Lesenswert?

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

: Bearbeitet durch User
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.