Forum: Compiler & IDEs AVR inline assembler / register Problem


von SuperUser (Gast)


Lesenswert?

Hallo,

ich habe ein Problem mit dem inline assembler von gcc für AVR. Ich
möchte aus Geschwindigkeitsgründen für zwei Pointer vordefinierte
Register benutzen und die dann direkt aus dem inline assembler
ansprechen. (im Beispiel val_ptr und ref_ptr)

Funktioniert auch, wenn ich die Pointer (val_ptr und ref_ptr) in C für
irgendwelche Operationen benutze. Setze ich die Pointer ausschliesslich
im inline assembler ein, werden die Zuweisungen
  val_ptr=val_tab;
  ref_ptr=ref_tab;
vom Compiler wegoptimiert und der Assemblercode greift ins leere.

Hat jemand eine Idee, wie ich dem Compiler sagen kann, dass ich die
Pointer-Zuweisungen im Assembler Teil benötige und er sie gefälligst
nicht wegoptimieren soll?

Testcode:
void test(void)
{
  int8_t val_pnt;
  int32_t cor;
  int8_t ref_tab[16];
  static int8_t val_tab[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

  register int8_t* val_ptr asm("r22");  // use r22,r23 for fast
access
  register int8_t* ref_ptr asm("r24");  // use r24,r25 for fast
access

  val_ptr=val_tab;  // nicht wegoptimiert, da Parameter bei Übergabe
  ref_ptr=ref_tab;  // wegoptimiert, da in C nicht benutzt...

  asm volatile(
        "mov %A1,__zero_reg__"      "\n\t"
        "mov %B1,__zero_reg__"      "\n\t"
        "mov %C1,__zero_reg__"      "\n\t"
  "mov %D1,__zero_reg__"      "\n\t"
  "mov r16,r22"               "\n\t"
  "mov r17,r24"               "\n\t"
        "ld r16, %a3"               "\n\t"
  "ld r17, %a4"               "\n\t"
        : "=&r" (val_pnt), "=r" (cor)
        : "0" (val_pnt), "e" (val_ptr), "e" (ref_tab)
  : "r16", "r17"
    );
}

von Benedikt (Gast)


Lesenswert?

Schreib die Funktion am besten komplett in Assembler und binde sie ein
(*.S Datei).
Ich hasse den Inline Assembler mit den ganzen Ersetzungen für die
Register und Variablen.

von SuperUser (Gast)


Lesenswert?

Hallo Benedikt,

leider handelt es sich um eine interrupt service routine mit einer
zeitintensiven Berechnung - die ich in assembler schreiben muss, da C
zu langsam ist - sowie einer komplexen State-Machine - die ich lieber
in C schreiben möchte.

Insgesamt habe ich aber nur 300 Takte, d.h. ich möchte auf Overhead für
function call, Parameter Übergabe etc. verzichten.

Die Statemachine in C wird relativ gut compiliert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> leider handelt es sich um eine interrupt service routine mit einer
> zeitintensiven Berechnung - die ich in assembler schreiben muss, ...

Genau da greift doch aber Benedikts Tip wirklich gut: schreib sie
komplett als Assemblerdatei, statt dich mit dem inline-Assembler
rumzuquälen.  Nur die ISR sollst du in die Assemblerdatei legen, den
Rest kannst du doch gut in C belassen.

Ansonsten natürlich das Übliche bei der Kommunikation zwischen
verschiedenen Code-Pfaden (also Interrupt-Kontext vs. Hauptprogramm):
die Variablen müssen `volatile' deklariert werden, damit der Compiler
weiß, dass sie sich außerhalb des aktuellen Pfades ändern können bzw.
dort abgefragt werden können.  Zwar wirst du für "volatile register"
eine Warnung vom Compiler bekommen, aber die kannst du ignorieren.  In
deinem Falle ist es wohl wirklich genau das, was du willst.

von peter dannegger (Gast)


Lesenswert?

@SuperUser

"... interrupt service routine mit einer zeitintensiven Berechnung
..."

Da schrillen bei mir sofort die Alarmglocken.

"Interrupt" und "Zeitintensiv" vertragen sich überhaupt nicht
miteinander.


In der Regel ist es nämlich möglich, im Interrupt nur die Parameter zu
sichern und dann die eigentliche Berechnung in aller Ruhe in der
Mainloop zu machen.


Ich hab z.B. mal sowas verrücktes gesehen, im Multiplexinterrupt die
Digits jedesmal neu aus einer float-Zahl berechnen zu lassen statt
einmalig alle Digits zu berechnen, deren 7-Segment-Code im RAM zu
hinterlegen und den Multiplexinterrupt einfach nur das jeweilige Byte
ausgeben zu lassen.
Bei ersterem schwitzt die CPU mächtig und bei letzterem gähnt sie nur
ständig.


Peter

von SuperUser (Gast)


Lesenswert?

Hallo Peter,

das ist hier anders. Es läuft ein Filter in Realtime und burst mode.
Der Filteraufwand ist abhängig vom Zustand. Worst case bleiben 300
Clocks für den Interrupt. Ich schätze im Moment das ich 200Takte für
den worst case Filter brauche - wenn optimal in ASM implementiert.

Das mit den volatile register probiere ich mal aus, glaube aber nicht,
das das hilft. Der Compiler optimiert die Zuweisung ja weg, da der
Pointer in C ja nicht benötigt wird - sollte egal sein ob volatile oder
nicht oder?

von AndreasH (Gast)


Lesenswert?

> Das mit den volatile register probiere ich mal aus, glaube aber
> nicht, das das hilft.

Das ist ja schon wie Gotteslästerung :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Der Compiler optimiert die Zuweisung ja weg, da der Pointer in C ja
> nicht benötigt wird - sollte egal sein ob volatile oder nicht oder?

Du hast den Sinn von volatile einfach noch nicht begriffen.

von SuperUser (Gast)


Lesenswert?

Ich freue mich, wenn's mit volatile klappt, kann es leider hier nicht
ausprobieren - werde heute abend berichten.

Ich stelle mir das also so vor: Da ich die Register als volatile
deklariere, muss der Compiler davon ausgehen, dass die Register
ausserhalb der ISR noch verändert werden und sie also anlegen, obwohl
sie in der ISR - aus Sicht des Compilers - nicht verwendet werden.
Richtig? (Würde ich schon fast als Trick bezeichnen :-)

Nur, die Variable an sich sind eigentlich ausserhalb der ISR nicht
sichtbar, es sind lokale Variablen. Daher ist obiger Rückschluss
eigentlich nicht gültig - oder eine Sonderdefinition für Register?

von SuperUser (Gast)


Lesenswert?

Ich hatte gehofft, das es eine #pragma Anweisung oder ähnliches für den
Compiler gibt.

In der Art:
Do not remove this line of code

Ich muss mir nochmal die Zeit nehmen, mich durch die gcc Doku zu
quälen...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Als SuperUser solltest du das doch aber wissen, oder?  SCNR...

Der GCC benutzt keine #pragmas (außer dort, wo sie vom C-Standard
vorgeschrieben sind).

von SuperUser (Gast)


Lesenswert?

@Jörg,

SuperUser sind auch nicht mehr das was sie mal waren....

Ich hab's gerade getestet:

  volatile register int8_t* ref_ptr asm("r24");

bringt, wie befürchtet, gar nichts. Die Zuweisung

  ref_ptr=ref_tab;

wird weiterhin weg-optimiert :-(

Ich gebe jetzt auf und akzeptiere die zusätzliche Kopiererei erstmal.

von AndreasH (Gast)


Lesenswert?

Das volatile muss sich auch auf das was wegoptimiert wird beziehen.

Also ref_ptr.

Grüße
Andreas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bau mal eine compilierbare Datei und klebe sie als Attachment ins
Forum.

von SuperUser (Gast)


Lesenswert?

@AndreasH

du meinst:
register int8_t* volatile ref_ptr asm("r24"); ?

habe ich natürlich auch probiert, geht auch nicht :-(

-> ich habe mich aber bereits damit abgefunden und das Konzept geändert
(ein pointer weniger). Ich brauche also keine Lösung mehr. Vielen Dank
für die Hilfe!! (Bei Bedarf stelle ich aber trotzdem eine compilierbare
Datei zur Verfügung)

P.S:
Ich habe auch mal testweise den assembler code in eine eigene function
gelegt, allerdings ist der Overhead deutlich höher als bei inline
assembler.

von Rolf Magnus (Gast)


Lesenswert?

Dein Problem ist, daß ref_ptr und val_ptr nicht als Eingangsparameter
deines asm definiert sind. Deshalb weiß der Compiler nicht, daß die
dort gebraucht werden.

Übrigens: Wenn ich volatile register versuche, bringt mir der Compiler
immer eine Warnung, daß das nicht das tue, was ich will. Was es
stattdessen tut, sagt er aber leider nicht.

von SuperUser (Gast)


Lesenswert?

Hallo Rolf,

genau darum wollte ich spezielle Register für die Pointer, um mir die
Eingangsparameter zu sparen. Jeder Eingangsparameter wird nämlich noch
einmal kopiert (erster pointer ins Y-reg, zweiter ins Z-reg, dritter
weiss ich nicht ...)

Also lege ich die pointer auf bekannte Register und kann dann direkt
und schnell darauf zugreifen - so die Theorie. Funktioniert auch, aber
nur wenn die Pointer auch in C verwendet werden. Sonst sind sie
wegoptimiert....

von SuperUser (Gast)


Lesenswert?

Pardon, habe mich vertan, natürlich nicht Y-reg. sondern erster ins Z
zweiter ins X.

von Rolf Magnus (Gast)


Lesenswert?

> genau darum wollte ich spezielle Register für die Pointer, um mir
> die Eingangsparameter zu sparen. Jeder Eingangsparameter wird
> nämlich noch einmal kopiert (erster pointer ins Y-reg, zweiter ins
> Z-reg, dritter weiss ich nicht ...)

Passiert das nicht nur, wenn du als Parametertyp "e" wählst?
Aber man kann auch direkt Registernamen als Parameter nutzen. Siehe
avr-libc-Dokumentation oder die von gcc, z.B.:
http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Extended-Asm.html#Extended-Asm

von Tom (Gast)


Lesenswert?

Nur so eine Idee:
Wenn du dem gcc einen Parameter -ffixed-r22 mit gibst, dann sollte er
die Finger von diesem Register lassen. Dieses Register steht dann auch
nicht mehr für optimierten Code zur Verfügung, das ist der Nachteil.

Tom

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.