PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Inline Asm Rückgabe



FoCus
08.02.2006, 14:44
Hi,
muss in einer c-Funktion aus Zeitgründen Asm verwenden. Bisher musste ich hierfür allerdings noch nie einen Wert zurückgeben.

Problem:
Ich muss in einer Interruptroutine so schnell wie möglich Port D lesen.
Ich habe hierfür folgende Routine erstellt:


volatile unsigned char G_cLastData = 0xAA;

EXTINT_INTERRUPT_HANDLER(SIG_INTERRUPT0){
asm volatile(
"IN %0, %1" "\n\t"
: "=r" (G_cLastData) //Output
: "I" (_SFR_IO_ADDR(PORTD)) //Input
);
}


Das produziert folgenden Asm Code


EXTINT_INTERRUPT_HANDLER(SIG_INTERRUPT0){
d70: 1f 92 push r1
d72: 0f 92 push r0
d74: 0f b6 in r0, 0x3f ; 63
d76: 0f 92 push r0
d78: 11 24 eor r1, r1
d7a: 8f 93 push r24
d7c: cf 93 push r28
d7e: df 93 push r29
d80: cd b7 in r28, 0x3d ; 61
d82: de b7 in r29, 0x3e ; 62
asm volatile(
d84: 82 b3 in r24, 0x12 ; 18
d86: 80 93 79 00 sts 0x0079, r24
d8a: 80 91 79 00 lds r24, 0x0079
d8e: df 91 pop r29
d90: cf 91 pop r28
d92: 8f 91 pop r24
d94: 0f 90 pop r0
d96: 0f be out 0x3f, r0 ; 63
d98: 0f 90 pop r0
d9a: 1f 90 pop r1
d9c: 18 95 reti



Meine Fragen nun:
-Funktioniert das so?
-Wie kann ich dem Compiler sagen, dass er mir den Stack vorher nicht aufräumen muss, da im Code die Register nicht verwendet werden?


Gruss
Michael

SprinterSB
08.02.2006, 16:52
Du machst eine naked Funktion, siehe auch das Beispiel bei GCC im Abschnitt Interrupts.


#include <avr/io.h>

uint8_t register G_cLastData asm ("r2");

void __attribute__ ((naked))
SIG_INTERRUPT0 (void)
{
// Port D
__asm__ __volatile (
"in r2, %0" "\n\t"
"reti"
:
: "M" (_SFR_IO_ADDR (PORTD))
);
}


GCC macht daraus:
.global __vector_1
.type __vector_1, @function
__vector_1:
in r2, 24
reti



Wichtig ist, daß in ALLEN deinen Modulen R2 so als Register deklariert ist, damit GCC nicht auf die Idee kommt, nach R2 Werte zu allokieren. Das gilt auch für Module, die G_cLastData nicht verwenden!

Oder du must anfangen, zu sichern mit push/pop:


#include <avr/io.h>

uint8_t volatile G_cLastData;

void __attribute__ ((naked))
SIG_INTERRUPT0 (void)
{
// Port D
__asm__ __volatile (
"push r2" "\n\t"
"in r2, %1" "\n\t"
"sts %2, r2" "\n\t"
"pop r2" "\n\t"
"reti"
:
: "M" (_SFR_IO_ADDR (PORTD)) : "i" (&G_cLastData)
);
}

Das führt zu

.global __vector_1
.type __vector_1, @function
__vector_1:
push r2
in r2, 18
sts G_cLastData, r2
pop r2
reti


Wichtig ist, daß dabei keine Instruktion den Status ändert (SREG). Daß R2 verwendet wird, braucht gcc nicht zu interessieren. Falls man gcc die Register-Allokierung überlässt, weiß man nicht, wofür er sich entscheidet. Und da gcc kein Epilog machen soll (naked), geht das auch gar nicht, weil das Register nicht wieder hergestellt werden könnte. Daher wird auch das Speichern in die Variable im Asm gemacht.

Zu den Zeiten der ISR-Befehlen muss noch die Interrupt-Respond-Zeit hinzugezählt werden (IRQ-Latenz) und die Zeit für den Sprung aus der Vektor-Tabelle, sie ist also davon abhängig, welche IRQs sonst noch aktiv sind und wie deren ISRs aussehen.

FoCus
08.02.2006, 17:37
Hi, danke für deine Kompetente Hilfe.
Ich habe nun folgendes in meinen Code eingefügt:


void __attribute__ ((naked)) SIG_INTERRUPT0 (void) {
// Port D einlesen
__asm__ __volatile(
"push r2" "\n\t"
"in r2, %1" "\n\t"
"sts %2, r2" "\n\t"
"pop r2" "\n\t"
"reti"
:
: "M" (_SFR_IO_ADDR(PORTD))
: "i" (&G_cLastData)
);
}


Leider liefert mir der Compiler die Fehlermeldung


../Treiber_Parallel.c: In function `__vector_1':
../Treiber_Parallel.c:143: error: parse error before '(' token
C:\AVR\WinAVR\utils\bin\make.exe: *** [Treiber_Parallel.o] Error 1

Zeile 143 ist die Zeile:


: "i" (&G_cLastData)


Hast du eine Ahnung, woran das liegen könnte?

Gruss
Michael

FoCus
09.02.2006, 10:40
Habe mir noch das Totorial von der avrlibc zum Thema inline durchgelesen, werde allerdings trotzdem nicht schlau daraus, was falsch sein könnte.

Grüsse
Michael