Hallo zusammen!
Ich habe ein echtes Problem bei der Registerzuordnung von einem
Inline-Assembler-Schnipsel. Und zwar muss ich aus Zeitgründen die
Befehlszeile
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:
Wenn ich das ergebnis jedoch Disassembliere, fällt deutlich auf, dass
manche Register zwei Variablen zugeordnet werden.
1
LDS R20,0x01A5
2
LDS R21,0x01A6
3
LDS R18,0x01A7
4
LDS R19,0x01A8
5
LDS R24,0x01A9
6
LDS R25,0x01AA
7
CLR R25
8
MULS R19,R21
9
MOVW R20,R0
10
MUL R18,R20
11
MOV R24,R1
12
MULSU R19,R20
13
SBC R21,R25
14
ADD R24,R0
15
ADC R20,R1
16
ADC R21,R25
17
MULSU R21,R18
18
SBC R21,R25
19
ADD R24,R0
20
ADC R20,R1
21
ADC R21,R25
22
ROL R24
23
ROL R20
24
ROL R21
25
ROL R24
26
ROL R20
27
ROL R21
28
SUB R20,R24
29
SBC R21,R25
30
CLR R1
31
STD Y+3,R21
32
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
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.
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.
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
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.html#faq_reg_usage
Wenn du mir mal normalen Assemblercode dafür aufschreibst (möglichst
mit symbolischen Namen), schreib ich dir den Prolog und Epilog dafür.
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
@ 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
@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
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