Datum:
Hi, ich wäre sehr dankbar, wenn mir jemand sagen könnte, wie der Assambler-Code hinter folgendem Makro aussieht, bzw. wie der gcc dieses Makro interpretieren würde:
#define mfmsr() ({ unsigned int _rval; \ __asm__ __volatile__ ( \ "mfs\t%0,rmsr\n" : "=d"(_rval) \ ); \ _rval; \ }) |
Das Makro stammt aus einem Header für Xlinx Microblaze. Da ich mit dieser Syntax wirklich nicht viel anfangen kann, würde mir eine detailierte Erklärung wirklich weiterhelfen. (Was bedeuten in diesem Kontext
__volatile__ |
,
\t%0 |
,
\n" : "=d"(_rval) |
) Schon mal Danke im voraus. Gruß Maria
Datum:
#define mfmsr() |
-> Definiert das Makro mfmsr (move from machine status register)
unsigned int _rval; |
-> Definition einer temporären Variable
__asm__ |
-> Jetzt kommt Inlineassemblercode.
__volatile__ |
-> Der Compiler darf den nachfolgenden Assemblercodeabschnitt nicht
wegoptimieren oder ihn aus einer Schleife vor die Schleife
verschieben."mfs\t%0,rmsr\n" : "=d"(_rval) \ |
-> Beschreibung des zu generierenden Assemblercodes
mfs\t%0,rmsr\n |
-> Pattern für Assemblerbefehl. Für %0 wird etwas eingesetzt, was
nach dem Doppelpunkt genauer spezifiziert wird.
mfs
|
-> move from special purpose register
rmsr
|
-> register "machine status register""=d"(_rval) |
-> Spezifikation dafür, was für %0 eingesetzt werden soll
=
|
-> Gibt an, dass %0 ein Output ist.
d
|
-> Gibt an, dass %0 ein General-Register sein soll. Der Compiler
wird also für %0 eines der Register r0—r31 einsetzen.
(_rval)
|
-> In dieser C-Variable soll der Inhalt von %0 am Schluss landen.
Ist _rval eine Registervariable, wird der Compiler dieses
Register für %0 einsetzen, so dass der msr-Befehl den Status
direkt in _rval schreiben kann. Liegt _rval im RAM, wird
der Compiler einen zusätzlichen Assemblerbefehl generieren,
der den Inhalt des ausgewählten Registers (r0—r31) an die
entsprechende RAM-Adresse kopiert.
_rval; |
-> Rückgabewert des vom Makro erzeugten C-Blocks. Alles zusammengenommen macht das Makro also den Inhalt des Machine- Statusregisters für das C-Programm verfügbar. Die Anweisung
status = mfmsr(); |
schreibt den Inhalt des Statusregisters in die Variable status. Noch viel mehr zu diesem Thema steht im GCC-Manual in diesen Kapiteln: http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html http://gcc.gnu.org/onlinedocs/gcc/Constraints.html http://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html http://gcc.gnu.org/onlinedocs/gcc/Explicit-Reg-Vars.html
Datum:
Yalu X. schrieb: > __volatile__ > > -> Der Compiler darf den nachfolgenden Assemblercodeabschnitt nicht > [...] aus einer Schleife vor die Schleife verschieben. Doch, darf er wohl. Es darf nicht mit anderen volatile-Operationen vertauscht werden. Mit "normalen" Operationen darf es vertauscht werden — sofern die Abhängigkeiten dies zulassen. Dies schliesst auch Speicherzugriffe ein sofern diese nicht volatile sind.
Datum:
Johann L. schrieb: >> __volatile__ >> >> -> Der Compiler darf den nachfolgenden Assemblercodeabschnitt nicht >> [...] aus einer Schleife vor die Schleife verschieben. > > Doch, darf er wohl. Sicher? > Es darf nicht mit anderen volatile-Operationen vertauscht werden. > > Mit "normalen" Operationen darf es vertauscht werden — sofern die > Abhängigkeiten dies zulassen. Schon richtig, aber das von mir Geschriebene widerspricht dem ja auch nicht. Ich war eigentlich der Auffassung, dass ein asm mit volatile, das in einer sich 100mal drehenden Schleife steht, auch tatsächlich 100mal ausgeführt wird. Ohne volatile kann der Compiler das asm vor die Schleife stellen, wenn der Input nicht von Berechnungen in der Schleife abhängt. Liege ich da falsch? Zumindest in diesem Beispiel verhält sich der Compiler genau nach meinen Vorstellungen:
uint8_t x; void test_volatile(void) { uint8_t i; for(i=0; i<10; i++) { __asm__ __volatile__ ("ldi %0,1" : "=d" (x)); } } |
Mit __volatile__:
test_volatile: ldi r24,lo8(10) .L2: ldi r25,1 ; <-- in der Schleife sts x,r25 subi r24,lo8(-(-1)) brne .L2 ret |
Ohne __volatile__:
test_volatile: ldi r24,lo8(10) ldi r25,1 ; <-- vor der Schleife .L2: sts x,r25 subi r24,lo8(-(-1)) brne .L2 ret |
Anmerkung: Das Beispiel ist für den AVR, da ich mich mit dem Microblaze nicht auskenne. Das sollte aber in diesem Zusammenhang nichts ausmachen.
Datum:
Vielen Dank, für die wirklich ausführlichen und vor allem hilfreichen Antworten/Links. Gruß Maria
Datum:
Yalu X. schrieb: > Johann L. schrieb: >>> __volatile__ >>> >>> -> Der Compiler darf den nachfolgenden Assemblercodeabschnitt nicht >>> [...] aus einer Schleife vor die Schleife verschieben. >> >> Doch, darf er wohl. > > Sicher? Ja :-) >> Es darf nicht mit anderen volatile-Operationen vertauscht werden. >> >> Mit "normalen" Operationen darf es vertauscht werden — sofern die >> Abhängigkeiten dies zulassen. > > Schon richtig, aber das von mir Geschriebene widerspricht dem ja auch > nicht. Ich war eigentlich der Auffassung, dass ein asm mit volatile, das > in einer sich 100mal drehenden Schleife steht, auch tatsächlich 100mal > ausgeführt wird. Ja, das stimmt. Die Seiteneffekte müssen natürlich die gleichen sein. Hier ein etwas komplexeres Beispiel:
#include <stdint.h> uint8_t x; uint8_t y; void test_volatile (void) { uint8_t i; for (i=0; i < 10; i++) { y = i; __asm__ __volatile__ ("ldi %0,1" : "=d" (x)); } } |
und übersetzt mit avr-gcc-4.7.1 -O3
test_volatile: ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,1 ldi r24,lo8(9) sts y,r24 ldi r24,1 sts x,r24 ret |
Was hier nicht geht, ist das Schreiben von y mit dem von x zu vertauschen. Allerdings nicht wegen dem volatile, sondern wegen den Aliasing-Rules. Bestübde der Seiteneffekt des asm jedoch in Umkonfigurieren der Hardware, zB Setzen von Segmentregistern die die Speicherzugriffe beeinflussen, hätte man mit dem obigem Code ein Problem, denn x bzw y hängen nicht vom asm ab.