Compare Skip if Equal Der Befehl muss wohl wichtig sein, sonst wäre er nicht in einem reduzierten Befehlssatz zu finden. Da ich ihn aber nur selten benutze, habe ich wahrscheinlich etwas nicht verstanden. Wer hilft mir auf die Sprünge?
Hi, kein Woerterbuch zu Hause oder wo ist das Problem? Compare Skip if Equal Vergleiche überspringe wenn gleich. Gruß, Dirk
Der Befehl vergleicht zwei Register, und wenn beide den selben Wert haben, wird der nächste Befehl übersprungen...
> Da ich ihn aber nur selten benutze, habe ich wahrscheinlich etwas > nicht verstanden. Na, wenn du ihn schon benutzt, dann weisst du doch wozu er gut ist?!? Wo ist dein Problem?
@Mathias, naja, nicht so ganz. Das Equivalent zu
1 | if (A != B) C = C - 5; |
2 | D = C |
wäre
1 | cpse A, B |
2 | subi C, 5 |
3 | out D, C |
PS: Der Compiler erzeugt natürlich was anderes. Ist jetzt nur so ein Gedankenkonstrukt.
Ich vermutete eine herausragende Bedeutung des Befehls, zumal ja auch im Gegensatz zu den anderen Compares die Flags nicht geändert werden. Normalerweise hat in einem RISC-Befehlssatz jeder Befehl seinen gewichtigen Grund.
Naja, man kann ja mal überlegen, wie oft so ein Vergleich vorkommt. Und wenn man den mit einem Befehl erschlagen kann, ist das sicher wünschenswert. Zumal die AVRs extra für Hochsprachen-Programmierung optimiert sind. Simons Beispiel mit dem if ist ein Konstrukt, das sehr häufig ist und bei dem es sinnvoll sein kann, es möglichst effizient zu implementieren
Mir ist bislang auch noch kein Beipiel eingefallen, in dem CPSE mehr einspart als einen nicht ausgeführten bedingten Sprung, also 1 Takt. Diese Einsparung kommt in Schleifen wie in memchr() zum Tragen, die zwei Abbruchbedingungen enthalten (http://www.mikrocontroller.net/forum/read-1-167735.html#167786, http://www.mikrocontroller.net/forum/read-1-182869.html#182875). Vielleicht gibt es ja einen wichtigen Controller-Benchmark, der sich damit ein bischen beschleunigen liess. Sonderlich teuer ist die Implementierung nicht.
Teuer wohl nicht, aber man hätte mit dem Bitmuster etwas anderes machen können, z.B. stolpere ich regelmäßig darüber, dass LDI und CPI nur mit r16..r31 arbeiten, während dieses CPSE alle Register zuläßt.
ALLE Register register befhle könne alle register ansprechen (wäre ja sonst blöd) Imidiate Befehle gehen nur auf den unteren registern, weil sont der imidiate Wert nichtmehr reinpasst! CPSE ist ein sehr wichtiger Befhel weil er ATOMAR arbeitet, das heißt folgendes: cpi temp, 100 brne woandershin kann in der mitte unterbrochen werden durch einen Interupt!! CPSE aber nicht weils ein Befehl ist! Dies sit besonders wichtig bei geschared recourcen, wo eine bestimmte Programmsektion nur betreten werden darf wenn ein bestimmter Wert (meistens 0) in einem register gibt (Stichwort counting semaphore im zusammenhang mit Reader Writer Problem und reader preference), bei den meisten Prozessoren gibt es einen SKIP IF ZERO Befehl für genau diesen einsatzzweck! Der Befehl ist deshalb eigentlich sehr wichtig, damit man geschüzte Bereiche erstellen kann ohne bei jedem test die golbalen Interupt komplett ausschalten zu müssen. Der Befehl ist von daher sogar sehr wichtig auch wenn man (meist)
> Teuer wohl nicht, aber man hätte mit dem Bitmuster etwas anderes > machen können, z.B. stolpere ich regelmäßig darüber, dass LDI und > CPI nur mit r16..r31 arbeiten, während dieses CPSE alle Register > zuläßt. Um hier noch r0 bis r15 zu unterstützen, bräuchte man aber viiiiiiiel mehr Platz im Befehlsraum. Du mußt dir überlegen, daß ein Befehl 16 bit breit ist. LDI und CPI bekommen jeweils einen Immediate-Wert mit, für den schon mal 8 Bit, also die Hälfte drauf geht. Bei 32 Registern bräuchte die Nummer des Registers nochmals 5 Bits. Dann wären gerade mal drei Bits für den Opcode übrig. Bei 16 Bits gibt es insgesamt 65536 mögliche Bitkombinationen (die nenne ich mal den Befehlsraum). Ein LDI, das alle Register ansprechen könnte, bräuchte davon alleine für sich schon ein Achtel. CPI nochmals. Dann gibt's ja auch noch SUBI, SBCI, ANDI und ORI. Damit würden diese 6 Befehle drei Viertel des gesamten Befehlsraumes brauchen. Es wäre einfach kein Platz mehr für all die anderen Befehle, die noch gebraucht werden. Deshalb hat man die Zahl der Register, mit denen sie benutzt werden, halbiert und damit ein Bit mehr für den Opcode rausbekommen. Die Befehle brauchen so nur noch drei Achtel des Befehlsraums. CPSE bekommt nur zwei Register, von denen für jedes fünf Bits benötigt werden. Es sind also noch sechs Bits für den Opcode übrig. Damit belegt dieser Befehl nur 1/64 des Befehlsraums und ist verglichen mit einem LDI deutlich leichtgewichtiger.
"weil er ATOMAR arbeitet" Jo, aber erklär mir, was es bringt, den Compare und seinen Sprung ununterbrechbar zu machen. Kann ich grad nicht nachvollziehen. Ergibt für mich erst einen Sinn, wenn auch der auf CPSE folgende Befehl noch garantiert ununterbrechbar mitläuft. Sowas wie: cpse r1,r2 inc r1 So koppeln denn auch alle Sema-Operationen, denen ich bislang begegnet bin, Test und Modifikation (test-and-set, compare-and-add, usw), oder sie ermöglichen es, das iterativ abzuwickeln (load-and-reserve/store-conditional).
Auch die sogenannten Test-and-Set-Befehle arbeiten atomar. Stell dir vor, du überprüfst eine Variable auf ihren Wert. Jetzt kommt vor dem Sprungbefehl ein Interrupt (oder machen wir es einfacher, es findet ein Threadwechsel statt). Dieser überprüft die Variable, verändert sie und gibt den Prozessor wieder ab. Jetzt hat dein alter Thread das Problem, dass die Variable verändert wurde, er das aber nicht merkt und nach seiner vorherigen Überprüfung weitergeht. Kino, Sitzplatzreservierung. Der erste Thread überprüft, ob der Sitz noch frei ist. Vor dem Sprung kommt der andere Thread, überprüft zufälligerweise den selben Sitz, reserviert ihn und gibt die Kontrolle ab. Nun reserviert der alte Thread den Sitz ebenfalls, da er nicht weiß, dass der andere das schon getan hat.
Hmmm, wie immer gilt, erst denken, dann schreiben. @A.K.: Jetzt ist klar, was du meinst. Ist schon spät und mein Kopf brummt schon wegen dieser dooofen Betriebssystem-Klausur.
War das jetzt einen Antwort an mich? Die Verwendung der test-and-set Varianten ist mir durchaus bekannt. Neu ist mir freilich, dass man mit compare-and-branch das gleiche erreichen kann. Beispiel bitte.
"Atomar" ist beim Vergleich völlig schnurz. Atomar hat nur Bedeutung bei Read-Modify-Write Zugriffen. Ob CP oder CPSE ist also völlig egal, da ja nichts zurück geschrieben wird. Und außerdem sind Zugriffe auf mehrere Bytes (16 oder 32 Bit) auf 8-Bittern per default nicht atomar. Ich finde CPSE auch nicht sonderlich nützlich. Ein EORI wäre wesentlich sinnvoller gewesen. So muß man jedesmal erst ein Register laden, um Bits zu kippen. Das mit den unteren Registern hätte man einfach dadurch lösen können, daß diese dann mit 2-Wort-Befehlen auch Konstanten verarbeiten können. Peter
Vor hundert Jahren galt das Atom noch als kleinste unteilbare Einheit. Daraus leitet sich der Begriff "atomar" für unteilbare Dinge ab. Das ergibt zwar seit Meissner/Hahn vor ~70 Jahren keinen Sinn mehr, aber da war's schon zu spät und die Bedeutung steckte in der Sprache drin. Selbst in einer Wissenschaft, die deutlich jünger ist. Heisst in diesem Zusammenhang also, dass die einzelnen Teiloperationen einer atomaren Befehlssequenz nicht von Interrupts unterbrochen werden können. Je nach System kann sich das auch auf nicht einem anderen Busmaster unterbrechbare Busoperationen beziehen.
> Vor hundert Jahren galt das Atom noch als kleinste unteilbare > Einheit. Daraus leitet sich der Begriff "atomar" für unteilbare > Dinge ab. Eigentlich ist es genau umgekehrt. Das Atom wurde nach dem griechischen Wort "atomos" (=unteilbar) benannt. Der Begriff "atomar" ist also eigentlich korrekt. Es ist vielmehr das Atom, das seinen Namen zu unrecht hat.
@A.K. >Heisst in diesem Zusammenhang also, dass die einzelnen >Teiloperationen einer atomaren Befehlssequenz nicht von Interrupts >unterbrochen werden können. Je nach System kann sich das auch auf >nicht einem anderen Busmaster unterbrechbare Busoperationen beziehen. Gut erklärt :) Ich vermute, dass folgender Befehl auch atomar ist? add R1, R2 ; zwei Register addieren Welcher Befehl wäre für einen ATmegaxx nicht atomar? Bernhard
einzelne Befehle sind immer atomar, denn während eines Befehlsablauf, wird der Controller nie in einen Interrupt springen. Höchstens zwischen zwei Befehlen. Ein Beispiel für eine nicht atomare sache ist ein ganz normaler Branch Befehl (Beispiel von Läubi):
1 | cpi temp, 100 |
2 | brne woandershin |
Dieses Konstrukt ist nicht atomar, da es unterbrochen werden kann. PS: man kann die ganze Suppe aber doch wieder mit cli/sei atomar machen. Ist dann halt ein atomarer Block, der mehrere Taktzyklen dauert, aber ist ja egal.
"Höchstens zwischen zwei Befehlen." Nö. Es gibt etliche Architekturen, in denen innerhalb eines Einzelbefehls unterbrochen werden kann. Das betrifft einerseits regelmässig Blocktransferbefehle (z.B. Z80, x86), da die Interrupt-Latenz sonst exorbitante Werte annehmen würde. Die können zwischen den Interationen unterbrochen werden und setzen danach automatisch wieder an der richtigen auf. Es gibt aber auch Implementierungen, bei denen ein ganz normal wirkender Befehl unterbrochen werden kann. Mir bekannt bei diversen Modelle der 68000-Reihe, ab 68010. Bei Speicherzugriffsproblemen wird dort der Ablauf vom Microcode unterbrochen und ggf. wieder aufgesetzt, d.h. da wird mitten drin im Befehlsablauf unterbrochen. Die eleganteste Variante atomarer Befehle findet sich in eZ8: Da gibt es einen Befehl, der die folgenden 3 Befehle zu einer atomaren Sequenz zusammenfasst.
"Ein Beispiel für eine nicht atomare sache ist ein ganz normaler Branch Befehl (Beispiel von Läubi):" Wie schon gesagt, das ist Quatsch mit Soße. Wenn zwischen CPI und BRNE ein Interrupt kommt, passiert genau absolut garnichts, d.h. der Sprung wird überhaupt nicht beeinflußt. Mit "nicht atomar" meint man nur Sachen, bei denen es auch darauf ankommt und die man dann durch Klammerung mit cli+sei atomar machen muß. Ein Beispiel ist das Lesen von 16Bit-Werten: [ARVASM] lds r16, myval_lo lds r17, myval_hi [/AVRASM] Wenn nun ein Interrupt dazwischenhaut und my_val ändert, dann hat man ein altes und ein neues Byte und der Wert ist falsch. Z.B. der Interrupt zählt myval hoch: ... 0x01FE 0x01FF 0x02FF <- falsch !!! 0x0200 0x0201 ... Peter
> ...d.h. der Sprung wird überhaupt nicht beeinflußt...
Er wird nur dann nicht beeinflusst, wenn das SREG im Interrupt Handler
ordnungsgemäß gesichert und am Ende wieder geladen wird. Sonst gehts
schief...
Genau, so wie johnny.m es sagt, habe ich auch gedacht. PS: Sogesehen hast du dann natürlich völlig Recht, Peter!
Hmn, aber denoch ist die Zeit in Takten die zwischen
1 | cpi temp, 100 |
2 | brne woandershin |
vergeht auf Grund dessen das sie nicht mehr atomar sind nicht mehr vorhersagbar wenn man im Interrupts arbeitet. Atomar macht für mich als Ausssage doch nur Sinn wenn ich bei der Manipulation von Thread/IRQ übergreifenden Daten, also gemeinsam benutzt, ohne ein CLI/STI auskommen möchte. Gruß Hagen
Wer in Interrupts das SREG nicht sichert, wird kaum lauffähige Programme heraus bekommen. Und wenn doch, dann sind diese nur schwer wartbar und erweiterbar. Gewisse Grundlagen sollte man schon als selbsverständlich voraussetzen können. Warum die AVR-Entwickler das SREG nicht automatisch sichern, ist mir völlig schleierhaft. Es gibt Architekturen, die können das. Peter
>Wer in Interrupts das SREG nicht sichert, wird kaum lauffähige >Programme heraus bekommen. So'n Quatsch. Dann müssten die meisten meiner Programme nicht laufen. Tun sie aber und ganz prächtig sogar. Wenn es nicht nötig ist, warum soll man es dann tun? >Und wenn doch, dann sind diese nur schwer wartbar und erweiterbar. Warten muss man Programme nur, wenn sie nicht funktionieren. Und wieso soll man sie nicht erweitern können? >Warum die AVR-Entwickler das SREG nicht automatisch sichern, ist mir >völlig schleierhaft. Mir nicht. Es nicht unbedingt nötig. Und wenn es automatisch gesichert würde, würden die Interruptroutinen mehr Takte benötigen. Und wenn Du es sichern willst, kannst Du das ja tun. >Es gibt Architekturen, die können das. Freilich gibt es die. Es gibt auch Controller, die haben eine Gleitkommaeinheit eingebaut.
@Xenu: Wenn Deine Programme alle funktionieren, ohne dass Du in Interrupt-Handlern das SREG sicherst, dann arbeitest Du entweder ohne Verzweigungsbefehle oder es ist Zufall (Ich vermute eher das zweite)! Wenn Du natürlich exzessiv mit cli/sei arbeitest, dann OK, aber der Code-Aufwand dürfte in den meisten Fällen ungleich höher sein. Klar, die Wahrscheinlichkeit, dass ein Interrupt bei einer (z.B.) cpX-brYY-Kombination dazwischenhaut ist nicht sehr hoch und dass in der ISR dann auch noch gerade die benötigten Flags verändert werden, auch nicht, aber über längere Zeit gesehen ist es sicher nicht mehr zuverlässig!
>>Wer in Interrupts das SREG nicht sichert, wird kaum lauffähige >>Programme heraus bekommen. > > So'n Quatsch. Dann müssten die meisten meiner Programme nicht > laufen. > Tun sie aber und ganz prächtig sogar. > Wenn es nicht nötig ist, warum soll man es dann tun? Wenn es nicht nötig ist (also die Interrupt-Routine das SREG nicht verändert), hat man ja auch keine Probleme, wenn man es nicht sichert. Das kommt zumindest bei mir aber sehr selten vor. >>Und wenn doch, dann sind diese nur schwer wartbar und erweiterbar. > > Warten muss man Programme nur, wenn sie nicht funktionieren. Oder wenn man sie ändern oder Teile davon in einem neuen Programm weiterverwenden möchte. >Warum die AVR-Entwickler das SREG nicht automatisch sichern, ist mir >völlig schleierhaft. > Mir nicht. Es nicht unbedingt nötig. Und wenn es automatisch > gesichert würde, würden die Interruptroutinen mehr Takte > benötigen. > Und wenn Du es sichern willst, kannst Du das ja tun. Allerdings braucht das noch mehr Takte, da man das SREG nicht direkt, sondern nur über den Umweg eines Registers auf den Stack bekommt.
Xenu hat recht, es gibt doch Befehle, die das SREG nicht beeinflussen. Wenn man nur solche in der Interruptroutine hat, brauch man das SREG doch auch nicht sichern....
Verzweigungsbefehle sind das eine, dazu kommen auch noch Arithmetik/Logik aller Art, wenn sie über mehr als ein Byte gehen, dazu werden auch die Flags gebraucht. Bei mir gibt es kaum Interruptprogramme, in denen das SREG nicht verändert wird, allzuviele sinnvolle Anwendungen kann ich mir auch gar nicht vorstellen. Deshalb sichere ich es erst mal grundsätzlich, sollte dann irgendwas besonders zeitkritisch werden, kann man es evtl. ja noch rausnehmen.
>Ach, Du arbeitest ohne Verzweigungsbefehle?
Hehe. Freilich benutze ich die auch.
Aber wenn ich zum Beispiel eine ADC-Interruptroutine habe,
die so wie unten ausschaut, würde es mich schon interessieren, wozu ich
dann das SREG speichern soll:
adc_finish: in adc_data, ADCH
out UDR, adc_data
reti
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.