ISR und atomarer Zugriff auf 16bit Variable

Gast #4400270
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
Gast #4400281
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
Persönliche Seite #4400304
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.
Gast #4400317
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!
Gast #4400325
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.
Gast #4400404
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.
Gast #4400419
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.
Gast #4400434
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
Gast #4400480
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
Gast #4400492
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 .....
Gast #4400616
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.
#4400653
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. ;-)
Gast #4400792
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
Gast #4400813
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).
Gast #4400932
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?
#4400965
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.
#4400983
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).
Gast #4401984
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.
Gast #4401988
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?
#4401994
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.
Gast #4401999
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.
#4402009
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.
Gast #4402011
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.
Gast #4402017
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.
#4402028
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.
#4402086
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).
#4402095
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.
Gast #4402099
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.
#4402101
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.
Gast #4402103
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.
#4402108
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.
#4402117
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.
Gast #4402119
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.
#4402126
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.
Gast #4402135
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.
Gast #4402145
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);
#4402161
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.
#4402167
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.
Gast #4402220
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
#4402226
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.
#4402237
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.
#4402306
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.
#4402317
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. ;-)
#4402348
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.
#4402468
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. ;-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren