Forum: Compiler & IDEs Compiler optimiert einfach Bitmanipulation nicht


von Philipp (Gast)


Lesenswert?

Hallo,

ich habe mir interessehalber mal den Assembler Code angeschaut, den ich 
vom AVR Studio 6.2 mit AVR GCC 4.8.1 produziert bekomme.

Dabei ist mir aufgefallen, dass einfache Bitmanipulationen, bei denen 
zwei Bits im gleichen Register gesetzt oder gelöscht werden, nicht 
optimiert werden.

Aus folgendem C-Code:
1
TIMSK |= (1<<TICIE1)|(1<<TOIE1);

Wird dann:
1
IN R24,0x39    In from I/O location 
2
ORI R24,0x24    Logical OR with immediate 
3
OUT 0x39,R24    Out to I/O location

Das braucht 3 Takte.

Warum wird das nicht zu
1
SBI 0x39,5    
2
SBI 0x39,3
Das würde nur 2 Takte brauchen und somit 1/3 der Rechenzeit einsparen!

Habe es auch schon auf Optimierungsstufe -O0 bis -O2 versucht, immer mit 
dem selben Ergebnis.

Kann mir jemand erklären, wie ich den GCC dazu bringen kann, das 
vernünftig zu Optimieren?

Philipp

von Malte S. (maltest)


Lesenswert?

Philipp schrieb:
> TIMSK |= (1<<TICIE1)|(1<<TOIE1);

Ist semantisch nicht gleich

TIMSK |= 1 << TICIE1;
TIMSK |= 1 << TOIE1;

Letzterem entspräche dein gewünschter Code.
Du hast mit  deiner Zeile aber explizit verlangt, dass beide Bits 
zeitgleich gesetzt werden, was je nach Register ein wesentlicher 
Unterschied sein kann. Daher darf der Compiler das nicht zu einem 
sequenziellen Setzen umwandeln.

Edit: bei einer nicht-volatile Variablen wäre das AFAIK eine mögliche 
Optimierung, aber Nagel mich nicht darauf fest.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

sbi kann nicht auf alle Register angewendet werden.
Wie war das noch mal? Alles was kleiner 32/0x20 ist?
Bei grossen Controllern fallen noch nicht mal alle
Portregister in diesen Bereich.

von Philipp (Gast)


Lesenswert?

Unglaublich!
Dann gibt es also keine andere Möglichkeit als das Ganze in zwei Zeilen 
zu schreiben?

SBI ist auf jeden Fall möglich, denn wenn ich nur ein Bit setzen/löschen 
will, benutzt er tatsächlich diesen Befehl.

von Malte S. (maltest)


Lesenswert?

Aber du hast ihm durch die Zuweisung zweier Bits in einem Ausdruck 
ausdrücklich verboten, daraus zwei Zuweisungen zu machen. Stichwort 
volatile.

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:
> Unglaublich!

Was ist daran unglaublich?

Der Compiler darf das gar nicht anders machen!

Überleg mal, wenn es tatsächlich wichtig ist, dass die beiden Bits 
gleichzeitig gesetzt werden MÜSSEN, weil das zb Portbits sind und da 
etwas lebenswichtiges drann hängt, wenn die nicht gleichzeitig schalten.

Und dann kommt nach dem ersten sbi ein Interrupt dazwischen ....
1
   sbi ...
2
                <------------- hier kommt ein Interrupt
3
   sbi ...
... und der Prozessor vertschüsst sich noch vor dem zweiten sbi in eine 
ISR?

Na. Dämmerts, warum der Compiler nicht eigenmächtig die angeordnete 
gleichzeitige Bitmanipulation einfach in 2 aufeinanderfolgende Schritte 
aufdröseln darf?

von Philipp (Gast)


Lesenswert?

Ok, überzeugt.
Dann werde ich wohl jedes bit mit einem eigenen Befehl setzen müssen.

Danke für die hilfreichen Antworten!

In der Zwischenzeit ist nochmal eine Ungereimtheit aufgetaucht, die zum 
Thema passt:
Ich verwende in meinem Code sehr oft die Variable Zahnzaehler. Deswegen 
habe ich diese mit der Zuweisung
1
register uint8_t  Zahnzaehler  asm ("r3");
an das Register r3 gebunden, damit sie nicht immer wieder erst aus dem 
Speicher  geladen werden muss.
An einer Stelle, wo ich die Variable nur um den Wert 1 Inkrementieren 
will, produziert mir der Compiler aus
1
 Zahnzaehler++
 diese drei Zeilen hier:
1
LDI R20,0x01    Load immediate 
2
ADD R20,R3    Add without carry 
3
MOV R3,R20    Copy register

An einer anderen Stelle im Code, an der genau der gleiche Befehl steht, 
macht er hingegen wie er soll
1
INC R3

An erstgenannter Stelle vergleiche ich die Variable direkt anschließend 
noch mit einer Konstante, könnte das hier ausschlaggebend sein? Hier zur 
Übersicht nochmal die ganze Stelle:
1
    Zahnzaehler++;    
2
LDI R20,0x01    Load immediate 
3
ADD R20,R3    Add without carry 
4
MOV R3,R20    Copy register 
5
    if ( Zahnzaehler == 15)  {
6
CPI R20,0x0F    Compare with immediate 
7
BRNE PC+0x05    Branch if not equal

Selbst wenn man für den Vergleich den Wert von Zahnzaehler in Register 
R20 bräuchte, wäre doch INC R3 gefolgt von MOV R20,R3 schneller.

Erwarte ich vielleicht einfach zu viel von einem Compiler?

von Peter II (Gast)


Lesenswert?

Philipp schrieb:
> register uint8_t  Zahnzaehler  asm ("r3");

kannst du überhaupt sicherstellen das r3 nicht in irgendeiner lib 
verwendet wird? Wenn nein, lasst das mit dem Register lieber bleiben.

> Erwarte ich vielleicht einfach zu viel von einem Compiler?
vermutlich, er optimiert ausreichend gut, das heißt aber nicht das es 
optimal ist.

Wenn dir der code zu langsam ist, dann schreibe die notwendigen 
Funktionen direkt in ASM. So legst dem Compiler nur steine in den weg.

von Malte S. (maltest)


Lesenswert?

Solange du nicht absolut sicher bist, dass r3 frei ist und dass du 
unbedingt dieses Register brauchst, solltest du dem Compiler da gar 
nicht erst reinpfuschen. register ohne Festlegung reicht auch. In aller 
Regel reicht es auch, das gar nicht zu machen, der Compiler wird 
normalerweise schon in Register legen, was dort sinnvoll aufgehoben ist.

Hast du ein akutes Performance-Problem? Dann suche zunächst nach 
größeren Schnitzern als einzelne Cycles zu tunen. Oder willst du nur 
allgemein verstehen, was da an Code generiert wird? Dann mach weiter 
aber bedenke, dass der Compiler einerseits nie so gut wie du wissen 
kann, was du willst. Andererseits ist er im Zweifelsfall besser als du 
daran, die entscheidenden Stellen zu optimieren.

von Philipp (Gast)


Lesenswert?

Dass R3 nicht anderweitig gebraucht wird, habe ich vorher überprüft, 
habe dazu den ASM Code nach R3 abgesucht, ist nirgendwo vorgekommen.

Dass der Compiler Variablen in Register legt, so sie frei sind, hätte 
ich auch erwartet. Wäre ja nicht schwer, automatisch zu prüfen, welche 
Register dauerhaft frei sind. Leider sind das aber tatsächlich einige 
Register, die dauerhaft ungenutzt sind.

Ein Performance "Problem" habe ich noch nicht wirklich, aber ich 
programmiere gerade an einer Art Motorsteuergerät, das über ein 60 Zahn 
Geberrad syncronisiert ist.
Dort ist es besonders wichtig, dass Steuersignale (z.B. für Zündung oder 
Einspritzung) möglichst schnell bzw. zeitnah an einem Ereignis erzeugt 
werden. Deswegen ist es wichtig, dass die Anzahl der Takte bis zum 
Schalten des Ports möglichst klein bleibt. Hier und da mal ein paar 
Takte mehr können bei hohen Drehzahlen den Winkelfehler schnell 
vergrößern.

Die zeitkritischen Steuerroutinen in ASM zu schreiben habe ich mir auch 
schon überlegt. Leider habe ich keine Ahnung, wie man den Assemblercode 
so schreibt, dass er sich nicht mit dem C-Code beißt.

von Peter II (Gast)


Lesenswert?

Philipp schrieb:
> Dass R3 nicht anderweitig gebraucht wird, habe ich vorher überprüft,
> habe dazu den ASM Code nach R3 abgesucht, ist nirgendwo vorgekommen.

und machst du das jedes mal, wenn du eine neue Version vom Compiler 
einspielst oder eine zusätzliche Funktion verwendest die du bis jetzt 
noch nicht hattest?

> Die zeitkritischen Steuerroutinen in ASM zu schreiben habe ich mir auch
> schon überlegt. Leider habe ich keine Ahnung, wie man den Assemblercode
> so schreibt, dass er sich nicht mit dem C-Code beißt.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Assembler_und_Inline-Assembler

von Philipp (Gast)


Lesenswert?

Cool, das habe ich noch nicht entdeckt. Wenn mein Programm läuft, werde 
ich das mit dem Inline Assembler mal versuchen.

Danke!

von Klaus (Gast)


Lesenswert?

Philipp schrieb:
> Dort ist es besonders wichtig, dass Steuersignale (z.B. für Zündung oder
> Einspritzung) möglichst schnell bzw. zeitnah an einem Ereignis erzeugt
> werden. Deswegen ist es wichtig, dass die Anzahl der Takte bis zum
> Schalten des Ports möglichst klein bleibt. Hier und da mal ein paar
> Takte mehr können bei hohen Drehzahlen den Winkelfehler schnell
> vergrößern.

Timing durch abgezählte Prozessorzyklen erzeugen zu wollen, ist doch von 
Anfang an ein Designfehler. Das hat man mal zu Zeiten des IBM-XTs 
versucht und es ist gründlich in die Hose gegangen.

MfG Klaus

von Falk B. (falk)


Lesenswert?

OMG!

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

Das gilt nicht nur für Software, auch für so ziemlich alles anders.

Mal eine Überlegung. Bei 10 MHz dauert ein Takt = 1 ASM Befehl beim AVR 
100ns. Bei 10.000 U/min dauert eine Umdrehung 36ms. 100ns/36ms = 2,7ppm 
oder 0,001 Grad. Wenn nun durch nicht perfekte Programmierung rein in C, 
OHNE das Schlüsselwort "register" VIELLEICHT eine Verzögerung oder 
Jitter von 10 Takten zusätzlich reinkommt, macht das sagenhafte 0,01 
Grad Winkelfehler. Ob dadurch ein Formel 1 Rennen verloren wird?

P S Schlaue Leute haben mit dem AVR schon vor Jahren eine jitterfreie 
Videoausgabe gemacht, sogar in C. Der Trick ist wie immer Know How 
(Sleep Mode, dadurch jitterfreier Einsprung in die ISR).

von tim (Gast)


Lesenswert?

Philipp schrieb:
> Dass R3 nicht anderweitig gebraucht wird, habe ich vorher überprüft,
> habe dazu den ASM Code nach R3 abgesucht, ist nirgendwo vorgekommen.

Soso, du Kompilierst also immer 2x. Erst mal ohne Register Festlegung 
und
prüfst dann ob R3 frei ist. Wenn ja dann das ganze nochmal mit register 
Festlegung. Und du checkst den gesamten ASM Code und nicht nur die
parr Zeilen von main.c.
Um wie viele Bytes wird das denn dann kürzer?

> Dass der Compiler Variablen in Register legt, so sie frei sind, hätte
> ich auch erwartet. Wäre ja nicht schwer, automatisch zu prüfen, welche
> Register dauerhaft frei sind. Leider sind das aber tatsächlich einige
> Register, die dauerhaft ungenutzt sind.

Die GCC Leute Freuen sich bestimmt über eine guten Patch.

> Dort ist es besonders wichtig, dass Steuersignale (z.B. für Zündung oder
> Einspritzung) möglichst schnell bzw. zeitnah an einem Ereignis erzeugt
> werden. Deswegen ist es wichtig, dass die Anzahl der Takte bis zum
> Schalten des Ports möglichst klein bleibt. Hier und da mal ein paar
> Takte mehr können bei hohen Drehzahlen den Winkelfehler schnell
> vergrößern.
Um bei Falk's (dem ich nur zustimmen kann) 10.000 U/min zu Bleiben:
Das sind ja gerade mal 10Khz. Und zwischen 2 Zähnen sind das
dann 1000 Clocks. Für den AVR dreht sich der Motor in Super Zeitlupe.

Außerdem: Was glaubst du wie schnell ein Einspritzventil ist und wie
genau die Auswertung von dem Geberrad....

Achte besser auf fehlerfreien Code als auf 0,irgendwas µS und
vertraue deinem Compiler. Der macht das schon richtig.

von Falk B. (falk)


Lesenswert?

@ tim (Gast)

>Um bei Falk's (dem ich nur zustimmen kann) 10.000 U/min zu Bleiben:
>Das sind ja gerade mal 10Khz.

U/min! Nicht U/s! Das ist keine Hyperturbine ;-)

> Und zwischen 2 Zähnen sind das
>dann 1000 Clocks. Für den AVR dreht sich der Motor in Super Zeitlupe.

Das wollte ich damit ausdrücken.

>Achte besser auf fehlerfreien Code als auf 0,irgendwas µS und
>vertraue deinem Compiler. Der macht das schon richtig.

In GCC we trust!

;-)

P S Ich hab mich verrechnet, hab die 10.000 U/m durch 360 geteilt, war 
wohl schon ne Rechnung weiter. Naja, macht trotzdem nur 6ms/U und damit 
0,06 Grad Winkelfehler bei 10 Takten.

von tim (Gast)


Lesenswert?

Falk B. schrieb:
>
> U/min! Nicht U/s! Das ist keine Hyperturbine ;-)

Die 10Kz Bezogen sich auf die Impulse vom Geberrad:
10.000U/min / 60 Sek * 60 Zähne


> In GCC we trust!

Yeah!
Bis jetzt hat er immer recht behalten.
Und um "Besseren" ASM-Code zu Schreiben
muss man sich schon verdammt anstrengen.
Das das hält man aber nicht das ganze
Projekt durch womit unterm Strich er dann
wieder "Besser" ist.

von PittyJ (Gast)


Lesenswert?

Ich mache diese Assembler-Dinge gar nicht mehr.
Weil in 2 Jahre kommt eine leicht geänderte Platine, oder das 
Prozessor-Nachfolgemodell, oder nur ein neuer GCC.
Und schon funktioniert das Neu-Kompilieren nicht, weil irgendwas beim 
Assembler nicht mehr stimmt. Dann debuggt man mühselig und ärgert sich 
über den Blödsinn von damals.

Ich bleibe auf C-Ebene, das macht die Wartung und Pflege viel einfacher.

Und wenn die Leistung nicht reicht, dann muss eben eine schnellere CPU 
benutzt werden. Dann würde aber auch bei Assembler die Leistung knapp 
werden, und Programmerweiterungen sind dann auch bald nicht mehr 
möglich.

von Rolf M. (rmagnus)


Lesenswert?

PittyJ schrieb:
> Ich mache diese Assembler-Dinge gar nicht mehr.
> Weil in 2 Jahre kommt eine leicht geänderte Platine, oder das
> Prozessor-Nachfolgemodell, oder nur ein neuer GCC.

Ein neuer GCC "kommt" nicht einfach, sondern auf den steigt man bewußt 
um.

> Und schon funktioniert das Neu-Kompilieren nicht, weil irgendwas beim
> Assembler nicht mehr stimmt. Dann debuggt man mühselig und ärgert sich
> über den Blödsinn von damals.

Warum sollte denn ausgerechnet beim Assembler was nicht mehr stimmen? 
Der bleibt doch genau gleich. Was sich ändern kann, ist das, was der 
Compiler aus dem C-Code macht. Deshalb ist die Gefahr bei dem doch 
eigentlich viel größer, daß er sich anders verhält.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Warum sollte denn ausgerechnet beim Assembler was nicht mehr stimmen?

So ziemlich alles, wenn man von einem AVR auf einen STM32 umsteigt. In C 
kann man wenigstens den hardware-unrelevanten Teil annähernd 1:1 
übernehmen, wenn man portabel genug programmiert hat.

: Bearbeitet durch Moderator
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Philipp schrieb:
> Dass R3 nicht anderweitig gebraucht wird, habe ich vorher überprüft,
> habe dazu den ASM Code nach R3 abgesucht, ist nirgendwo vorgekommen.

Alle Module sollten mit -ffixed-3 übersetzt sein.  libgcc verwendet R3 
nicht; einzige Ausnahme ist evtl. mit -mcall-prologues (__prologue_saves 
und __epilogue_restores).

Philipp schrieb:
> Selbst wenn man für den Vergleich den Wert von Zahnzaehler in Register
> R20 bräuchte, wäre doch INC R3 gefolgt von MOV R20,R3 schneller.
>
> Erwarte ich vielleicht einfach zu viel von einem Compiler?

Ohne testfall ist das lesen aus dem Kaffeesatz.  Evtl. eine Änderung 
analog zu

https://gcc.gnu.org/r192198

von Karl H. (kbuchegg)


Lesenswert?

Philipp schrieb:

noch mit einer Konstante, könnte das hier ausschlaggebend sein? Hier zur
> Übersicht nochmal die ganze Stelle:
>
1
>     Zahnzaehler++;
2
> LDI R20,0x01    Load immediate
3
> ADD R20,R3    Add without carry
4
> MOV R3,R20    Copy register
5
>     if ( Zahnzaehler == 15)  {
6
> CPI R20,0x0F    Compare with immediate
7
> BRNE PC+0x05    Branch if not equal
8
>
>
> Selbst wenn man für den Vergleich den Wert von Zahnzaehler in Register
> R20 bräuchte, wäre doch INC R3 gefolgt von MOV R20,R3 schneller.

und wie gehts dann weiter?
Ev. kann der Compiler die 1 in R20 ja an späterer Stelle noch gut 
gebrauchen.

von Rolf M. (rmagnus)


Lesenswert?

Frank M. schrieb:
> Rolf M. schrieb:
>> Warum sollte denn ausgerechnet beim Assembler was nicht mehr stimmen?
>
> So ziemlich alles, wenn man von einem AVR auf einen STM32 umsteigt.

Ja, wenn ich auf eine komplett andere Architektur umsteige, natürlich. 
Davon war aber nicht die Rede, sondern von:

PittyJ schrieb:
> eine leicht geänderte Platine, oder das Prozessor-Nachfolgemodell, oder nur
> ein neuer GCC.

Ich sage ja nicht, dass man C vermeiden soll, sondern nur, dass man, 
wenn man ein zyklengenaues Timing machen will, durchaus Assembler nutzen 
kann. Ich finde es jedenfalls albern, dann gleich einen um 
Größenordnungen schnelleren Prozessor einzusetzen, nur damit man das 
Timing auch anders hinbekommt.

: Bearbeitet durch User
von Timmo H. (masterfx)


Lesenswert?

In der avr gcc abi ist doch klar definiert welche Register vom Compiler 
wofür verwendet werden und welche frei sind oder nicht?
Aber potentiell werden glaube ich alle verwendet, also sollte man da mit 
gewisser Vorsicht herangehen. Lieber reines C

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Karl H. schrieb:
> Ev. kann der Compiler die 1 in R20 ja an späterer Stelle noch gut
> gebrauchen.

Schau nochmal genau hin, da ist selten eine 1 drin.

von Philipp (Gast)


Lesenswert?

Mir ist klar, dass dass sich durch das Verkürzen des Codes um ein paar 
Takte der Winkelfehler nicht Wesentlich ändern wird. Wahrscheinlich wird 
selbst ein halbes Grad nichts ausmachen.

Habe aber den Ehrgeiz, auszutüfteln wie man durch "geschickte" 
Programmstruktur den Code so schnell wir möglich zu machen. Habe wie 
gesagt noch nichts derartiges Programmiert, deswegen habe ich -wie wohl 
viele hier- noch nie drauf geachtet, wie der Assemblercode im Detail 
aussieht.
Da aber überall behauptet wird, dass der Compiler ja sooo intelligent 
wäre, hätte ich halt erwartet, dass er nicht dermaßen Takte verschenkt, 
dass ich es sogar auf einen Blick im ASM Code sehe.

Dazu ist mir gleich das Nächste aufgefallen: Am Anfang einer ISR werden 
teilweise Register auf den Stack gesichert, die in der ISR garnicht 
geändert werden. 12 push Befehle + 12 pop Befehle, davon je 10 unnötig, 
also 20! Takte verschenkt. Da wird mir wohl nichts anderes übrig 
bleiben, als selbst in ASM Hand anzulegen.

von Klaus (Gast)


Lesenswert?

Philipp schrieb:
> 12 push Befehle + 12 pop Befehle, davon je 10 unnötig,
> also 20! Takte verschenkt. Da wird mir wohl nichts anderes übrig
> bleiben, als selbst in ASM Hand anzulegen.

Und um dann, wie oben schon bemerkt, bei jeder Programmänderung, jeder 
neuen Compilerversion etc. zu überprüfen, ob sich die Registernutzung 
nicht doch geändert hat. Welche Register werden benutzt, wenn mal ein 
long oder ein long long im Code vorkommt?

MfG Klaus

von Malte S. (maltest)


Lesenswert?

Philipp schrieb:
> Da aber überall behauptet wird, dass der Compiler ja sooo intelligent
> wäre, hätte ich halt erwartet, dass er nicht dermaßen Takte verschenkt,
> dass ich es sogar auf einen Blick im ASM Code sehe.

Guckst du dabei auch über den Tellerrand der jeweils aktuell von die 
betrachteten Zeile C-Ursprung hinaus? Genau das tut der Compiler oft 
genug, was zu unsinnig erscheinendem Assembler führen kann, weil während 
der einen Berechnung schon die nächste vorbereitet wird, z.B. eben dass 
Registerinhalte nebenbei abfallen. Oder auch nur Flags. Und so wird bei 
der einen Anweisung 1 Takt investiert, um bei den nächsten drei jeweils 
2 zu sparen. Sieh dir mal den Code daraufhin an.

Klar ist das nicht 100% perfekt, aber im Schnitt auf den gesamten Code 
gesehen meistens besser als was man so händisch hinzutunen versucht.

von Rolf M. (rmagnus)


Lesenswert?

Philipp schrieb:
> Dazu ist mir gleich das Nächste aufgefallen: Am Anfang einer ISR werden
> teilweise Register auf den Stack gesichert, die in der ISR garnicht
> geändert werden.

Sowas macht er eigentlich nur dann, wenn von der ISR aus Funktionen 
aufgerufen werden, die nicht inline aufgelöst werden. Da er dann nicht 
weiß, welche Register innerhalb der Funktion benutzt werden, muss er 
eben alle sichern, die die Funktion möglicherweise nutzt.

von (prx) A. K. (prx)


Lesenswert?

Kleine unproblematische Registeroptierung: Einige AVRs haben ein paar 
freie Bytes im I/O-Bereich, GPIOR0..2. Die lassen sich kürzer und 
schneller ansprechen als normaler Speicher und eignen sich besonders gut 
für globale Flags/Einzelbits, weil sie von den Einzelbitbefehlen 
angesprochen werden können.

von Klaus (Gast)


Lesenswert?

Rolf M. schrieb:
> Philipp schrieb:
>> Dazu ist mir gleich das Nächste aufgefallen: Am Anfang einer ISR werden
>> teilweise Register auf den Stack gesichert, die in der ISR garnicht
>> geändert werden.
>
> Sowas macht er eigentlich nur dann, wenn von der ISR aus Funktionen
> aufgerufen werden, die nicht inline aufgelöst werden. Da er dann nicht
> weiß, welche Register innerhalb der Funktion benutzt werden, muss er
> eben alle sichern, die die Funktion möglicherweise nutzt.

Da zeigt uns Philip dann mal den Sourcecode und den Assemblercode, in 
dem das von ihm beobachtete auftritt. Dazu bitte auch die benutzten 
Optionen.

von avr (Gast)


Lesenswert?

Philipp schrieb:
> Das würde nur 2 Takte brauchen und somit 1/3 der Rechenzeit einsparen!

Da es anscheinend noch niemand bemängelt hat: sbi und cbi brauchen auf 
"normalen" avrs 2 Takte. Folglich wäre diese Lösung langsamer, würde 
jedoch Speicher sparen. Nur auf den reduced core avrs (attiny4/5/9/10) 
braucht der Befehl 1 Takt.

von (prx) A. K. (prx)


Lesenswert?

avr schrieb:
> Nur auf den reduced core avrs (attiny4/5/9/10)
> braucht der Befehl 1 Takt.

Und bei den Xmegas.

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.