Forum: Mikrocontroller und Digitale Elektronik PIC32 ASM atomic Funktionen


von Stephan (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

ich brauche für ein Projekt ein paar Funktionen die mir Flags setzen und 
löschen können sollen und das auch bei globalen Variablen (INT-Vars).

Bis jetzt hatte ich einfach die INTs zwischen drin deaktiviert und die 
Sachen bearbeitet.
Aber auf dauer ist das sehr umständlich.

So, jetzt habe ich mir ein paar Funktionen geschrieben die ohne 
deaktivierung der INTs auskommen sollen.
Könntet Ihre euch die mal ansehen ob das so in Ordnung sind oder ob ich 
da noch Fehler drin habe.

Optimierungsvorschläge sind auch willkommen. ;-)

PS: ist mein erstes mal mit dem PIC ASM also bitte etwas nachsicht.

Stephan

von Bronco (Gast)


Lesenswert?

Stephan schrieb:
> So, jetzt habe ich mir ein paar Funktionen geschrieben die ohne
> deaktivierung der INTs auskommen sollen.

Da ich mich in PIC32 Assembler nicht auskenne:
Wodurch erreichst Du die Atomarität bei den Read-Modify-Write-Zugriffen 
ohne die Interrupts zu sperren?

von Stephan (Gast)


Lesenswert?

durch die Befehle 'LL' und 'SC'.

LL - merkt sich beim lesen die Speicherstelle und wenn der Wert wieder 
geschrieben wird, durch SC, funktioniert das nur, wenn die 
Speicherstelle nicht verändert worden ist.

von Stephan (Gast)


Lesenswert?

keiner was zu sagen?

von Peter D. (peda)


Lesenswert?

Ohne besondere Maßnahmen (Interruptsperre) ist alles, was länger als ein 
Assembler-Befehl ist, nicht atomar.
Ob man es in C oder Assembler schreibt, ist egal.

Bei RISC-CPUs würde ich mir deshalb eine Art Präfixbefehl wünschen: "DI 
5" (Führe die folgenden 5 Befehle unter Interruptsperre aus).

von (prx) A. K. (prx)


Lesenswert?

Peter Dannegger schrieb:
> Ohne besondere Maßnahmen (Interruptsperre) ist alles, was länger als ein
> Assembler-Befehl ist, nicht atomar.

Die für einige RISCs typische LL/SC Methodik ist zwar nicht atomar, muss 
es aber auch nicht sein. Der Trick besteht darin, dass die Kollision mit 
Interrupts oder anderen Busmastern erkannt wird und zur Neuversuch 
führt.

von Dieter W. (dds5)


Lesenswert?

> Bei RISC-CPUs würde ich mir deshalb eine Art Präfixbefehl wünschen:
> "DI 5" (Führe die folgenden 5 Befehle unter Interruptsperre aus).


PIC24 und dsPIC33 haben sowas:

DISI Disable Interrupts Temporarily
Syntax: {label:} DISI #lit14
Operands: lit14  [0 ... 16383]
Operation: lit14 → DISICNT
1 → DISI
Disable interrupts for (lit14 + 1) cycles
Status Affected: None

PIC32 kenn ich (noch) nicht.

von (prx) A. K. (prx)


Lesenswert?

Die Vorteil von LL/SC gegenüber Interrupt-Abschaltung:
- Die Reaktionszeit von Interrupts wird nicht verschlechtert.
- Es funktioniert auch zwischen verschiedenen Cores.

Der Nachteil: Man muss es verstanden haben. ;-)

von (prx) A. K. (prx)


Lesenswert?

Stephan schrieb:
> Könntet Ihre euch die mal ansehen ob das so in Ordnung sind oder ob ich
> da noch Fehler drin habe.

Bin mit MIPs nicht vertraut. Üblicherweise gibts aber Beispielcode, 
nicht zuletzt vom Hersteller (Microchip oder MIPS).

Da Microchips Compiler aber GCC ist scheint mir der Rückgriff auf 
Assembler unnötig. Oft gibts für solche Spezialbefehle irgendwo inline 
Pseudo-Funktionen, so dass man in C bleiben kann.

von Reinhard Kern (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ohne besondere Maßnahmen (Interruptsperre) ist alles, was länger als ein
> Assembler-Befehl ist, nicht atomar.

Aber bei manchen Prozessoren gibt es geeignete Befehle in der Art Test 
and Set, die ein Bit atomar lesen und dann setzen, damit lassen sich 
einige Aufgaben lösen. Ob es sowas bei PIC32 gibt weiss ich nicht.

Beim guten alten Z80 konnte man mit einem Befehl ein Byte im Speicher 
decrementieren und dabei die Flags setzen. Kann man für Semaphore usw. 
brauchen.

Gruss Reinhard

von (prx) A. K. (prx)


Lesenswert?

Zum Code: Der Abstand zwischen LL und SC sollte kurz gehalten werden. 
Ich könnte mir vorstellen, dass fast alle Befehle zwischendrin auch vor 
LL platziert werden knnen.

von Peter D. (peda)


Lesenswert?

A. K. schrieb:
> Der Trick besteht darin, dass die Kollision mit
> Interrupts oder anderen Busmastern erkannt wird und zur Neuversuch
> führt.

Klingt sehr kompliziert.
Heißt das, die CPU merkt sich die LL-Zeile und wenn vor SC ein Interrupt 
erfolgt, führt sie allen Code ab LL nochmal aus?

Heißt das auch, LL/SC müssen genau paarweise erfolgen?
Und sind mehrere LL/SC geschachtelt möglich?

von (prx) A. K. (prx)


Lesenswert?

Reinhard Kern schrieb:
> Aber bei manchen Prozessoren gibt es geeignete Befehle in der Art Test
> and Set, die ein Bit atomar lesen und dann setzen, damit lassen sich
> einige Aufgaben lösen. Ob es sowas bei PIC32 gibt weiss ich nicht.

Da es LL/SC gibt offensichtlich nicht. TAS oder LOCK XCHG (x86) sind 
aufgrund ihrer Ineffizienz etwas aus der Mode gekommen. Solche LL/SC 
sind wesentlich effizienter.

von (prx) A. K. (prx)


Lesenswert?

Peter Dannegger schrieb:
> Heißt das, die CPU merkt sich die LL-Zeile und wenn vor SC ein Interrupt
> erfolgt, führt sie allen Code ab LL nochmal aus?

Ich beschreibe es mal ungefähr so, wie ich es von PowerPC und neueren 
ARMs her kenne: Die CPU läd in LL ein Wort aus dem Speicher und merkt 
sich die Adresse des Wortes in einem speziellen Register. Wenn dieser 
Wert bei SC noch übereinstimmt, dann wird ein Wort dort abgespeichert, 
sonst nicht. Zusätzlich wird dieser Status als Ergebnis von SC irgendwo 
abgelegt.

In Interrupts kann nun dieses Register gelöscht werden, wenn sinnvoll. 
Bei externen Buszyklen (anderer Core, anderer Busmaster), die auf 
ebendiese Adresse zugreifen, geschieht es automatisch.

Wenn also der Code beim SC Befehl merkt, dass etwas dazwischen kam, dann 
probiert der Code den Kram einfach nochmal. Also nicht der Processor, 
sondern das Programm. Siehe oben, im direkt auf SC folgenden bedingten 
Sprung.

Vorteil: Diese Befehle sind sehr einfache Befehle, die im Normalfall 
schnell durchlaufen und keine besonderen und teuren Buszyklen erfordern.

> Heißt das auch, LL/SC müssen genau paarweise erfolgen?

So ist es gedacht. Man sollte sich das ggf. im Handbuch genau nachlesen, 
oft gibt es bestimmte Voraussetzungen und Randbedingungen.

> Und sind mehrere LL/SC geschachtelt möglich?

Nein. Wüsste auch nicht, was das hier brächte.

Wenn dir das zu einfach ist und du es gerne etwas komplexer hast, dann 
schau dir mal Intels Befehlserweiterung in Haswell an, der Support für 
Transactional Memory. David Kanter hat das auf 
http://www.realworldtech.com/ gut beschrieben.

von Peter D. (peda)


Lesenswert?

Danke für die Erklärung.

Ein DISI x fände ich allerdings einfacher.
Und da Interrupts eh immer ein Gedöns an Prolog/Epilog haben, sind mal 5 
Zyklen Sperre nicht von Bedeutung.

von (prx) A. K. (prx)


Lesenswert?

Peter Dannegger schrieb:
> Ein DISI x fände ich allerdings einfacher.

Ist in Assembler recht nett.

Für die heute dominierende Umsetzung in Hochsprache ist es allerdings 
ziemlich unpraktisch, setzt es doch eine entsprechende Compilermimik 
voraus. Sowas wie
  __run_with_irqs_disabled { code ... }

Zilog hat in den eZ8 auch sowas in Art drin - aber dank geradezu 
klassischer Konstrukteur/Entwickler-Schere keinen entsprechenden Support 
im eigenen Compiler.

von G++ (Gast)


Lesenswert?

Gibt's doch beim AVR-GCC. Nennt sich ATOMIC_BLOCK!

von (prx) A. K. (prx)


Lesenswert?

G++ schrieb:
> Gibt's doch beim AVR-GCC. Nennt sich ATOMIC_BLOCK!

Und nun mach das mal für DISI.

von G++ (Gast)


Lesenswert?

Oder ist das Bestandteil der AVRlibc oder so?

von (prx) A. K. (prx)


Lesenswert?

G++ schrieb:
> Oder ist das Bestandteil der AVRlibc oder so?

So ist es. Sieht dort nicht genauso aus, aber die Sequenz
1
a;
2
{ ... }
3
b;
lässt sich beispielsweise ungefähr als Makro
1
for (a; irgendwas; b) { ... }
verstecken.

Aber mach das mal, wenn du in "a" die Anzahl Maschinenbefehle im 
Codeblock angeben musst.

von G++ (Gast)


Lesenswert?

Ich bin halt ein AVR-Fanboy. Warum soll ich da die Probleme der PIC's 
lösen ;-)

von Klaus (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ein DISI x fände ich allerdings einfacher.
> Und da Interrupts eh immer ein Gedöns an Prolog/Epilog haben, sind mal 5
> Zyklen Sperre nicht von Bedeutu
> Ein DISI x fände ich allerdings einfacher.
> Und da Interrupts eh immer ein Gedöns an Prolog/Epilog haben, sind mal 5
> Zyklen Sperre nicht von Bedeutung.ng.

Code und Kommentar aus einem Manual für PIC24 kopiert, weiß nicht mehr 
aus welchem (benutze nur C):
1
void EepErase(void) {
2
    NVMCON = 0x4050;            // Set up NVMCON to bulk erase the data EEPROM
3
    asm volatile ("disi #5");   // Disable Interrupts For 5 Instructions
4
    __builtin_write_NVM();      // Issue Unlock Sequence and Start Erase Cycle
5
    while(_WR)
6
        ;
7
}

MfG Klaus

von Peter C. (peter_c49)


Lesenswert?

yup, dass gibt es mit gcc schon frei haus fuer mips/arm und andere,
auch im gcc des xc32 scheint es enabled zu sein. also auch ohne 
assembler oder _ASM_ in c/c++ nutzbar.

siehe gcc doc: Atomic-Builtins

37:                  result = __sync_add_and_fetch(&safecounter, 1);

9D001054  0000000F   SYNC
9D001058  C3828014   LL V0, -32748(GP)
9D00105C  24410001   ADDIU AT, V0, 1
9D001060  E3818014   SC AT, -32748(GP)
9D001064  1020FFFC   BEQ AT, ZERO, 0x9D001058
9D001068  24420001   ADDIU V0, V0, 1
9D00106C  0000000F   SYNC
9D001070  AF828018   SW V0, -32744(GP)

von Stephan (Gast)


Lesenswert?

Morgen

A. K. schrieb:
> Der Abstand zwischen LL und SC sollte kurz gehalten werden.

Das werde ich noch ändern.
Danke.

von Bronco (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ein DISI x fände ich allerdings einfacher.
> Und da Interrupts eh immer ein Gedöns an Prolog/Epilog haben, sind mal 5
> Zyklen Sperre nicht von Bedeutung.

Wenn ich es richtig verstehe, funktioniert LL/SC nicht nur für 
Interrupts, sondern auch für mehrere Busmaster. Letzteres kann DISI 
nicht abfangen.

von MCUA (Gast)


Lesenswert?

>LL - merkt sich beim lesen die Speicherstelle und wenn der Wert wieder
>geschrieben wird, durch SC, funktioniert das nur, wenn die
>Speicherstelle nicht verändert worden ist.
LL-SC ist als atomic Ersatz witzlos, da im Falle des veränderten 
Mem-Wertes doch wieder zusätzlicher Code (mit zus. Takten) erforderlich 
ist.


>Aber bei manchen Prozessoren gibt es geeignete Befehle in der Art Test
>and Set, die ein Bit atomar lesen und dann setzen,...
sind schon CISC-Befehle (RMW), die typ. RISCs nicht haben.

>PIC24 und dsPIC33 haben sowas:
>DISI Disable Interrupts Temporarily
PIC24 kann oft darauf verzichten, da der versch. CISC-Befehle hat.
(sofern 8kB-Bereich)

von (prx) A. K. (prx)


Lesenswert?

MCUA schrieb:
> LL-SC ist als atomic Ersatz witzlos, da im Falle des veränderten
> Mem-Wertes doch wieder zusätzlicher Code (mit zus. Takten) erforderlich
> ist.

Warum es deshalb witzlos?

Es ist übrigens kein zusätzlicher Code, sondern der gleich wie beim 
vorherigen Versucht, als Schleife. Voraussetzung ist natürlich, dass man 
mit einer nichtdeterministischen Laufzeit der Sequenz leben kann.

Aber wie ich oben schon andeutete: Diese Methode adressierte 
ursprünglich Multiprocessing. Die Verwendbarkeit auf Singlecores als 
Ersatz atomarer Operationen ist eher ein Nebenffekt. Wer damit nichts 
anfangen kann, dem bleibt es unbenommen, in traditioneller Weise die 
Interrupts abzuschalten.

von Reinhard Kern (Gast)


Lesenswert?

A. K. schrieb:
> in traditioneller Weise die
> Interrupts abzuschalten.

Ich sehe auch nicht, wieso eigentlich ein Pärchen DI..EI so unzumutbar 
viel Aufwand sein soll und die Performance beeinträchtigt. Das Problem 
ist höchstens, dass man es vergisst, aber das gilt für alles andere 
auch, des Problems atomarer Operationen muss man sich bewusst sein, oder 
man kann eben nicht programmieren. Natürlich darf man zwischen DI und EI 
nur das absolut notwendige durchführen und nicht eine 
Differentialgleichung lösen.

Gruss Reinhard

von (prx) A. K. (prx)


Lesenswert?

Reinhard Kern schrieb:
> Ich sehe auch nicht, wieso eigentlich ein Pärchen DI..EI so unzumutbar
> viel Aufwand sein soll und die Performance beeinträchtigt.

Tut es auch nicht, die paar Takte abgeschalteten Interrupts sind 
meistens kein Problem. Es gibt eben beide Wege. Jeder Weg hat seine 
Vorteile. Der hier betrachtete ist freilich vielen Leuten unbekannt, was 
leicht zu dem "kenn ich nicht, also mag ich es nicht" Effekt führt.

Ein bedeutender Unterschied entsteht freilich dann, wenn man nicht mehr 
mit universellen Rechten programmiert. Denn wenn es überhaupt 
Privilegstufen gibt (z.B. ARM7, Cortex-M), dann sind DI/EI privilegiert. 
LL/SC artige Operationen stehen jedoch auch nichtpriviligierten 
Programmen zur Verfügung.

Dass die Cortex-M3/4 und PIC32 Microcontroller solche Operationen 
überhaupt haben liegt daran, dass diese Cores universell gebaut sind und 
ggf. auch in Mehrprozessorsystemen einsetzbar sein sollen.

von Stephan (Gast)


Lesenswert?

A. K. schrieb:
> Der hier betrachtete ist freilich vielen Leuten unbekannt, was
> leicht zu dem "kenn ich nicht, also mag ich es nicht" Effekt führt.
Ja da kenne ich genügend Leute..... ;-)

> Ein bedeutender Unterschied entsteht freilich dann, wenn man nicht mehr
> mit universellen Rechten programmiert. Denn wenn es überhaupt
> Privilegstufen gibt (z.B. ARM7, Cortex-M), dann sind DI/EI privilegiert.
> LL/SC artige Operationen stehen jedoch auch nichtpriviligierten
> Programmen zur Verfügung.
das ist gut. kannte ich auch noch nicht.

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.