www.mikrocontroller.net

Forum: Compiler & IDEs fehlerhafte Registerzuordnung bei Inline-Assembler


Autor: Schawwi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen!

Ich habe ein echtes Problem bei der Registerzuordnung von einem 
Inline-Assembler-Schnipsel. Und zwar muss ich aus Zeitgründen die 
Befehlszeile
osc_x0 = ( ( (signed long)osc_coeff * (signed long)osc_x1 ) >> 14) - ((signed long)osc_x2);
in Assembler optimieren. Hierfür verwende ich grundlegend das 
Application Note 201 "Using the AVR Hardware Multiplier" von Atmel, was 
ich noch ein wenig für mein Problem optimiert habe.

Der Inline-Assembler-Schnipsel sieht im C-Code wie folgt aus:
    char asm_temp1;
    char asm_temp2;
    asm volatile (
      "clr   %A[temp1]                    \n\t" // [osc_x0 + temp] = osc_coeff * osc_x1
      "muls  %B[osc_x1],    %B[osc_coeff] \n\t"
      "movw  %A[osc_x0],    r0            \n\t"
      "mul   %A[osc_x1],    %A[osc_coeff] \n\t"
      "mov   %A[temp2],     r1            \n\t"
      "mulsu %B[osc_x1],    %A[osc_coeff] \n\t"
      "sbc   %B[osc_x0],    %A[temp1]     \n\t"
      "add   %A[temp2],     r0            \n\t"
      "adc   %A[osc_x0],    r1            \n\t"
      "adc   %B[osc_x0],    %A[temp1]     \n\t"
      "mulsu %B[osc_coeff], %A[osc_x1]    \n\t"
      "sbc   %B[osc_x0],    %A[temp1]     \n\t"
      "add   %A[temp2],     r0            \n\t"
      "adc   %A[osc_x0],    r1            \n\t"
      "adc   %B[osc_x0],    %A[temp1]     \n\t"
      "rol   %A[temp2]                    \n\t" // [osc_x0 + temp] = [osc_x0 + temp] << 1
      "rol   %A[osc_x0]                   \n\t"
      "rol   %B[osc_x0]                   \n\t"
      "rol   %A[temp2]                    \n\t" // [osc_x0 + temp] = [osc_x0 + temp] << 1
      "rol   %A[osc_x0]                   \n\t"
      "rol   %B[osc_x0]                   \n\t"
      "sub   %A[osc_x0],    %A[osc_x2]    \n\t" // osc_x0 = osc_x0 - osc_x2
      "sbc   %B[osc_x0],    %B[osc_x2]    \n\t"
      "clr   __zero_reg__                     "
        : [osc_x0] "=r" (osc_x0), [temp1] "=r" (asm_temp1), [temp2] "=r" (asm_temp2)
        : [osc_coeff] "a" (osc_coeff), [osc_x1] "a" (osc_x1), [osc_x2] "r" (osc_x2)
        : "r0", "r1"
    );

Wenn ich das ergebnis jedoch Disassembliere, fällt deutlich auf, dass 
manche Register zwei Variablen zugeordnet werden.
LDS     R20,0x01A5
LDS     R21,0x01A6
LDS     R18,0x01A7
LDS     R19,0x01A8
LDS     R24,0x01A9
LDS     R25,0x01AA
CLR     R25
MULS    R19,R21
MOVW    R20,R0
MUL     R18,R20
MOV     R24,R1
MULSU   R19,R20
SBC     R21,R25
ADD     R24,R0
ADC     R20,R1
ADC     R21,R25
MULSU   R21,R18
SBC     R21,R25
ADD     R24,R0
ADC     R20,R1
ADC     R21,R25
ROL     R24
ROL     R20
ROL     R21
ROL     R24
ROL     R20
ROL     R21
SUB     R20,R24
SBC     R21,R25
CLR     R1
STD     Y+3,R21
STD     Y+2,R20

Und zwar sieht die Zuordnung ja so aus:

osc_x0 = R20:R21
temp1 = R25
temp2 = R24
osc_coeff = R20:R21
osc_x1 = R18:R19
osc_x2 = R24:R25

Wie man sieht, werden die Ausgabe-Variablen mit gleichen Registern 
verknüpft, die schon für Eingabe-Variablen verwendet werden.

Mache ich hier etwas falsch? Muss ich noch irgendwelche Befehle 
hinzufügen? Oder ist das ein Fehler im GCC? Ich verwende das AVR Studio 
4.13, Build 528.
Wenn die Variablen eindeutige Register erhalten, funktioniert übrigens 
auch der Code. Also der stimmt.

Danke für Antworten im Voraus!

Christian

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schau mal da rein: 
http://www.roboternetz.de/wissen/index.php/Inline-...
 Tipp: du suchst wahrsceinlich "&"

hth. Jörg

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

Bewertung
0 lesenswert
nicht lesenswert
Schawwi wrote:

> Wie man sieht, werden die Ausgabe-Variablen mit gleichen Registern
> verknüpft, die schon für Eingabe-Variablen verwendet werden.

Ja, du hast dem Compiler ja auch nicht gesagt, dass er das nicht
darf.  Das ist also `by design' so.

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

Bewertung
0 lesenswert
nicht lesenswert
p.s.: Falls du das in eine separate Funktion auslagern kannst, wäre
es wahrscheinlich in einer reinen Assemblerdatei besser aufgehoben.
Inline-Assembler-Code hat eine Tendenz, "write-only" zu sein.

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Jörg,
wie würde denn ein solches Gerüst aussehen, d.h eine im C Code genutzte 
Funktion in einer seperaten Datei in assembler definieren. Bin auf dem 
Gebiet recht frisch und freue mich über Hilfe.
Gruss,
Christoph

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

Bewertung
0 lesenswert
nicht lesenswert
Du brauchst kaum ein ,,Gerüst''.  Alles, was du brauchst, ist das
ABI, das findest du in der avr-libc-FAQ:

http://www.nongnu.org/avr-libc/user-manual/FAQ.htm...

Wenn du mir mal normalen Assemblercode dafür aufschreibst (möglichst
mit symbolischen Namen), schreib ich dir den Prolog und Epilog dafür.

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für dein Angebot. Momentan schaut der Teil meines Programmes wie 
folgt aus:

void set(base *cp)
{
   unsigned int *ptr_1 = cp->start_ptr;
   unsigned int *ptr_2 = cp->end_ptr;

    _asm_ __volatile__("mov   r2,  %0           \n\t"
                         ....
                         ......


                                   :
                                   :"r"(ptr_1),"r"( ptr_2)
                                   :"r2","r3","r4","r5","r6","r7"

                                  );
}

Hierbei handelt es sich um eine Speichermanipulation Routine. Anfangs- 
und Endadresse liegen in einer struct namens 'base' vor. Da dieser Teil 
relative zeitkritisch ist, habe ich mich für die Implemtierung in inline 
assembler entschieden. Was das ganze natürlich beschleunigen würde wäre 
der Verzicht auf die Nutzung einer Funktion und den Code direkt 
einzubetten. Jedoch kommt diese Routine an recht vielen Programmstellen 
vor.
Habe ich Dich richtig verstanden, dass es möglich ist die komplette 
Routine in assembler zu schreien und diese via headerdatei einzubinden ?
Die Zielplatform ist übrigens kein Atmel sondern ein NiosII. 
Beispeilcode kann sich jedoch auch gerne auf einen AVR beziehen.
Vielen Dank, Christoph

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Christoph (Gast)

>Die Zielplatform ist übrigens kein Atmel sondern ein NiosII.

BOOAAHHH! Auf nem FPGA so einen Krampf veranstalten? Mann, da hätte ich 
dreimal nen kleinen Coprozessor in Hardware drangehängt und gut ist. 
Wenn der dann immer noch zu langsam ist gehts sowieso nicht.

>Beispeilcode kann sich jedoch auch gerne auf einen AVR beziehen.

Nützt wenig bis gar nix. Denn diese Tricks sind sehr Prozessor- und 
Compiler spezifisch.

MFG
Falk

Autor: Christoph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Falk

Krampf ist dies keinewegs. Bei diesem Projekt handelt es sich um ein 
komplettes SOPC mit einem schnellen Hardware Frontend und einem NiosII 
der als PC Schnittstelle agiert. Er empfängt die prozessierten Daten des 
Frontends, bereitet sie auf und gibt sie, wenn der PC fragt an diesen 
weiter. Mit zeitkritisch habe ich vielleicht etwas übertrieben. Aber 
umso mehr Luft, umso besser. Jedoch habe ich die Erfahrung gemacht das 
NiosII gcc Compiler nicht wirklich immer netten Code ausspuckt, was 
nicht verwunderlich ist in Anbetracht der konfigurierbaren Zielplatform. 
Daher kam ich auf den Weg gewisse Codefragmente in assembler zu 
implementieren, was der Nios nun auch spielend schafft. Eine Coprozessor 
Lösung wäre in meinem konkreten Fall sicherlich übertriebene Härte. Was 
die Portierbarkeit von AVR Lösungen angeht kann ich nur sagen, dass ich 
schon vielerlei 'Kniffs' (die ich unter anderem auch hier gefunden 
habe), wenn auch sicherlich in leicht abgewandelter Form auf dem Nios 
zum Laufen bekommen habe. Was mich nun eigentlich interessiert ist wie 
schon oben aufgeführt das 'Auslagern und Definieren' einer im C 
Quellcode benutzten assembler Funktion.
Gruss,
Christoph

Autor: Schawwi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo und danke für die Antworten!

Also amn muss erst mal durch diese Kürzel für die Registernutzung 
durchsteigen! :-)

Aber jetzt funktioniert es wunderbar.

Christian

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.