mikrocontroller.net

Forum: Compiler & IDEs AVR inline assembler / register Problem


Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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"
    );
}

Autor: Benedikt (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: peter dannegger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: AndreasH (Gast)
Datum:

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

Das ist ja schon wie Gotteslästerung :-)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: AndreasH (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das volatile muss sich auch auf das was wegoptimiert wird beziehen.

Also ref_ptr.

Grüße
Andreas

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bau mal eine compilierbare Datei und klebe sie als Attachment ins
Forum.

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: SuperUser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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....

Autor: SuperUser (Gast)
Datum:

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

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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/Extend...

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.