Forum: Mikrocontroller und Digitale Elektronik ISR und atomarer Zugriff auf 16bit Variable


von Grobi (Gast)


Lesenswert?

Hi,
ich hab da eine Verständnissfrage zu atomarem Zugriff auf 16 bit 
Variablen.
So wie ich das verstanden habe sollte eine Variable die in einer ISR und 
in anderen Funktionen wie z.B. der Hauptschleife verwendet wird als 
static volatile deklariert werden und wenn es eine 16Bit Variable ist 
dann sollte vor dem Zugriff ausserhalb der ISR die Interrupts 
ausgeschlatet und danach wiedser eingeschlatet werden damit der Wert der 
Variable während des Zugriffs nicht durch die ISR verändert werden kann.

z.B.:
1
//...
2
static volatile uint16_t a;
3
4
void ISR()
5
{
6
    a++;
7
}
8
9
int main()
10
{
11
    //...
12
    sei();
13
    //...
14
    while (1)
15
    {
16
        cli();
17
        if (a == ...)
18
        {
19
            //...
20
        }
21
        sei();
22
... oder :
23
        cli();
24
        uint16_t b = a;
25
        sei();
26
        if (b == ...)
27
        {
28
            //...
29
        }
30
    }
31
    //...
32
    return 0;
33
}

... wenn jetzt aber sagen wir "a" sehr wichtig für die Funktionalität 
des Programms ist z.B. für eine synchronisierung eines Protokolls etc. 
und ich den ISR auf gar keinen Fall "verpassen" darf, wäre es ja schon 
nicht so schlau beim Zugriff in der Hauptschleife die Interrupts 
auszuschalten.
Wie geht man bei sowas am Besten vor?

Thx
Grobi

von Georg (Gast)


Lesenswert?

Grobi schrieb:
> ich den ISR auf gar keinen Fall "verpassen" darf, wäre es ja schon
> nicht so schlau beim Zugriff in der Hauptschleife die Interrupts
> auszuschalten.

Den kann man nicht verpassen, der wird ausgeführt sobald sei ausgeführt 
wurde. Also wird er, bei korrekter Handhabung, nur um ein paar 
Prozessortakte verzögert. Man darf nur zwischen cli und sei nicht 
längere Zeit verstreichen lassen, cli - laden - sei hat keinen 
merklichen Einfluss auf das Verhalten des Systems.

Georg

von Peter D. (peda)


Lesenswert?

Grobi schrieb:
> wäre es ja schon
> nicht so schlau beim Zugriff in der Hauptschleife die Interrupts
> auszuschalten.

Wenn Dein Interrupt nichtmal 4 Zyklen Sperre verkraftet, dann hast Du 
die falsche CPU gewählt.
Schon jedes RET sperrt beim AVR die Interrupts für 4 Zyklen.

von Timmo H. (masterfx)


Lesenswert?

ATOMIC_BLOCK(ATOMIC_FORCEON)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Grobi schrieb:
> den ISR auf gar keinen Fall "verpassen" darf

Die meisten IRQs setzen ein Flag, und das passiert auch bei 
deaktivierten Interrupts.  Nach einem SEI werden diese IRQs nachgeholt.

Es werden alse keine IRQs verpasst, es sei denn

o Während der Sperre wird die gleiche IRQ mehr als 1x getriggert

o Die IRQ setzt kein Flag, was bei manchen Level-Triggered IRQs
  der Fall ist.

Da dein Code bis zur maximalen Unkenntlichkeit verunstaltet ist, lässt 
sich nix konkretes darüber sagen.

von Grobi (Gast)


Lesenswert?

Ah ok, Danke für die Infos, war mir nicht klar das der Interrupt 
sozusagen nachgeholt wird sobald er wieder durch sei freigegeben wird.

Also sollte die Variante :
cli();
uint16_t b = a;
sei();
im Prinzip die "sicherste" in diesem Fall sein.


"Da dein Code bis zur maximalen Unkenntlichkeit verunstaltet ist, lässt
sich nix konkretes darüber sagen." - das war auch nur ein schnell 
dahingeschmiertes Beispiel.

Vielen Dank!

von Max M. (jens2001)


Lesenswert?

BtW:

Grobi schrieb:
> wird als
> static volatile deklariert

Ein "static" vor einer globalen Variablen ist unnötig/unsinnig.
Globale variablen sind immer "static".

von Cyblord -. (cyblord)


Lesenswert?

Max M. schrieb:
> BtW:
>
> Grobi schrieb:
>> wird als
>> static volatile deklariert
>
> Ein "static" vor einer globalen Variablen ist unnötig/unsinnig.
Ist es nicht. Static bewirkt hier, dass die Variable von außerhalb des 
Moduls nicht sichtbar ist. Sie also gerade wegen dem static NICHT global 
sichtbar.

> Globale variablen sind immer "static".
So ein Quatsch.

von Stefan F. (Gast)


Lesenswert?

Nein, im globalen Kontext steuert man mit dem Schlüsselwort static, dass 
die damit gekennzeichnete Variable/Funktion nur in dieser einen Datei 
verwendet werden kann.

Das ist eine der Feinheiten, die ich an C nicht so gerne habe.

von W.S. (Gast)


Lesenswert?

Grobi schrieb:
> und wenn es eine 16Bit Variable ist
> dann sollte vor dem Zugriff ausserhalb der ISR die Interrupts
> ausgeschlatet und danach wiedser eingeschlatet werden damit...

Das ist - mal ganz grob gesagt - ein ganz schlechter Stil. Vielleicht 
kann man sowas auf den AVR's machen, aber woanders fällt man bei sowas 
fast immer auf die Nase. Bei den klassischen ARM-Controllern z.B. läuft 
main und alle von dort aufgerufenen Programmteile zumeist im User-Modus, 
da gibt es keine Möglichkeit, mal eben die Interrupts zu sperren. Das 
geht dort nur im Supervisor-Modus.

Die Lösung für dein eigentliches Problem ist schlichtweg eine 
geeignete Synchronisation zwischen ansonsten unkorrelierten 
Programmteilen. Wieso sollten denn auch bloß zwei verschiedene 
Programmteile SCHREIBEND auf die selbe variable zugreifen? Sowas braucht 
man regelmäßig nicht, wenn man sich ein paar Gedanken zuvor gemacht hat.

W.S.

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> Bei den klassischen ARM-Controllern z.B. läuft main und alle von dort
> aufgerufenen Programmteile zumeist im User-Modus, da gibt es keine
> Möglichkeit, mal eben die Interrupts zu sperren.

Quatsch, nur wenn man ein OS verwendet kann das so sein. Im normalen 
"bare metal " Betrieb ist es auch beim ARM überhaupt kein Problem, im 
Interrupt die Interrupts zu sperren.

W.S. schrieb:
> Wieso sollten denn auch bloß zwei verschiedene Programmteile SCHREIBEND
> auf die selbe variable zugreifen? Sowas braucht man regelmäßig nicht,
> wenn man sich ein paar Gedanken zuvor gemacht hat.
Das Problem mit der Atomizität hat man auch, wenn man nur an einer 
Stelle schreibt. Und selbstverständlich muss man oft auch von mehreren 
Interrupts schreiben, zB wenn man einen Ringpuffer hat der von 
verschiedenen Interrupts gelesen & geschrieben wird - dann muss man die 
Zeiger von allen diesen Interrupts aus anpassen.

Im bare metal Betrieb ist die Interruptsperre das gängige Mittel zur 
Synchronisation. Die klassischen Thread Synchronisations Mittel wie 
Mutexe machen nur Sinn wenn man ein (RT)OS mit Scheduler verwendet, was 
bei weitem nicht immer der Fall ist, auch nicht auf dem ARM.

von Loddaar (Gast)


Lesenswert?

W.S. schrieb:
> Wieso sollten denn auch bloß zwei verschiedene
> Programmteile SCHREIBEND auf die selbe variable zugreifen?

das Problem tritt doch auch auf wenn nur ein Programmteil schreibt (der 
Interrupt) und im anderen gelesen (main) gelesen wird.
main liest dann z.B. das erste Byte des Worts, jetzt kommt der Interrupt 
und verändert das Wort und dann holt sich main das zweite Byte und hat 
jetzt ein Wort gelesen, das aus zwei verschiedenen Worten 
zusammengesetzt ist

von Georg (Gast)


Lesenswert?

Loddaar schrieb:
> das Problem tritt doch auch auf wenn nur ein Programmteil schreibt (der
> Interrupt) und im anderen gelesen (main) gelesen wird.

Klar, was denn sonst. Hier werden primitivste Grundlagen zerredet, die 
seit 40 Jahren vollkommen geklärt und ausdiskutiert sind, und die Folge 
cli - Varaible lesen/schreiben - sei ist jedem Programmierer vertraut, 
der sein Handwerk versteht.

W.S. schrieb:
> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.

Das ist - mal ganz grob gesagt - vollkommene Ahnungslosigkeit.

Georg

von Ulrich F. (Gast)


Lesenswert?

Auch als Arduino Jünger weiß ich, dass in solchen Fällen, das sperren 
der Interrupts, und sei es auch nur dieser eine kritische, nahezu 
alternativlos ist.

Das Eingangsposten "riecht" nach 8Bit AVR.
Also sind die ARM Gedichte sowieso .....

von Falk B. (falk)


Lesenswert?

Siehe Interrupt.

von Peter II (Gast)


Lesenswert?

Georg schrieb:
> Klar, was denn sonst. Hier werden primitivste Grundlagen zerredet, die
> seit 40 Jahren vollkommen geklärt und ausdiskutiert sind, und die Folge
> cli - Varaible lesen/schreiben - sei ist jedem Programmierer vertraut,
> der sein Handwerk versteht.

wobei man schauen kann ob die CPU dafür spezielle Atomic befehle hat. 
Beim x86 z.b. InterlockedIncrement

könnte mir vorstellen, das es auch bei ARM so etwas gibt.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> könnte mir vorstellen, das es auch bei ARM so etwas gibt.

Nicht atomar, aber ohne Abschaltung oder Verzögerung von Interrupts:
Beitrag "Re: STM32 Systick Variable zurücksetzen"

Kommt ein Interrupt dazwischen, wird es einfach nochmal gemacht.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Georg schrieb:
>> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.
> Das ist - mal ganz grob gesagt - vollkommene Ahnungslosigkeit.
Das ist - mal ganz grob gesagt - eine stillose Keilerei.

Es gibt mehrere Wege. Je nach Umgebung. Und je nachdem, ob man mit 
Interrupts oder parallelen Threads zu tun hat. Was bei AVR richtig ist 
kann anderswo falsch sein.

Bei AVR, wo man weder User-Mode hat noch irgendwelche Realtime-Kernels 
oder gar Betriebssysteme regelmässig mit sich rumträgt, läuft es kaum 
vermeidbar auf cli/sei raus, wobei dies bei Verwendung vom GCC 
sinnvollerweise hinter ATOMIC_BLOCK(...) versteckt wird.

Andere Prozessoren mit bare metal programming ohne RTOS haben teilweise 
andere Methoden dafür. So kann man bei priorisierten Interrupt-Systemen 
statt alle Interrupts abzuschalten auch dafür sorgen, dass der 
Interrupt-Level zeitweilig grad so weit angehoben wird, dass der 
betreffende Interrupt kurz gesperrt ist, aber höher priorisierte 
ungebremst durchkommen. Die Cortex M besitzen dafür ein recht speziell 
arbeitendes Register BASEPRI_MAX.

Spezielle nicht-atomare Befehle für den Umgang mit solchen Daten hatte 
ich vorhin verlinkt. Solche Befehle sind charakteristisch für RISC 
Prozessoren, bei denen der komplexe Ablauf von atomaren 
read-modify-write Befehlen untypisch für deren Arbeitweise ist. Die 
können dann auch in Varianten auftreten, je nachdem ob es dabei um 
Interrupts oder um parallele Cores geht.

CISC Prozessoren wie x86 wiederum verfügen oft über diverse Befehle zur 
atomaren Manipulation von Daten.

Wenn man ein RTOS oder gar ein komplettes Betriebssystem drauf hat, dann 
verwendet man sinnvollerweise dessen Methoden, wobei man bei Spinlocks 
wieder ungefähr auf dem Level der ldrex/strex Befehle landet. Im 
Usermode wohlgemerkt. Und man in der Oberklasse noch weitere Feinheiten 
berücksichtigen darf, aber da sind wir hier wohl eher nicht.

Und wem das alles immer noch zu einfach ist, der schaut mal rein, was 
Intel mit Haswell dazu neu eingebracht hat. ;-)

: Bearbeitet durch User
von Georg (Gast)


Lesenswert?

A. K. schrieb:
> läuft es kaum
> vermeidbar auf cli/sei raus

Und das hast du als schlechten Stil bezeichet. Hast du dich inzwischen 
weitergebildet? Pseudointelligentes Geschwafel über Usermodus ist völlig 
fehl am Platz, Hardware mit Interruptsteuerung kann man nun mal bloss 
bedienen, wenn man auch auf Interrupt Control Bits zugreifen kann und 
ISR ausführen kann, also eben nicht im User Modus. Jemanden, der einen 
Treiber für interruptgesteuerte Peripherie-Hardware schreibt schon 
allein deswegen eines schlechten Stils zu beschuldigen ist einfach eine 
Frechheit. Da ein Controller ohne Peripherie nutzlos ist, ist das eine 
Notwendigkeit, auch wenn du glaubst dass du über sowas erhaben bist.

Und das ist nicht nur bei AVR so, selbst x86-Prozessoren verwenden 
Interrupts. Und die schalten sich nicht von selbst aus und ein.

Georg

von c-hater (Gast)


Lesenswert?

W.S. schrieb:

> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.

Nein, ist es nicht. Auf AVR8 ist es sogar praktisch (fast) die einzige 
Möglichkeit zur Synchronisation zwischen ISRs untereinander und ISRs und 
main. Die einzige Alternative daz wäre die spezifische Sperrung von IRQs 
mittels der entsprechenden Maskenbits.

> Vielleicht
> kann man sowas auf den AVR's machen

Nicht "kann", nein man MUSS es so tun.

> aber woanders fällt man bei sowas
> fast immer auf die Nase. Bei den klassischen ARM-Controllern z.B. läuft
> main und alle von dort aufgerufenen Programmteile zumeist im User-Modus,
> da gibt es keine Möglichkeit, mal eben die Interrupts zu sperren. Das
> geht dort nur im Supervisor-Modus.

Da bietet dann aber auch die Hardware andere Mechanismen zur 
Synchronisation an.

> Wieso sollten denn auch bloß zwei verschiedene
> Programmteile SCHREIBEND auf die selbe variable zugreifen? Sowas braucht
> man regelmäßig nicht, wenn man sich ein paar Gedanken zuvor gemacht hat.

Bullshit, das ist gang und gäbe, es wird z.B. regelmäßig für FIFOs 
benötigt. Es ist nicht gerade ungewöhnlich, Daten zwischen ISRs und main 
per FIFO zu buffern. Und dazu gehört halt zwingend der atomare Zugriff 
auf Schreib- und Lesepointer (bzw. -offsets).

von (prx) A. K. (prx)


Lesenswert?

Georg schrieb:
> Hast du dich inzwischen
> weitergebildet? Pseudointelligentes Geschwafel über Usermodus ist völlig
> fehl am Platz,

Danke für die Blumen. Hast du damit wirklich mich gemeint? Zu viel 
Emotion kann auch mal die Sicht behindern.

: Bearbeitet durch User
von Norbert (Gast)


Lesenswert?

1
volatile uint16_t pleaseReadMe;
2
3
uint16_t var1;
4
uint16_t var2;
5
do {
6
    var1 = pleaseReadMe;
7
    var2 = pleaseReadMe;
8
} while (var1 != var2);

Wäre so etwas nicht auch möglich falls man auf gar keinen Fall 
Interrupts abschalten will?

von M. K. (sylaina)


Lesenswert?

Auch das wäre eine Möglichkeit, Norbert. Ich sehe das aber auch wie 
manch anderer hier: Bei den AVRs ist das keine Schande mal eben zum 
Lesen einer Variablen die Interrupts abzuschalten. Wieviel Interrupts 
sollen denn in 4 Zyklen kommen die man verpassen könnte? Wenn man es so 
zeitkritisch hat ist vielleicht auch ein AVR nichts für die gestellte 
Aufgabe.

von Peter D. (peda)


Lesenswert?

Insbesondere, da der AVR keine Interruptlevel hat.
D.h. wenn man noch andere Interrupts benutzt, ist die Sperre durch einen 
gerade aktiven Handler mindestens 40 Zyklen. Dagegen sind nur 4 Zyklen 
ein Witz.

Sogar ein völlig leerer Handler sperrt sich selber für die nächsten 
11..14 Zyklen. Daher sieht man es manchmal für sehr schnelle Sequenzen, 
daß kein Interrupt benutzt wird, sondern Polling (3 Zyklen).

von W.S. (Gast)


Lesenswert?

Norbert schrieb:
> Wäre so etwas nicht auch möglich falls man auf gar keinen Fall
> Interrupts abschalten will?

Nun ja, so ähnlich etwa.
Es gibt immer und auf allen Architekturen die Möglichkeit, Daten 
zwischen unabhängigen Programmteilen auszutauschen, ohne Interrupst zu 
sperren.

Das, was hier der Ausgangspunkt war, ist ja das KONKURRIERENDE Zugreifen 
auf die gleiche Speicherstelle.

Aber wozu dies?
Wenn man für sowas einen winzigkleinen Fifo (sprich Ringpuffer) 
einführt, ist alles entkoppelt. Der eine Prozeß hat exclusiven Zugriff 
auf den Schreibzeiger und der andere auf den Lesezeiger. Fertig.

Eine andere Methode ist das Verwenden von "events", noch eine andere ist 
das Benutzen von zwei simplen Aktualisierungscodes (ist ähnlich zum 
Fifo). Kurzum, es gibt ne Menge von Verfahren, die völlig ohne solche 
Brutalmethoden wie dem Sperren von Interrupts auskommen.


c-hater schrieb:
>> Das ist - mal ganz grob gesagt - ein ganz schlechter Stil.
>
> Nein, ist es nicht.

Oh ist es doch. Das Prinzip ist ja immer, daß ein Programmteil einem 
anderen Programmteil etwas mitteilen will. Es gibt also immer einen 
"Ansager" und einen "Zuhörer" und man muß lediglich darauf achten, daß 
diese Art Kommunikation einseitig ist. Wenn man also bidirektional sich 
was mitteilen will, muß man zwei Mechanismen (Ansager-->Zuhörer) 
einrichten, für jede Richtung eben einen - ganz generell formuliert. Und 
wenn es noch mehr Programmteile geben sollte, die irgendwelche 
Mitteilungen zur Kenntnis nehmen müssen, dann eben per 
"Event-Broadcast".

W.S.

von Stefan F. (Gast)


Lesenswert?

> Der eine Prozeß hat exclusiven Zugriff auf den
> Schreibzeiger und der andere auf den Lesezeiger. Fertig.

Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller 
diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?

von (prx) A. K. (prx)


Lesenswert?

Stefan U. schrieb:
>> Der eine Prozeß hat exclusiven Zugriff auf den
>> Schreibzeiger und der andere auf den Lesezeiger. Fertig.
>
> Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller
> diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?

In diesem speziellen Fall geht das wirklich, wenn man es richtig macht 
und diese Zeiger (ggf. Indexe) mit der maximalen atomaren Wortbreite der 
Maschine glücklich sind. Dann ein N-Byte FIFO maximal N-1 Bytes puffern. 
Also maximal 255 Bytes bei AVRs.

Allerdings halte ich die Aussage, dass man immer ohne Interrupt-Sperre 
auskommt, für reichlich gewagt.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Stefan U. schrieb:
> Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller
> diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?

Indem der eine Prozeß eben den EXCLUSIVEN Zugriff auf den Schreibzeiger 
hat und der andere selbigen auf den Lesezeiger. Lerne Programmieren und 
lies nach, wie man einen Fifo bzw. Ringpuffer schreibt. Herrjenochmal!

W.S.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
> Indem der eine Prozeß eben den EXCLUSIVEN Zugriff auf den Schreibzeiger
> hat und der andere selbigen auf den Lesezeiger.

Das mir bekannte Verfahren benötigt schreibenden Zugriff auf den eigenen 
Zeiger und lesenden Zugriff auf den anderen. Exklusiv ist er also nicht.

Daraus ergibt sich aber die eben erwähnte Einschränkung auf atomare 
Lese- und Schreibzugriffe.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> Allerdings halte ich die Aussage, dass man immer ohne Interrupt-Sperre
> auskommt, für reichlich gewagt.

Nö. es geht prinzipiell immer.
Der Knackpunkt ist ja nichts anderes als die Daten-Korruption, die genau 
dadurch zustande kommt, daß es zwei unkorrelierten Prozessen erlaubt 
ist, auf die gleiche Ressource zuzugreifen. Das gilt nicht nur für den 
Fall des TO mit seinem 16 Bit Wort, sondern auch für den Betrieb von 
Schnittstellen aller Art, wo nicht gepollt, sondern auch mit Interrupts 
gearbeitet werden soll.

W.S.

von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> Das mir bekannte Verfahren benötigt schreibenden Zugriff auf den eigenen
> Zeiger und lesenden Zugriff auf den anderen. Exklusiv ist er also nicht.

Dann guck in die Lernbetty ;-)

Also: das Programm, was den Fifo füllt, darf den Schreibzeiger verändern 
und den Lesezeiger nur lesen (aber nicht verändern). Das Programm, was 
den Fifo leert, darf den Lesezeiger verändern, aber auf den 
Schreibzeiger nur lesend zugreifen. Fazit: ein jedes Programm hat seine 
exclusive Ressource (jeder seinen Zeiger) und keines kommt dem anderen 
in die Quere.

Das einzige, was da noch kommen könnte, wäre ein drohendes Überlaufen 
des Fifo - aber das kann nur aufgrund eines krepierten Threads oder 
sonstigen generellen Fehlers im Systementwurf vorkommen. Der andere Fall 
(Fifo ist leer) ist normal, denn in diesem Falle haben sich beide 
Programme eben mal nix zu sagen.

W.S.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
> Nö. es geht prinzipiell immer.

Dann will ich es mal anders ausdrücken: Hältst du es wirklich für guten 
Stil, auf Prozessoren der AVR Kategorie in jedem Fall auf 
Interrupt-Sperre zu verzichten? Mich deucht, dass diese Verfahren die 
Komplexität der Programme deutlich erhöhen werden, ohne aber in den 
meisten Fällen irgendwelche Vorteile zu bringen.

Stelle dabei diesen Lösungen einen Code wie in
  ATOMIC_BLOCK(...)
  {
    ...
  }
gegenüber. Der das auf recht elegante Art kapselt, technisch aber nichts 
anderes ist als disable/enable oder save/disable/restore.

Das lässt sich auch verallgemeinern, wenn du solche AVR Kleinigkeiten 
unbedingt als einen Aspekt von Interprozess-Kommunikation ausdrücken 
willst. Vom Rendevouz-Konzept im ursprünglichen Ada abgesehen bin ich 
noch keinem API zur Interprozess-Kommunikation begegnet, in dem die 
Sperre von Unterbrechungen innerhalb der Prozessfamilie kein 
wesentliches und auch ab und zu genutztes Element war. Und sei es nur, 
um andere höherstehende Methoden auf einfache Art zu implementieren. Das 
heisst ja nicht, dass man auf die anderen Methoden verzichten sollte. 
Aber umgekehrt eben auch nicht, dass man Sperren unbedingt vermeiden 
sollte.

Wenn solche Sperren also stets schlechter Stil seien, dann scheint es 
mir, dass du einen andere Stilbegriff pflegst, als der Rest der Welt.

von Klaus (Gast)


Lesenswert?

W.S. schrieb:
> Also: das Programm, was den Fifo füllt ....

Watt ein Aufwand, nur um einen "atomaren Zugriff auf eine 16 Bit 
Variable" zu ermöglichen.

Da sieht man mal die Vorteile eines 32-Bitters, da ist das alles for 
free;)


MfG Klaus

von Carl D. (jcw2)


Lesenswert?

> Dann guck in die Lernbetty ;-)

> Da sieht man mal die Vorteile eines 32-Bitters, da ist das alles for
> free;)

AVR ist 8-Bit, oder W.S.?

von Konrad S. (maybee)


Lesenswert?

Norbert schrieb:
> volatile uint16_t pleaseReadMe;
>
> uint16_t var1;
> uint16_t var2;
> do {
>     var1 = pleaseReadMe;
>     var2 = pleaseReadMe;
> } while (var1 != var2);
> Wäre so etwas nicht auch möglich falls man auf gar keinen Fall
> Interrupts abschalten will?

Das stellt nicht sicher, dass du einen korrekten Wert erhältst. An 
atomaren Zugriffen führt kein Weg vorbei. Und wenn W.S. lange genug in 
seine Lernbetty reingeguckt hat, dann wird er möglicherweise erkennen, 
dass er auf seine Schreib- und Lesezeiger atomar zugreift (was aber 
jetzt auf einem ARM auch kein Kunststück ist).

von Peter II (Gast)


Lesenswert?

Konrad S. schrieb:
> Das stellt nicht sicher, dass du einen korrekten Wert erhältst. An
> atomaren Zugriffen führt kein Weg vorbei.

kannst du das begründen?

Ich wüsste nicht wie ein unkorrekter Wert da durch kommen soll.

Andere System machen es ähnlich:

http://i1.blogs.msdn.com/b/oldnewthing/archive/2013/09/13/10448736.aspx

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
>> Das stellt nicht sicher, dass du einen korrekten Wert erhältst. An
>> atomaren Zugriffen führt kein Weg vorbei.
>
> Ich wüsste nicht wie ein unkorrekter Wert da durch kommen soll.

Es ist je nach Häufigkeit und Dauer von Interrupts nicht völlig 
ausgeschlossen, dass man zweimal den gleichen Fehler kriegt. Nur extrem 
unwahrscheinlich.

von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Es ist je nach Häufigkeit und Dauer von Interrupts nicht völlig
> ausgeschlossen, dass man zweimal den gleichen Fehler kriegt. Nur extrem
> unwahrscheinlich.

müsste nicht direkt nacheinander der High und Lowpart geändert werden?

Wenn man 2mal hintereinander den gleichen wert liest, kann keine ISR in 
der Zeit den wert geändert haben. Es soll doch nur verhindert das der 
Low-Wert nicht zum High-wert einer 16bit variabel passt.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> Andere System machen es ähnlich:
> http://i1.blogs.msdn.com/b/oldnewthing/archive/2013/09/13/10448736.aspx

Völlig andere Baustelle. load-linked und store-conditional sind in sich 
atomar, erkennen aber eine Unterbrechung oder einen konkurrierenden 
Buszugriff über ein entsprechendes Flag oder ein Adresstag. Das hatte 
ich oben schon aufgeführt, siehe 
https://www.mikrocontroller.net/topic/goto_post/4273089.

von Stefan F. (Gast)


Lesenswert?

@W.S.

>> Schön geschrieben. Wie realisiert man denn auf einem AVR Mikrocontroller
>> diesen Exklusiven Zugriff, wenn nicht durch Sperren von Interrupts?

> Indem der eine Prozeß eben den EXCLUSIVEN Zugriff auf den
> Schreibzeiger hat und der andere selbigen auf den Lesezeiger.
> Lerne Programmieren und lies nach, wie man einen Fifo bzw.
> Ringpuffer schreibt. Herrjenochmal!

Ich würde das ja gerne lernen. Hilf mir doch dabei, anstatt zu meckern.

Wenn ich mit zwei Zeigern Arbeite, dann vergleiche ich sie miteinander, 
um festzustellen, ob der Puffer leer ist bzw. wie viele Zeichen drin 
sind. Für diesen Vergleich muss ich zwangsläufig auf beide Pointer 
zugreifen.

Oder ich nutze eine Variable, die die Anzahl der Zeichen im Puffer 
angibt. Das wäre dann aber auch keine exklusive Variable, auch die 
müsste ich in beiden Prozessen verwenden.

Wie kann ich das umgehen bzw. besser machen?

Jetzt sag Du mit bitte, wie man es anders (besser?) machen kann.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> Wenn man 2mal hintereinander den gleichen wert liest, kann keine ISR in
> der Zeit den wert geändert haben.

Doch. Beispielsweise wenn bei sehr aktiven Interrupts der Zähler 
zwischendrin eine komplette Runde gedreht hat und du beim zweiten 
Durchlauf der Schleife exakt den gleichen Moment erwischst.

Ja klar, ist extrem unwahrscheinlich. Müssen aber auch nicht komplette 
65536 sein. Etwas wahrscheinlicher, wenn jemand zwar 16 Bits verwendet, 
aber nur -1, 0, 1 zählt.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Stefan U. schrieb:
> Für diesen Vergleich muss ich zwangsläufig auf beide Pointer
> zugreifen.

Ja. Aber so lange jeder dieser Zugriff in sich atomar ist, kann man bei 
richtiger Reihenfolge der Operationen auf eine Sperre verzichten.

Etwas unglücklich ist deshalb hier der Begriff "Zeiger". Verwendet man 
einen 8-Bit Index ist das auch beim AVR möglich, begrenzt aber den 
maximal verarbeitbaren Puffer.

von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Doch. Beispielsweise wenn bei sehr aktiven Interrupts der Zähler
> zwischendrin eine komplette Runde gedreht hat und du beim zweiten
> Durchlauf der Schleife exakt den gleichen Moment erwischst.

ok, bei den AVS ist es ja so, dann immer noch mindestens 1Befehl 
außerhalb der ISR ausgeführt wird. Damit kann das ja nach Hardware gar 
nicht passieren.

> Etwas wahrscheinlicher, wenn jemand zwar 16 Bits verwendet,
> aber nur -1, 0, 1 zählt.
dann spielt nur das LowByte eine rolle und ist damit immer richtig.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
>> Etwas wahrscheinlicher, wenn jemand zwar 16 Bits verwendet,
>> aber nur -1, 0, 1 zählt.
> dann spielt nur das LowByte eine rolle und ist damit immer richtig.

Wenn der Programmierer einen nicht-atomaren 16-Bit Typ verwendet, dann 
wird der Compiler beide Bytes laden und verwenden.

Klar hätte der Programmierer dann ein Byte nehmen können und damit das 
Problem vom Hals gehabt. Sowas kann in auch mal entstehen, wenn man 
generischen Code verwendet, der nicht immer mit 8 Bits auskommt.

von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Wenn der Programmierer einen nicht-atomaren 16-Bit Typ verwendet, dann
> wird der Compiler beide Bytes laden und verwenden.

ja aber da High-Byte ändert sich nicht. Damit passt es immer zum 
LowByte.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> ok, bei den AVS ist es ja so, dann immer noch mindestens 1Befehl
> außerhalb der ISR ausgeführt wird. Damit kann das ja nach Hardware gar
> nicht passieren.

Es sei denn, der Programmierer verwendet verschachtelte Interrupts, 
indem er im low-prio Handler die Interrupts freigibt (oder ein RTOS).

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> ja aber da High-Byte ändert sich nicht.

Dein AVR ist kaputt. Bei meinem wird -1 als 0xFFFF codiert, 0 aber als 
0x0000.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Dein AVR ist kaputt. Bei meinem wird -1 in 0xFFFF codiert.

ah, sorry, -1 hatte ich ignoriert.

von Konrad S. (maybee)


Lesenswert?

Peter II schrieb:
> mindestens 1Befehl
> außerhalb der ISR ausgeführt wird.

Du brauchst auf AVR nur einen (Assembler-)Befehl um die zweite Hälfte 
der ersten Variablen und die erste Hälfte der zweiten Variablen zu 
kopieren?

von Peter II (Gast)


Lesenswert?

Konrad S. schrieb:
> Du brauchst auf AVR nur einen (Assembler-)Befehl um die zweite Hälfte
> der ersten Variablen und die erste Hälfte der zweiten Variablen zu
> kopieren?

nein, wer hat das gesagt? Durch den einen Befehl wird aber verhindert 
das die variabel innerhalb der beiden ausleseversuche einmal komplett 
überläuft.

von Konrad S. (maybee)


Lesenswert?

Peter II schrieb:
> Durch den einen Befehl wird aber verhindert
> das die variabel innerhalb der beiden ausleseversuche einmal komplett
> überläuft.

Für den allgemeinen Fall musst du davon ausgehen, dass sich die Daten 
durch die Interruptroutine beliebig ändern können.

von Peter II (Gast)


Lesenswert?

Konrad S. schrieb:
> Peter II schrieb:
>> Durch den einen Befehl wird aber verhindert
>> das die variabel innerhalb der beiden ausleseversuche einmal komplett
>> überläuft.
>
> Für den allgemeinen Fall musst du davon ausgehen, dass sich die Daten
> durch die Interruptroutine beliebig ändern können.

dann beschreibe doch mal bitte genau wo und wann die ISR die Variabel 
abändert so das etwas ungültige gelesen wird

> uint16_t var1;
> uint16_t var2;
> do {
>     var1 = pleaseReadMe;
>     var2 = pleaseReadMe;
> } while (var1 != var2);

von Peter D. (peda)


Lesenswert?

Ich setze die atomic.h sehr gerne ein. Sie macht vieles deutlich 
einfacher, kürzer und schneller und man bekommt beim Programmieren auch 
keine Knoten im Gehirn von den ganze Würgarounds. Je komplexer eine 
Lösung, umso höher ist nämlich die Gefahr von Programmierfehlern.

Ich würde sogar soweit gehen, wenn man sich nicht zu 100% sicher ist, 
lieber ein atomic zuviel, als eins vergessen zu haben. Solche Fehler 
sind nämlich sehr eklig zu finden.

Niemand fragt danach, ob eine Lösung besonders elegant ist. Aber ob man 
sie schnell implementieren kann, sie sicher ist, und möglichst wenig 
Seiteneffekte hat.

Daß andere Architekturen andere Mechanismen für konkurrierende Zugriffe 
haben, bezweifelt doch niemand. Der AVR hat sie eben nicht und Atmel 
wird da auch nichts mehr einbauen.

Wer sich auf dem AVR die Hose mit der Kneifzange anziehen will, kann 
dies gerne tun. Nur soll er anderen gefälligst nicht sein persönliche 
Meinung aufzwingen. Das geht eindeutig zu weit.

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

I steht für das Schreiben des Interrupts in pleaseReadMe, R und W für 
das Lesen aus pleaseReadMe bzw. das Schreiben in var1/var2 im 
Hauptprogramm.
1
I 0x1234
2
R 0x34
3
I 0x5678
4
W 0x34
5
R 0x56
6
I 0x9a34
7
W 0x56    ---> 0x5634 in var1
8
R 0x34
9
I 0x56bc
10
W 0x34
11
R 0x56
12
W 0x56    ---> 0x5634 in var2
Das Ergebnis ist ein Wert, den die Interruptroutine nie geschrieben hat.

von Peter II (Gast)


Lesenswert?

Konrad S. schrieb:
> Das Ergebnis ist ein Wert, den die Interruptroutine nie geschrieben hat.

ok, ich sehen es ein.

Es geht als nur sicher, wenn man wie oben a++ mit der Variable rechnet.

von Peter D. (peda)


Lesenswert?

Ich sag mal, die AVR-GCC Entwickler sind bestimmt keine Dummköpfe. Und 
daß sie die atomic.h implementiert haben, beweist, daß sie sie für 
notwendig erachten und daß ihnen keine adäquate bessere Lösung bekannt 
ist.

: Bearbeitet durch User
von Georg (Gast)


Lesenswert?

A. K. schrieb:
> Ja klar, ist extrem unwahrscheinlich.

Wenn etwas bei der Programmierung absolut verboten ist, dann einen 
Fehler zu belassen, weil man glaubt er wäre so unwahrscheinlich dass er 
nie auftritt. Abgesehen davon, dass das unter Sicherheitsaspekten 
sowieso indiskutabel ist, es stimmt in den meisten Fällen auch nicht. 
Ich habe mir vor vielen Jahren auch mal gedacht, das tritt nur alle paar 
Jahre mal auf (was an sich ja schon ein Fehler war, da war ich halt noch 
jung und unerfahren), dann habe ich das Gerät eingeschaltet und nach 20 
sec war der Fehler da. So was ist nie mehr unterlaufen.

Heutige Controller führen Millionen Befehle pro Sekunde aus, da kann die 
Wahrscheinlichkeit noch so gering sein, es passiert.

Georg

von Konrad S. (maybee)


Lesenswert?

Man übersieht im täglichen Programmiergeschäft gerne (weil meist 
irrelevant), dass Zugriffe auf Daten "nativer" Größe auf allen(?) 
Prozessoren und Controllern atomar ablaufen und bei Zugriffen auf 
größere Daten ggf. Vorsorgemaßnahmen für atomaren Zugriff getroffen 
werden müssen.

von (prx) A. K. (prx)


Lesenswert?

Konrad S. schrieb:
> dass Zugriffe auf Daten "nativer" Größe auf allen(?)
> Prozessoren und Controllern atomar ablaufen

Wobei das noch ein wenig komplizierter wird, wenn man mit mehreren 
parallel arbeitenden Cores arbeitet. Das ist in jenen µC Anwendungen, 
mit denen man hier im Forum meist zu tun hat, zwar noch nicht 
verbreitet, aber arg lange wird es wohl nicht mehr dauern, bis das auch 
da Einzug hält.

Interrupts zu sperren bringt da nichts und Befehle, die man bisher als 
atomar ansah, sind es vielleicht nicht mehr. Aber genau da greift dann 
das vorhin erwähnte load-linked/store-conditional Verfahren. Oder eine 
Lib, die darauf aufbaut.

In anderen Bereichen ist das längst Tagesgeschäft, etwa bei PC 
Programmierung. Und in "höheren" embedded Umgebungen wird man das wohl 
auch schon finden.

Wer mit dem aktuellen RPi seine Steuerung baut, der kann damit schon mal 
üben.

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

A. K. schrieb:
> Wobei das noch ein wenig komplizierter wird, wenn man mit mehreren
> parallel arbeitenden Cores arbeitet. Das ist in jenen µC Anwendungen,
> mit denen man hier im Forum meist zu tun hat, zwar noch nicht
> verbreitet, aber arg lange wird es wohl nicht mehr dauern, bis das auch
> da Einzug hält.
>
> Interrupts zu sperren bringt da nichts und Befehle, die man bisher als
> atomar ansah, sind es vielleicht nicht mehr. Aber genau da greift dann
> das vorhin erwähnte load-linked/store-conditional Verfahren. Oder eine
> Lib, die darauf aufbaut.

Caches, Membar usw. hab ich mir annodazumal reingezogen. Es muss ja nur 
einmal das grundlegende Verständnis für die Problematik in den Kopf. 
Danach ist es relativt leicht, man muss ja nur noch die vorgesehenen 
Mechanismen nutzen (und natürlich kennen ;-).

> In anderen Bereichen ist das längst Tagesgeschäft, etwa bei PC
> Programmierung. Und in "höheren" embedded Umgebungen wird man das wohl
> auch schon finden.

Da muss sich Otto Normalprogrammierer um den ganzen Kleinkram nicht mehr 
kümmern. Er meldet seine Wünsche beim Betriebssystem an und das kümmert 
sich dann darum. Und weil's so langweilig wär, holt man sich mit 
Multithreading die gleichen Probleme in die Anwendungsprogramme. ;-)

> Wer mit dem aktuellen RPi seine Steuerung baut, der kann damit schon mal
> üben.

Lieber papp ich dem RPi einen AVR dran. Soll der sich um die Echtzeit 
kümmern. Der kann das besser und ich behalte den Überblick.

von (prx) A. K. (prx)


Lesenswert?

Konrad S. schrieb:
> Danach ist es relativt leicht,

Anfangs. Wird komplizierter, desto tiefer du einsteigst.

Neben atomarem Zeugs hast du flugs auch massive Performanceaspekte am 
Hals. Es ist nämlich saublöd, wenn der eine Thread mit seinen Zugriffen 
dem anderen Thread die Leistung verhagelt.

Und am oberen Ende wüsste ich ja schon gerne, ob du glaubhaft versichern 
kannst, dass du alle Aspekte von Haswells TSX Befehlen relativ leicht 
findest. Die haben nämlich direkt damit zu tun. ;-)

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

A. K. schrieb:
> Konrad S. schrieb:
>> Danach ist es relativt leicht,
>
> Anfangs. Wird komplizierter, desto tiefer du einsteigst.
>
> Neben atomarem Zeugs hast du flugs auch massive Performanceaspekte am
> Hals. Es ist nämlich saublöd, wenn der eine Thread mit seinen Zugriffen
> dem anderen Thread die Leistung verhagelt.

... nicht zu vergessen: NUMA und processor binding ...

Ausgebremste Software schreiben ist garnichtmal so schwer. ;-)

> Und am oberen Ende wüsste ich gerne, ob du glaubhaft versichern kannst,
> dass du alle Aspekte von Haswells TSX Befehlen relativ leicht findest.
> ;-)

Ich versichere hiermit glaubhaft ... ;-)
Nein, sicher nicht. Ich schlage mich (beruflich) eher auf der 
Applikationsseite mit Multithreading rum. Viele der sonstigen lustigen 
Sachen muss ich mir in meiner Freizeit zu Gemüte führen. Und Freizeit 
ist ein ziemlich begrenztes Gut, leider, und es gibt noch andere Themen 
und Menschen, die Aufmerksamkeit (er)fordern.

von (prx) A. K. (prx)


Lesenswert?

Konrad S. schrieb:
> Nein, sicher nicht. Ich schlage mich (beruflich) eher auf der
> Applikationsseite mit Multithreading rum.

Also genau da, wo TSX interessant ist. Es sei denn du meinst mit der 
Applikationsseite, dass du Zahlen in Excel eintippst statt Excel zu 
programmieren. ;-)

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

A. K. schrieb:
> Also genau da, wo TSX interessant ist.

Ich bin in der glücklichen Situation, dass meine Threads fast nicht 
gegeneinander kämpfen müssen. Die Threads sehen sich kurz beim 
Initialisieren von Ressourcen bzw. beim Aufteilen der Daten, dann 
rechnet jeder Thread für lange Zeit sein Zeug. Wesentlich wichtiger ist, 
dass auf allen Platformen (Windows, MacOS, Linux, Unixe / x86, SPARC, 
HP-RISC, PowerPC) die vorhandene CPU-Leistung auch tatsächlich 
ausgenutzt wird.

> Es sei denn du meinst mit der
> Applikationsseite, dass du Zahlen in Excel eintippst statt Excel zu
> programmieren. ;-)

Und Multithreading ist, wenn man gleichzeitig Kaffee trinkt. ;-)

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.