Hallo,
Habe in meinem Programm die Zykluszeit gemessen und möchte jetzt weiter
mittels inline Assembler optimieren.
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include"Zykluszeit.h"
4
5
intmain(void)
6
{
7
//Ein-Ausgabe Init
8
DDRB=255;
9
DDRD|=(1<<DDD0);//Zykluszeit PIN
10
11
while(1)
12
{
13
asmvolatile(
14
"in r16, PORTB"
15
:"d,M"
16
);
17
}
18
return1;
19
}
Compiler Fehler:
Error 1 expected '(' before ')' token
Woran liegt das?
Prinzipiell verstehe ich diese ganzen Contraints und In und Out List
nicht. Wieso so kompliziert?
Gibt es eine Möglichkeit normalen Assembler inline zu codieren?
Die benutzten Register sichere ich via push pop und die Sache hat sich.
Da braucht es doch keine %A0 oder %Dn oder was weiß ich was das heißt...
> Gibt es eine Möglichkeit normalen Assembler inline zu codieren?> Die benutzten Register sichere ich via push pop und die Sache hat sich.
Sicher, kannst du machen (oben machst du es aber nicht).
Der Vorteil der "was weiß ich was das heißt" ist, dass du dem C-Compiler
die Hauptarbeit gibst.
Angenommen du willst den PORTB (wieso eigentlich PORTB und nicht PINB?)
nicht dem Register r16 zuweisen, sondern du willst den PINB Wert einer
deiner Variablen zuweisen.
Bei deiner Einfachstmethode weisst du bereits nicht, wie deine Variable
anzusprechen ist... dafür brauchst du die Listen.
http://www.rn-wissen.de/index.php/Inline-Assembler_in_avr-gcchttp://www.nongnu.org/avr-libc/user-manual/inline_asm.html
Krapao schrieb:> Sicher, kannst du machen (oben machst du es aber nicht).
1
asmvolatile("push r16");
2
asmvolatile("in r16, PORTD");
3
asmvolatile("pop r16");
Aber so funktioniert es auch nicht.
Wie normal soll ich es denn noch schreiben?
Krapao schrieb:> (wieso eigentlich PORTB und nicht PINB?)
Eigentlich PORTD. Ich will keinen Eingangssignal lesen sondern einfach
den PORT Zustand erfragen.
Krapao schrieb:> sondern du willst den PINB Wert einer> deiner Variablen zuweisen.
dann weise ich diesen vor mein Inline Assembler Code zu?
Wo liegt das Problem?
AndyS schrieb:> Prinzipiell verstehe ich diese ganzen Contraints und In und Out List> nicht. Wieso so kompliziert?
Weil dir der Compiler sonst allzu leicht einen Strich durch die Rechnung
macht. Er muss wissen, welche Register von der Assembler-Sequenz in
welcher Form verwendet oder verändert werden.
A. K. schrieb:> Weil dir der Compiler sonst allzu leicht einen Strich durch die Rechnung> macht. Er muss wissen, welche Register von der Assembler-Sequenz in> welcher Form verwendet oder verändert werden.
Habe jetzt folgendes überlegt:
1
unsignedcharreg=0;
2
while(1)
3
{
4
unsignedcharport=PORTD;
5
asmvolatile("in %0 %1":"=d"(reg):"I"(port));
6
}
Wieso muss ich laut Tabelle
>in r,I input from I/O
für Input das Constraint I verwenden? I ist doch eine positive 6 Bit
Konstante? PORTD ist aber 8 Bit breit, dh ich kann nicht alle bits darin
darstellen.
Beim Kompilieren bekomme ich einen Compiler Fehler:
Warning 1 asm operand 1 probably doesn't match constraints
Error 2 impossible constraint in 'asm'
Ich habe auch schon _SFR_IO_ADDR(PORTD) probiert, jedoch nicht erkannt.
Eine Theoretische Frage die bei beiden Tutorials nicht direkt
herauskommt: Wird jetzt ein Register mit dem Variablennamen reg belegt
und zeigt diese 8 Bit Variable genau auf dieses eine Register in welches
ich den PORT lade? Wenn ja, kann ich dann festlegen in welches Register
(r16-r31) diese Variable kommt oder lasst mich der Compiler insofern
keine Entscheidung treffen?
AndyS schrieb:> für Input das Constraint I verwenden? I ist doch eine positive 6 Bit> Konstante? PORTD ist aber 8 Bit breit, dh ich kann nicht alle bits darin> darstellen.
Es geht nicht um die Breite des Ports, sondern um die Breite der Adresse
des Ports. Und die beträgt beim IN Befehl 6 Bits.
> Wenn ja, kann ich dann festlegen in welches Register> (r16-r31) diese Variable kommt oder lasst mich der Compiler insofern> keine Entscheidung treffen?
Überlass das lieber dem Compiler. Auf R16-R31 hast du es ja schon selbst
eingeschränkt (weshalb?).
Compiler Fehler:
Error 1 `,' required
Wenn ich auf den Fehler doppelklicke, dann passiert nichts.
Es fehlt aber kein ",".
A. K. schrieb:> Auf R16-R31 hast du es ja schon selbst> eingeschränkt (weshalb?).
Weil das immer noch Assemblercode sein soll und ich nicht will dass das
alles der Compiler einteilt wie er möchte.
AndyS schrieb:>> Sicher, kannst du machen (oben machst du es aber nicht).> asm volatile("push r16");> asm volatile("in r16, PORTD");> asm volatile("pop r16");> Aber so funktioniert es auch nicht.
Wäre ja auch Blödsinn.
1
(void)PORTD;
hätte den gleichen Effekt: PORTD wird gelesen, aber das Ergebnis
danach verworfen.
Vielleicht erzählst du uns ja lieber, was du auf diese Weise
optimieren willst. Das simple Einlesen eines Portregisters
bekommst du schließlich auch in deinem Assemblercode nicht schneller
hin als der C-Compiler (sofern man ihm die Optimierung einschaltet
natürlich).
Jörg Wunsch schrieb:> Vielleicht erzählst du uns ja lieber, was du auf diese Weise> optimieren willst. Das simple Einlesen eines Portregisters> bekommst du schließlich auch in deinem Assemblercode nicht schneller> hin als der C-Compiler (sofern man ihm die Optimierung einschaltet> natürlich).
Das war nur ein Beispiel.
Ich wollte zwei Abfragen und Ausgaben in assembler abfragen und wollte
den Unterschied zum C Compiler messen.
AndyS schrieb:> Prinzipiell verstehe ich diese ganzen Contraints und In und Out List> nicht. Wieso so kompliziert?
Die Leistungsfähigkeit dieser Technik erschliesst sich erst dann, wenn
man sich von klassischer Assembler-Programmung löst, und nicht zig oder
hunderte Zeilen Assembler-Quelltext in C Code einzubetten versucht.
In GCC verwendet man Assemblercode beispielsweise in Form kleiner als
eigenständige Inline-Funktion formulierter Häppchen aus einer oder
weniger Zeilen. So kann man beispielsweise bestimmte Maschinenbefehle
elegant für C verfügbar machen, wie Rotation oder Bitreverse.
Die Programmstuktur und den übrigen Code belässt man in C und ruft darin
diese Häppchen auf. Eine eigene Registerverwaltung wird so unnötig und
man kann die Vorzüge des C Optimizers auch für diese Assembler-Häppchen
nutzen.
Man schreibt also keine Assembler-Funktion, die eine
Bitreverse-Operation auf ein ganzes Array durchführt, sondern man
schreibt eine C-Funktion, die dies in normalem C durchführt und darin
die Bitreverse-Operation nutzt. Und mit etwas Glück erlebt man
vielleicht, dass der Compiler das besser optimiert, als man es selbst
geschrieben hätte, weil er auf die Schleife unrolling anwendet und man
hinterher im Code nicht einen Bitreverse-Befehl findet, sondern 4
verzahnte.
AndyS schrieb:> Ich wollte zwei Abfragen und Ausgaben in assembler abfragen und wollte> den Unterschied zum C Compiler messen.
Sowas muss man nicht "messen", sonder man schaut sich den generierten
Assemblercode an und zählt die Takte. Die stehen im Manual drin.
Parallel schreibst du dir deine Assemblerimplementierung auf und
zählst dort.
Ansonsten gilt: Never start optimizing before you have profiled it.
Jörg Wunsch schrieb:> AndyS schrieb:>> Ich wollte zwei Abfragen und Ausgaben in assembler abfragen und wollte>> den Unterschied zum C Compiler messen.>> Sowas muss man nicht "messen", sonder man schaut sich den generierten> Assemblercode an und zählt die Takte. Die stehen im Manual drin.>> Parallel schreibst du dir deine Assemblerimplementierung auf und> zählst dort.>> Ansonsten gilt: Never start optimizing before you have profiled it.
Gerade zum Profilen sind solche Sequenzen aber nützlich.
Hier hausbacken:
1
#ifndef TICKS_H
2
#define TICKS_H
3
4
#include<avr/io.h>
5
6
#ifdef __ASSEMBLER__
7
.macroticksn
8
lds0,TCNT1
9
ststick\n,0
10
lds0,TCNT1+1
11
ststick\n+1,0
12
.endm
13
14
#else /* !__ASSEMBLER__ */
15
16
externuint16_tvolatiletick1;
17
externuint16_tvolatiletick2;
18
19
staticinline__attribute__((always_inline))
20
voidticks(charn)
21
{
22
if(n==1)cli();
23
__asmvolatile(
24
"lds __tmp_reg__, %1""\n\t"
25
"sts tick%0, __tmp_reg__""\n\t"
26
"lds __tmp_reg__, %1+1""\n\t"
27
"sts tick%0+1, __tmp_reg__"
28
::"n"(n),"n"(&TCNT1)
29
:"memory");
30
if(n==2)sei();
31
}
32
33
#endif /* __ASSEMBLER__ */
34
#endif /* TICKS_H */
Und natürlich muss man aufpassen, was man misst. Die Sequenz braucht
selber schon 8 Ticks. Und wenn man mit volatile-Variablen unterwegs ist,
brauchen deren Zugriffe u.U deutlich länger als das, was man eigentlich
messen will ;-)
Obige Sequenz kommt zB zum Einsatz bei Performance-Test für neue
libgcc-Algorithmen.