Hallo,
ich möchte einzelne Register in avr-gcc fest verwenden. Dazu gibt es
eine Syntax im Wiki-Artikel AVR-GCC-Codeoptimierung. Die habe ich
auch schon vor Jahren erfolgreich verwendet.
1
register uint8_t more_flags asm("r2");
Nun hat Falk an diese Stelle im Wiki eine dicke Warnung plaziert, dass
es zu Problemen kommen kann. Im AVR Libc FAQ steht, dass die Register
r2-r7 bedenkenlos auf diese Weise genutzt werden können, wenn keine
Quellen eingebettet werden, die unter anderen Annahmen kompiliert
wurden.
Warum soll man sich also vor der Verwendung hüten? Welche Probleme
können auftauchen, wenn ich das komplette Programm auf einmal übersetze?
Leider konnte ich keine weiteren Details/Hintergründe dazu finden.
AVR-Nutzer schrieb im Beitrag #4897528:
> Warum soll man sich also vor der Verwendung hüten? Welche Probleme> können auftauchen, wenn ich das komplette Programm auf einmal übersetze?
Weil immer auch bereits übersetzter Code (aus Libraries) hinzukommt, der
von deiner Reservierung nichts weiß und das Register möglicherweise
verwendet.
Es gibt doch ein einfaches Verfahren, um zu prüfen, ob man ein Register
fest zuweisen kann. Erst mal ohne Zuweisung übersetzen, .lss File nach
dem Register durchsuchen und bei Nichtfinden sicher sein.
Carl D. schrieb:> Es gibt doch ein einfaches Verfahren, um zu prüfen, ob man ein Register> fest zuweisen kann. Erst mal ohne Zuweisung übersetzen, .lss File nach> dem Register durchsuchen und bei Nichtfinden sicher sein.
Das musst du dann aber auch immer wieder machen. Schon eine kleine
Änderung in den Sourcen kann zusätzlichen Code aus Libraries rein
ziehen.
Stefan E. schrieb:> Das musst du dann aber auch immer wieder machen. Schon eine kleine> Änderung in den Sourcen kann zusätzlichen Code aus Libraries rein> ziehen.
...oder dass der vorliegende Code anders übersetzt wird.
Eigentlich muss jedes Modul mit -ffixed-2 übersetzt werden, und zwar
auch solche, die R2 nicht verwenden.
Ein weiteres Problem mit globalen Registern ist, dass diese nicht
volatile sein können, diele Anwender aber gerne Kommunikation mit einer
ISR darüber abwickeln möchten.
Carl D. schrieb:> .lss File nach> dem Register durchsuchen und bei Nichtfinden sicher sein.
Sicher sein?
War das jetzt Satire oder irgendein aufs Programmieren transponierter
Insider-Running-Gag den ich zufällig noch nicht kenne weil ich die
falschen Fernsehserien sehe? Oder was?
Hi
Denke, .lss meint das List-File (heißen bei mir .lst ?).
Dort sind unter Anderem alle Register und Deren Anzahl an Aufrufen im
Code enthalten.
Wenn r6 0 dort steht, wird r6 nicht benutzt und kann beliebig für eigene
Zwecke verunglimpft werden.
Allerdings muß man, wenn man weiteren Code einbindet, immer schauen, ob
die Nutzung des eigenen Register sich nicht schlagartig geändert hat -
dann spielt nämlich noch wer Anders mit diesem Register.
MfG
@Crisduf: Das beantwortet meine Frage nicht. Der Grund steht aber schon
da: Ich "brauche" more_flags (bitadressierbar, schneller Zugriff)
@Stefan Ernst: "immer ... code hinzukommt": Da wirst du wohl die
erwähnte AVR Libc meinen, die ja explizit die Reservierung von r2-r7 für
unbedenklich hält). Geht das FAQ davon aus, dass ich dafür die Libc neu
übersetze und schreibt das nicht, oder gibt es noch eine andere Quelle,
aus der übersetzter Code in meinem Programm landen könnte?
AVR-Nutzer schrieb im Beitrag #4898207:
> oder gibt es noch eine andere Quelle,> aus der übersetzter Code in meinem Programm landen könnte?
Ja, die Libraries des Compilers. Wenn du z.B. den Divisions- oder
Modulo-Operator verwendest, landen dafür Funktionen aus diesen Libraries
in deinem Code (außer natürlich es lässt sich zu was anderem optimieren,
wie Shiften).
Oder auch (noch weniger offensichtlich) bei einem switch, wenn der
Compiler dieses über eine Sprungtabelle realisiert.
Um nur mal zwei Beispiele zu nennen.
Stefan E. schrieb:> AVR-Nutzer schrieb im Beitrag #4898207:>> oder gibt es noch eine andere Quelle,>> aus der übersetzter Code in meinem Programm landen könnte?>> Ja, die Libraries des Compilers.
Die avr-libgcc verwendet R2-R7 nicht — mit einer Ausnahme: Die
Funktionen, die für -mcall-prologues verwendet werden. Bei diesen ist
es vom Caller abhängig, welche Register verwendet werden.
Die Strategie zur Verwendung globaler Register ist also:
0. Überprüfen, ob diese wirklich notwendig / hilfreich sind :-)
1. Alle eigenen Module mit -ffixed-2 etc. übersetzen.
2. Verifizieren, dass keine Fremdmodule (libc, libm, libgcc, Vendor-
spezifische Module / Bibliotheken) die entsprechenden Register
verwenden. Mit Ausnahme von 3. verwendet libgcc R2-R7 nicht.
3. Falls __prologue_saves / __epilogue_restores der libgcc gelinkt
werden, sind alle Caller zu überprüfen, wie diese die genannten
Funktionen verwenden.
Die Register R2 bis R17 werden von aufgerufenen Funktionen, die sie
benutzen, hinterher wieder auf ihren alten Wert zurückgesetzt. Das gilt
selbstverständlich auch für Bibliotheksfunktionen. Deswegen kann man
alle diese Register als globale Variablen verwenden, wenn man folgende
Dinge beachtet:
1. Wenn aus einer Interruptroutine auf globale Registervariablen
zugegriffen wird, müssen alle Funktionen, die von dem Interrupt
unterbrochen werden können (also i.Allg. der komplette Code
einschließlich aller Bibliotheken), mit der Registerdeklaration oder
der -ffixed-Option neu kompiliert werden.
2. Wenn aus einer Callback-Funktion auf globale Registervariablen
zugegriffen wird, müssen mindestens alle direkten und indirekten
Aufrufer dieser Funktion mit der Registerdeklaration oder der
-ffixed-Option neu kompiliert werden.
3. Da die Register R8 bis R17 beim Aufruf von Funktionen, deren
Argumentlisten größer als 8 Bytes sind, für die Übergabe von
Funktionsargumenten genutzt werden, kann es zu einem Konflikt kommen,
wenn eines dieser Register als globale Registervariable verwendet
wird. Der Compiler gibt dann aber die Warnung
fixed register r<i> used to pass parameter to function
aus.
Diese Liste habe ich mir eben ausgedacht und ist deswegen möglicherweise
unvollständig. Wenn jemand noch weitere Fallstricke in Verbindung mit
globalen Registervariablen kennt, möge er sie nennen.
Johann L. schrieb:> Die avr-libgcc verwendet R2-R7 nicht — mit einer Ausnahme: Die> Funktionen, die für -mcall-prologues verwendet werden.
Ist das garantiert? Auch für die Zukunft? Der GCC an sich verwendet
diese Register ja schon.
Danke, insbesondere an die letzten drei Beiträge. Damit weiß ich jetzt
besser, woran ich bin und was zu tun ist.
Ich werde weiterhin versuchen diese Register möglichst nicht reservieren
zu müssen. Allerdings war es etwas unbefriedigend keinen konkreten Grund
für den Verzicht zu kennen.
Ich habe diese Diskussion im Warnhinweis verlinkt, falls mal wieder
jemand an der Begründung hängen bleibt und lieber Hintergründe anstelle
Best-Practice-Ratschläge erfahren möchte.
AVR-Nutzer schrieb im Beitrag #4898207:
> bitadressierbar, schneller Zugriff
Falls dein verwendeter Controller es unterstützt, könntest du über
ein GPIOx-Register stattdessen nachdenken. Auch beliebige andere,
gerade nicht benutzte IO-Register kleiner 0x20 sind dafür geeignet.
Der Vorteil ist, dass dir hier garantiert niemand anders in die Suppe
spucken kann. ;)
Hallo Jörg,
ich verwende bereits das GPIOR0. Die Register GPIOR1 und GPIOR2 sind
oberhalb von 0x1F. Meine Hoffnung war, dass es nicht notwendig ist DDRx-
oder PORTx-Bits ungenutzter Ausgänge zu toggeln. Die Register der
Interrupt-Flags sind in diesem Zusammenhang auch nicht hilfreich. Ich
werde jetzt vorerst mal meine Flags an solchen Orten unterbringen.
Sobald ich die Zeit finde, versuche ich evtl. das mit den reservierten
Registern sauber umzusetzen.
Ich kann aktuell aber auch noch nicht genau sagen, ob ich nicht doch mit
langsameren Zugriffen auskomme, oder ob ich sogar darauf angewiesen bin
Einzelheiten in anderer Hardware (z.B. diskret) zu realisieren.
Aktuell bin ich nicht am optimieren der Performance, sondern an der
Implementierung des Proof-Of-Concept.
AVR-Nutzer schrieb im Beitrag #4900318:
> ich verwende bereits das GPIOR0
OK, dann bist du natürlich schon in einem Bereich, wo es den Aufwand
lohnen kann, auch mal Register zu reservieren. Die Hinweise, auf was
man dabei achten sollte, sind ja im Thread ausreichend vorhanden.
Yalu X. schrieb:> Johann L. schrieb:>> Die avr-libgcc verwendet R2-R7 nicht — mit einer Ausnahme: Die>> Funktionen, die für -mcall-prologues verwendet werden.>> Ist das garantiert?
Momentan ist es gegeben durch die Codebasis der libgcc, die zum Großteil
in Assembler steht. Es gibt zwar noch Funktionen, die in C
implementiert sind, aber die wirst Du fleißig suchen müssen und in wenig
verwendeten Ecken wie Fixedpoint auch finden.
> Auch für die Zukunft?
Dokumentiert ist das Verhalten nicht, und die Generierung erfolgt ohne
-ffixed o.ä. Was den asm-Code angeht, ist die Frage also, ob es andere
Leute gibt, die zur libgcc beitragen. In den letzten Jahren war das
nicht der Fall — bis auf den Code von Sean D'Epagnier, der allerdings
nicht von ihm selbst integriert wurde.
Markus F. schrieb:> Müsste nicht - wenn man die gesamte Toolchain mit -ffixed-xx neu baut -> bei Erfolg "weitgehende Problemfreiheit" garantiert sein?
Die Tools neu zu generieren hilft nix, es geht um die
Target-Bibliotheken.
Wie auch immer, bisher war es niemandem wichtig genug, das zu ändern.
Und ob das in Bezug auf die AVR-Libc hilft wäre abzuklären, denn auch
diese implementiert viele Funktionen in handverlesenem Assembler.
Mir ist auch noch nicht zu Ohren gekommen, dass jemand tatsächlich
Probleme mit solchen Kollisionen hatte. Das mag daran liegen, dass
globale Register nicht oft eingesetzt werden, wohl auch weil die
Realität hinter den Erwartungen zurückbleibt.
Neben Problemen der Anwendung wie o.g dass globale Register nicht
volatile sind und daher asynchrone Verwendung nicht ohne zusätzlichen
Inline-Asm funktioniert, sowie potentielle Kollisionen mit Verwendung in
Libs, sind die folgende Punkte zu berücksichtigen:
o Weil globale Register keine ABI-Register sein dürfen, sind es
untere Register R2...R9, die weniger Instruktionen zulassen;
insbesondere keine mit Konstanten wie LDI, SUBI, CPI, ANDI, ...
Der Compiler wird dann obere Regs verwenden und MOVs etc. erzeugen.
o Ein Register global zu machen, sagt dem Compiler: "Verwende
dieses Register nicht zum Allokieren für einen Wert" was
bedeutet, dass manche Optimierungen nicht auf globalen Registern
ausgeführt werden und die Codequalität hinter "normalen" Registern
zurückbleibt.
Da Verwendung globaler Regs ausschließlich durch (Spekulation über)
bessere Codeerzeugung motiviert ist, sind diese beiden Punkte auch
relevant und sollten in die Entscheidung eingehen. I.d.R. geht es dabei
um kleine ISRs, wo der robuste und erfolgversprechende Ansatz ist, diese
in Assembler zu schreiben (Portabilität ist eh schon futsch).