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
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?
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.
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).
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.
> 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.
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. ;-)
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.
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
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.
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?
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.
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.
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.
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.
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
voidEepErase(void){
2
NVMCON=0x4050;// Set up NVMCON to bulk erase the data EEPROM
3
asmvolatile("disi #5");// Disable Interrupts For 5 Instructions
4
__builtin_write_NVM();// Issue Unlock Sequence and Start Erase Cycle
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)
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.
>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 TemporarilyPIC24 kann oft darauf verzichten, da der versch. CISC-Befehle hat.
(sofern 8kB-Bereich)
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.
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
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.
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.