Hallo,
ich stolpere über ein Laufzeitproblem bei der Verteilung der 3 unteren
Bytes eines 24-bit Wertes auf 3 Ports.
inline void machmal(uint32_t wert)
{
PORTA = (wert & 0xff); // ok, OUT PORTA,Rn
PORTB = ((wert >> 8) & 0xff); // 4 Register werden sinnlos umkopiert
PORTC = ((wert >> 16) & 0xff); // ok, OUT PORTC, Rn+2
}
machmal(0x00112233UL);
Der gcc läuft mit -o3
Wie gehts besser ?
Union etc. geht nicht, weil ich alles in Registern behalten will.
Gruß,
Michael
Michael Appelt schrieb:> Union etc. geht nicht, weil ich alles in Registern behalten will.
???
Solange du in C programmierst, hast du eh keinen Einfluß darauf.
Aber:
Schau dir das ABI an. Dein uint32-Wert steht ja beim Funktionsaufruf in
4 Registern. Mit ein paar Zeilen inline-Assembler oder einer eigenen
Assembler-Routine kannst du die Werte direkt aus den Registern in die
Ports schreiben. Schneller gehst es nicht.
Oliver
>Solange du in C programmierst, hast du eh keinen Einfluß darauf.
Nicht komplett aber man kann mit dem Schlüsselwort register eine
variable in den Registern halten.
Gruß Jonas
Hallo,
@Oliver S:
Union geht über den Spicher und die genannte Sequenz und ein wenig mehr
sollen unter 1us laufen.
Das ganze muss 6mal aufgerufen werden, und mir gefallen die hart
kodierten Register nicht. Muss vermutlich tiefer in den inline-Assembler
reinschauen.
@Jonas:
welche Casts ?
PORTB = (uint8_t) ((uint16_t) wert) >> 8);
Wäre das optimal ? Stimmt, hab ich noch nicht probiert.
Gruß,
Michael
Jonas Biensack schrieb:> Nicht komplett aber man kann mit dem Schlüsselwort register eine> variable in den Registern halten.
Register allocation ist Sache des Compilers. Daher wird das
Schlüsselwort register seit vielen Compiler-Generationen vom gcc
schlicht ignoriert.
Peter Dannegger schrieb:> Besser gehts nicht:
In reinem Assembler wären es nur die 3 out-Befehle und return.
Oliver
Hallo,
naja, bei einem Call stehen diese Parameter bei meinem Programm immer in
Registern. Am Anfang steht allerdings eine Variable.
Der Code von Peter ist da ein wenig zu optimistisch, aber ich probiers
heute abend mit -Os
Gruß,
Michael
Michael Appelt schrieb:> naja, bei einem Call stehen diese Parameter bei meinem Programm immer in> Registern.
es gibt aber bei inline keinen call mehr.
Michael Appelt schrieb:> Der Code von Peter ist da ein wenig zu optimistisch
Ja, durch das "inline" hat er zu gut optimiert.
Versuch mal erst zu maskieren und dann schieben.
Peter II schrieb:> aber nur wenn die werte schon in einem Register stehen.
Die Werte stehen in Registern, weil der gcc sie darin in die Funktion
übergibt.
Oliver
Oliver S. schrieb:> Die Werte stehen in Registern, weil der gcc sie darin in die Funktion> übergibt.
aber nur wenn eine eine Funktion gibt, und bei inline gibt es keine
Funktion mehr.
>naja, bei einem Call stehen diese Parameter bei meinem Programm immer in>Registern.
nö, die können auch auf dem stack liegen.
Schon mal dran gedacht das es funktionensaufrufe mit mehr parametern als
verfügbaren registern gibt?
Gruß Jonas
Peter II schrieb:> Michael Appelt schrieb:>> naja, bei einem Call stehen diese Parameter bei meinem Programm immer in>> Registern.>> es gibt aber bei inline keinen call mehr.
Aber auf die drüberliegenden Functions, die sind nicht inline.
Es sind 6 Functions f1....f6. Jede hat leicht unterschiedliche
Parameter, d.h. es werden möglicherweise unterschiedliche Register
benutzt. Jede ruft am Anfang die genannte inline function auf.
Wegen der Registerzuweisung bin ich mit inline asm nicht glücklich.
Wenn es so jetzt funktioniert, dann löst das mein Problem.
Gruß,
Michael
Michael Appelt schrieb:> Aber auf die drüberliegenden Functions, die sind nicht inline.
die hast du uns aber nicht gezeigt, damit können wir das ja schlecht
wissen.
Hallo,
also man sollte schon in der Lage sein, abstrakt über ein Problem zu
reden ohne immer gleich ein 8k-Programm im Source anzuhängen.
Ich habe ja nicht gesagt, daß der gcc was falsch macht und die Hinweise
waren durchaus zutreffend genug, um damit mein Brett vorm Kopp zu
entfernen.
Gruß,
Michael
Michael Appelt schrieb:> Union etc. geht nicht, weil ich alles in Registern behalten will.Michael Appelt schrieb:> Union geht über den Spicher und die genannte Sequenz und ein wenig mehr> sollen unter 1us laufen.
Wie kommst du darauf? Hast du's ausprobiert?
Wenn nicht, dann wage einen Versuch und sei überrascht ;-)
Hallo,
nein, habe ich nicht probiert. Es würde auf Union als Parametertyp
hinauslaufen, und da müsste ich mich ebenfalls erstmal einlesen.
Ich weiß garnicht:
Wo steht, dass man hier nur Fragen stellen darf, wenn man vorher bereits
alles ausprobiert hat ?
Wo steht, dass man alles ausprobieren muss ?
Gruß,
Michael
Jonas Biensack schrieb:> nö, die können auch auf dem stack liegen.
Bei irgendwelchen Compilern für irgendwelche Zielplattformen ja.
Beim avr-gcc mit einem uint32-Funktionsparameter wie im oben gezeigten
Code liegt der immer in den Registern.
Oliver
Hallo,
ok, ich muss zugeben, dass hat was :-))
Die Details des Union bleiben in der function verborgen.
Danke für die Mühe, die Ihr Euch gemacht habt.
Vermutlich bleibt es aber dabei: ohne -Os klappt es auch mit union
nicht.
BTW die genaue Zeit der out-Befehle ist mir relativ egal, aber mal eben
zusätzlich 4 register kopieren ging mir irgendwie quer - technisch wäre
es aber relativ egal.
Gruß,
Michael
Michael Appelt schrieb:> nein, habe ich nicht probiert. Es würde auf Union als Parametertyp> hinauslaufen, und da müsste ich mich ebenfalls erstmal einlesen.>> Ich weiß garnicht:>> Wo steht, dass man hier nur Fragen stellen darf, wenn man vorher bereits> alles ausprobiert hat ?
Nirgends, das ist ganz dir überlassen.
> Wo steht, dass man alles ausprobieren muss ?
Ebenfalls nirgends.
Nur tust du dir dann mit Aussagen wie der folgenden selber keinen
Gefallen:
Michael Appelt schrieb:> Union geht über den Spicher
Denn das erweckt den Eindruck, dass du dir diesbezüglich sicher bist und
deswegen auch für die Helfenden keinerlei Bedarf siehst, die Unions doch
einmal auszuprobieren.
Ohne die obige Aussage wäre der Vorschlag mit der Union sicher schon in
der ersten oder zweiten Antwort gekommen, so waren aber alle Hirne erst
einmal blockiert.
Ich selber war mir erst auch nicht sicher, ob hier eine Union etwas
bringen würde. Nach einer kurzen Goole-Suche nach
gcc struct argument register
kam ich zum aber Schluss, dass das zumindest einen Versuch wert ist. Und
siehe da ...
Hallo,
jawoll, Du bist ein ganz schlauer, deshalb darfst Du ja auch Moderator
sein.
Also ich habe ein Berufsleben, und privat geht mein Ehrgeiz zu einer
angemessene Lösung in angemessener Zeit.
Aktuell erhalte ich bei -Os
DATA_OUT = (uint8_t) ((uint16_t) adr >> 8);
250: 27 2f mov r18, r23
252: 33 27 eor r19, r19
254: 28 b9 out 0x08, r18 ; 8
Gruß,
Michael
Peter II schrieb:> was macht das für einen sinn?>> 250: 27 2f mov r18, r23>> 252: 33 27 eor r19, r19
Das ist die optimale Sequenz für einen 16-Bit Shift um 8 mit src=r22/23,
dst=r18/19. Danach kommt die Zuweisung, die nur die untere Hälfte
verwendet.
Sorry, meine Frage war rein rhetorisch.
Ich wunder mich nur, was vom gcc alles optimiert wird und was
andererseits manchmal auch nicht. Aber die Bewunderung der Entwickler
überwiegt.
Nachtrag:
DATA_OUT = (uint8_t) (adr >> 16);
25a: aa 27 eor r26, r26
25c: bb 27 eor r27, r27
25e: 88 b9 out 0x08, r24 ; 8
Auch nicht schlecht....ich probiers wohl besser mit union.
Gruß,
Michael
A. K. schrieb:> Peter II schrieb:>> was macht das>> eor r19, r19>> für einen sinn?>> Bei non-volatile lhs entfallen mov und eor.
Das versteh ich nicht.
@Falk: mir fehlt in Deinem Posting noch das Hexfile ;-)
Ok, ich habs verstanden und ausprobiert, das Union hat gewonnen.
Allerdings war -Os nix, weil dann meine inline functions per call
aufgerufen werden. s=size und nicht s=superefficient.
Zur Zeit nehme ich -Os und #defines - Blöcke statt functions, damit
läufts wie gewünscht.
Gruß,
Michael
Michael Appelt schrieb:> Allerdings war -Os nix, weil dann meine inline functions per call> aufgerufen werden. s=size und nicht s=superefficient.
dann verwende always_inline
Michael Appelt schrieb:>> Bei non-volatile lhs entfallen mov und eor.>> Das versteh ich nicht.
"lhs" = left hand side, und zwar hier von der Zuweisung.
Der Code mit dem EOR entsteht bei
*(volatile uint8_t *)ptr = adr >> 8;
aber nicht bei
*(uint8_t *)ptr = adr >> 8;