Forum: Compiler & IDEs ineffektive Interrupts bei WinAVR


von Benedikt (Gast)


Lesenswert?

Gibt es irgendeine Möglichkeit WinAVR dazu zu bringen die ganzen pushs
in der Interruptroutine etwas zu optimieren ?

In der Routine werden 4 Register benötigt, aber alle 15 auf den Stack
geschoben. Das sind immerhin 44 CPU Takte, die sinnlos verschwendet
werden, fast genausoviel wie der Code in der Interruptroutine benötigt.

von Peter D. (peda)


Lesenswert?

Du rufst warscheinlich im Interrupt eine Unterfunktion auf, die erst
nach dem Interrupt oder in einem anderen Objekt definiert wird.

Dann weiß der Compiler ja nicht, daß nur 4 Register verwendet werden.

Abgesehen davon kostet ein Funktionsaufruf selber ja schon 7 Zyklen.


Peter

von Benedikt (Gast)


Lesenswert?

Stimmt, daran lags.

Allerdings ist das ganze immer noch ziemlich mies.
Der Compiler sichert z.B. das zero und temp reg obwohl diese nicht
benötigt werden, und einige weitere Register könnte man auch sparen.

Ich will aber nicht weiter meckern, immerhin ist WinAVR kostenlos (im
Gegensatz zum M16C Compiler NC30, der in der Vollversion immerhin ca.
3k€ kostet.

von Branko (Gast)


Lesenswert?

Bei CodeVisionAVR mit #pragma savereg- kann man selber entscheiden
welche von den Register zu reten sind.
Naturlich am Ende nicht vergessen #pragma savereg+

/* Turn registers saving off */
#pragma savereg-

/* interrupt handler */
interrupt [1] void my_irq(void) {
/* now save only the registers that are
   affected by the routines in the interrupt
   handler, for example R30, R31 and SREG */
#asm
    push r30
    push r31
    in   r30,SREG
    push r30
#endasm

/* place the C code here */

/* now restore SREG, R31 and R30 */
#asm
    pop r30
    out SREG,r30
    pop r31
    pop r30
#endasm

von Peter Dannegger (Gast)


Lesenswert?

Beim AVR-GCC geht das mit:

void signame (void) _attribute_ ((naked));

siehe signal.h

Aber wirklich nur, wenn es garnicht anders geht !!!

Man muß dann nach jeder Änderung des Quelltextes oder der
Compilereinstellungen überprüfen, ob man auch wirklich alle benutzten
Register manuell gesichert hat !


Nochn Tip für die ganz eiligen:

Der AVR-GCC benutzt fast nie R2..R11, d.h. die kann man dann in dem
schnellen Interrupt nehmen, das spart richtig:

movw r3:r2, r17:16
... mache was mit r17, r16
movw r17:r16, r3:r2

sind nur 2 Zyklen gegenüber 8 Zyklen bei PUSH/POP

Aber dann auch im gesamten Listing prüfen ob R2..R11 nicht doch
irgendwo anders auftauchen !


Peter

von Jörg Wunsch (Gast)


Lesenswert?

> Aber dann auch im gesamten Listing prüfen ob R2..R11 nicht doch
> irgendwo anders auftauchen !

Besser wäre es, stattdessen dafür eine festzugewiesene
Registervariable zu deklarieren (... asm("r2");).  Dann kann man
sich
u. U. auch gleich das Inline-Asm-Gewurschtel sparen und die Zuweisung
selbst auch noch in C machen.

Aber auch hier: alle Module müssen mit dieser Deklaration übersetzt
worden sein.  Besonders für die Bibliothek ist das natürlich
normalerweise nicht der Fall, ich habe keine Ahnung, ob wirklich keine
der Bibliotheksfunktionen je eins dieser Register benutzt oder (in
einer anderen Compilerversion) mal benutzen könnte.

von Peter D. (peda)


Lesenswert?

"Besser wäre es, stattdessen dafür eine festzugewiesene
Registervariable zu deklarieren"


Nützt bloß nichts.

Der Compiler gibt keinerlei Fehlermeldung aus, wenn er auch das
Register benutzen will.

Um das Überprüfen des Listings nach jedem Compile kommt man also nicht
drumrum.

Daß R2..R11 in der Regel oft unangetastet bleiben, ist noch lange keine
Garantie dafür.


Peter

von Jörg Wunsch (Gast)


Lesenswert?

> Der Compiler gibt keinerlei Fehlermeldung aus, wenn er auch das
> Register benutzen will.

Das wäre allerdings einen GCC-Bugreport wert.

von Peter Dannegger (Gast)


Lesenswert?

@Jörg Wunsch,

ich habs nochmal getestet:

Warnungen erhalte ich bei R18..R31.

Keine Warnungen erhalte ich bei R0..R17.

Frei sind aber nur R2..R11.

Deklariert habe ich sie im h-File, z.B.:

register unsigned char PCMValB1 asm("r11");


Peter

von Jörg Wunsch (Gast)


Lesenswert?

> Keine Warnungen erhalte ich bei R0..R17.

> Frei sind aber nur R2..R11.

Eigentlich sollten R2...R17 frei sein.  Dass er R0 und R1 nicht warnt,
ist ganz sicher ein Bug, aber einer spezifisch für den AVR-Port des
GCC (der für R0 und R1 Sonderbedeutungen zugewiesen hat).

Für R2...R17 würde ich erwarten, dass bereits der generische Code von
GCC diese Register einfach nicht anderweitig benutzt.

Wie gesagt, das kann nur funktionieren, sofern in der jeweiligen
compilation unit (also das aktuelle C-File plus alle Includes) die
entsprechende ... asm("rXX")-Deklaration auch wirksam war.  Im
fertigen Binary sollte man dabei insbesondere natürlich drauf gucken,
dass nichts aus der avr-libc (die ja ohne derartige Deklarationen
compiliert worden ist) eventuell das entsprechende Register benutzt
(oder man arbeitet gleich mit einer privaten Kopie der avr-libc, die
ebenfalls die entsprechende Deklaration benutzt).  Wenn du das mit
einem kurzen Beispiel nachweisen kannst, dass der GCC innerhalb eines
Quellfiles trotz ... asm("r2")-Deklaration trotzdem noch R2 in
seinem
eigenen Code benutzt, dann halte ich das für einen berichtenswerten
Bug (zumal sehr wahrscheinlich einen nicht-AVR-spezifischen).

von Peter Dannegger (Gast)


Lesenswert?

@Jörg,

daran lags.

Ich Depp hatte natürlich nur dort included, wo ich sie benutzte.

Man muß sie in alle C-Files includieren und dann versucht der Compiler
ohne sie auszukommen bzw. wird wohl auch eine Warnung erzeugen, wenn
ihm das nicht gelingt.

Der Bug betrifft also doch nur R0 und R1.

Schön ist auch, daß ich nun R16 und R17 benutzen kann, das macht meine
nackten Interrupts nochmal nen Zacken schneller.


Allerdings liefert mir der neue WINAVR nun eine Warnung:

I2C.C:49: warning: `i2c_interrupt' appears to be a misspelled signal
handler

Der Grund ist, daß ein I2C-Interrupt ja langsam ist, aber sich nicht
selber löscht. D.h. ich brauche einen nackten Interrupt, der nur den
I2C disabled und dann erst den eigentlichen Handler aufruft.

Damit nun dieser Handler aber auch alle seine Register selber sichert,
muß er als signal deklariert sein, ist aber keinem Interruptvektor
zugeordnet:

void i2c_interrupt ( void ) _attribute_ ((signal));
void i2c_interrupt ( void )
{
...
}

Aber irgendwas paßt dem WINAVR daran nicht und er macht besagte
Warnung.

Sollte ich ihn vielleicht doch einem unbenutzten Interrupt zuordnen,
z.B. dem SPM_READY ?


Peter

von Jörg Wunsch (Gast)


Lesenswert?

Gut, dass das erstmal grundlegend funktioniert.  Für R0/R1 kannste ja
ruhig mal einen Bugreport öffnen.

Die Warnung ist die ,,Notbremse'', falls sich jemand mit den SIG_*-
Namen verschreibt.  Das ist früher sehr häufig passiert (und führt
dann zu einem spontanen Neustart ab Adresse 0), und irgendwann fiel
dann Ted Roth ein genialer Trick ein, wie man den GCC selbst für
bestimmte Dinge eigene Warnungen erstellen lassen kann.  Das
einfachste ist natürlich, du lebst mit der Warnung (du weißt ja, warum
sie kommt und dass sie bei dir harmlos ist).  Ansonsten bliebe noch
die ,,Eigenerfindung'' eines ,,Interruptvektors''.  Meiner Meinung
nach werden die __attribute__((signal))-Routinen darauf überprüft, ob
sie mit _vector anfangen und auf einer Zahl enden.  Mit __vector_999
oder so solltest du auf der sicheren Seite sein.

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.