Hallo,
wieder einmal komme ich nicht weiter. Und zwar habe ich eine
zeitkritische Anwendung, in der das Speichern von Status- und anderen
Registern innerhalb der ISR einen nicht unerheblichen Teil der CPU-Zeit
in Anspruch nimmt, obwohl nur ein Flag gesetzt werden soll. Nun habe ich
hier im Forum gelesen, dass man die ISR als naked definieren kann:
1
ISR(ADCA_CH0_vect,ISR_NAKED)
2
{
3
intFlags|=(1<<4);// Bit 4 setzen
4
}
Um zu verhindern, dass der Compiler Code erzeugt, der die nun nicht
gesicherten Register anfasst, wuerde ich gerne die Variable intFlags in
ein ungenutztes Peripherie-Register speichern. Mir schwebt da z.B. das
IRCOM-TXPLCTRL-Rgister des xMegas (ATxMega64A1)vor, das ich auf keinen
Fall anderweitig nutzen werde. Irgendein ungenutzter Timer geht
natuerlich auch. Mit den general-purpose-Registern geht das wohl so:
1
registervolatileuint8_tintFlagsasm("r4")
Wie aber nutze ich ein beliebiges Register? Muss ich da die Adresse
angeben und wenn ja, wie?
Vielen Dank!
Ingo.
@AVR (Gast)
>Hat der xmega kein GPIO0 mehr, das man via SBI/CBI beackern kann?
Gute Idee. Man kann aber auch andere Registerbits nutzen, die ungenutz
sind, auch Portbits etc! Müssen halt nur in den unteren Registern
liegen, um mit sbi/cbi angesprochen werden zu können.
Johann L. schrieb:> 1) Schreibe die ISR in Assembler!
Habe leider noch keine Erfahrung im Misch-Kauderwelsch, wohl aber in
reinem asm. Kannst du mir einen Tipp geben, wo ich etwas ueber die
Integration von asm-Routinen nachlesen kann? Oder meinst du inline-asm?
> 2) Register sind nicht volatile, entsprechende Compilerwarnung beachten!
Heisst das, dass "volatile" schlichtweg unnoetig ist?
Falk Brunner schrieb:> Man kann aber auch andere Registerbits nutzen, die ungenutz> sind, auch Portbits etc!
Meine Frage zielt darauf ab, ob das auch in gcc geht, und wenn wie
(Bsp). Oder ist hier asm obligatorisch?
Gruss,
Ingo.
Ingo B. schrieb:> Johann L. schrieb:>> 1) Schreibe die ISR in Assembler!>> Habe leider noch keine Erfahrung im Misch-Kauderwelsch, wohl aber in> reinem asm. Kannst du mir einen Tipp geben, wo ich etwas ueber die> Integration von asm-Routinen nachlesen kann? Oder meinst du inline-asm?
Mischen ist hier nicht angesagt sondern reiner Assembler. Dafür wiederum
gibt es 2 Möglichkeiten:
i) Ein Assemblermodul (.sx oder .S) mit der ISR oder
ii) Integration der in Assembler stehenden ISR per globalem asm
in die C-Quelle.
Als Vorlage kann die Assembler-Ausgabe des Compilers dienen, welcher
dieser für eine minimale naked-ISR erzeugt. Siehe -save-temps und das
s-File.
>> 2) Register sind nicht volatile, entsprechende Compilerwarnung beachten!>> Heisst das, dass "volatile" schlichtweg unnoetig ist?
voltile bei globalen Registern hat keinen Effekt. Dementsprechend kann
GCC Zugriffe auf globale Register wegoptimieren.
@ Ingo B. (tueddler)
>Habe leider noch keine Erfahrung im Misch-Kauderwelsch, wohl aber in>reinem asm. Kannst du mir einen Tipp geben, wo ich etwas ueber die>Integration von asm-Routinen nachlesen kann?
In der Doku der libc, ist beim WinAVR dabei.
> Oder meinst du inline-asm?
Nein.
>> 2) Register sind nicht volatile, entsprechende Compilerwarnung beachten!>Heisst das, dass "volatile" schlichtweg unnoetig ist?
Nein, es funktioniert nicht wie gewünscht.
>> Man kann aber auch andere Registerbits nutzen, die ungenutz>> sind, auch Portbits etc!>Meine Frage zielt darauf ab, ob das auch in gcc geht,
Was?
Siehe Anhang.
Falk Brunner schrieb:> Nein, es funktioniert nicht wie gewünscht.
Was heisst gewuenscht? Ich moechte nur, dass der Compiler die Variable
ausschliesslich in der angegebenen Adresse (z.B. timer compare register)
speichert und nicht im SRAM.
>>Meine Frage zielt darauf ab, ob das auch in gcc geht,>> Was?
Naiv soetwas wie:
1
registeruint8_tintFlagsasm("TXPLCTRL");
Also, wie kann ich gcc klarmachen, dass die Adresse fuer intFlags die
eines Peripherie-Registers ist?
Dein Beispiel sollte es schon tun, nur warum kann ich sicher sein, dass
der Code, der im define angegeben wird
1
PORTD&=~(1<<PD0);
keines der nun nicht gespeicherten Register (Status etc.) aendert?
>> Also, wie kann ich gcc klarmachen, dass die Adresse fuer intFlags die> eines Peripherie-Registers ist?
Garnicht. Schreib einfach TXPLCTRL und gut ist.
Johann L. schrieb:> voltile bei globalen Registern hat keinen Effekt. Dementsprechend kann> GCC Zugriffe auf globale Register wegoptimieren.
Nun bin ich verwirrt. Sind Register nicht immer global? Also entweder
weise ich einer Variable das Register zu (dann steht dieses Register
fuer den Compiler fuer den Rest des Codes nicht mehr zur Verfuegung),
oder die ganze Aktion "register uint8_..." hat keinen Sinn, oder?
Offensichtlich verstehe ich da etwas nicht richtig...
Johann L. schrieb:> Wenn alles andere scheitert: Doku lesen!
Super, danke, das hatte ich nicht gesehen. Aber im Prinzip stimmt meine
Vorstellung schon. Nur, dass das "nicht zur Verfuegung stehen" bei
lokalen Registern nur innerhalb der Funktion zutrifft, oder?
Bleibt die Frage, wie ich statt "r0" etc. ein Peripherie-Register
spezifiziere. Ein Code-Beispiel, dass die falsche Version oben
korrigiert waere super.
Ingo B. schrieb:> Johann L. schrieb:>> Wenn alles andere scheitert: Doku lesen!>> Super, danke, das hatte ich nicht gesehen. Aber im Prinzip stimmt meine> Vorstellung schon. Nur, dass das "nicht zur Verfuegung stehen" bei> lokalen Registern nur innerhalb der Funktion zutrifft, oder?
Nein, selbst in der Funktion kann das Register anderwärtig verwendet
werden.
> Bleibt die Frage, wie ich statt "r0" etc. ein Peripherie-Register> spezifiziere. Ein Code-Beispiel, dass die falsche Version oben> korrigiert waere super.
Du denkst zu kompliziert.
Johann L. schrieb:> Nein, selbst in der Funktion kann das Register anderwärtig verwendet> werden.
OK, hier steht's:
"This option does not guarantee that GCC generates code that has this
variable in the register you specify at all times. You may not code an
explicit reference to this register in the assembler instruction
template part of an asm statement and assume it always refers to this
variable. However, using the variable as an asm operand guarantees that
the specified register is used for the operand."
Nur was genau heisst "asm operand"? Ist das register-keyword also nur
von Nutzen, wenn ich asm-Code anwende (z.B damit ich sicher sein kann,
dass in r0 die Variable intFlags steht)? Irgendwie ist mir der Sinn von
"register" wohl doch noch nicht klar. Gibt es irgendwo dazu ein
praktischen Beispiel, dass den Sinn verdeutlicht?
Johann L. schrieb:> Du denkst zu kompliziert.
Hmm ja, das sollte gehen, da muss ich noch etwas rumprobieren...
Danke jedenfalls fuer die Tipps!
Ingo B. schrieb:> Johann L. schrieb:>> Nur was genau heisst "asm operand"?
Inline asm kann Input- und / oder Output-Operanden haben, im Manual "C
Expression Operands".
http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html> Ist das register-keyword also nur von Nutzen, wenn ich asm-Code> anwende (z.B damit ich sicher sein kann, dass in r0 die Variable> intFlags steht)?
Die Zuordnung Variable <-> lokales Register gilt nur zum "Zeitpunkt" der
Verwendung als asm-Operand.
> Gibt es irgendwo dazu ein praktischen Beispiel, das den Sinn> verdeutlicht?
Ja, hier, für avr-gcc:
1
staticinlineuint8_tprandom8(void)
2
{
3
registeruint16_tpasm("r26");
4
asmvolatile("%~call lfsr_8":"=r"(p));
5
returnp;
6
}
prandom8 ist eine C-Funktion, die eine 8-Bit Pseudozufallszahl liefern
soll. Die Implementierung steht in asm in lfsr_8, allerdings genügt
diese Funktion nicht dem avr-gcc ABI: Sie gibt nämlich einen 16-Bit
Pseudozufall in R26/R27 zurück. prandom8 funktioniert hier als Wrapper
für die asm-Funktion und teilt dem Compiler die Nebeneffekte des Aufrufs
von lfsr_8 mit.
Alles in allem beschreibt das asm einen transparenten Funktionsaufruf,
d.h. es ist ein Funktionsaufruf, den der Compiler aber nicht als solchen
sieht.
Eingesetzt wird das in einem Context wo minimaler Overhead geboten ist
aber dennoch möglichst viel des Codes (Asteroids-Clone) in C steht.
http://www.youtube.com/watch?v=TDiPibnHgW4
Das Spiel läuft übrigens auf einem ATmega168, und als globales
Flag-Register wird GPIOR0 eingesetzt.