PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Volatile und Interrupt



Arexx-Henk
25.02.2006, 09:06
Hallo,

Wenn in eine normalen Funktion ein oder mehere variabelen benutzt werden und irgendwo lauft ein Interrupt Funktion der seinen eigenen lokalen variabelen benutzt, welche variabelen in die normalen Function soll ich dann wie 'volatile' deklarieren damit die ubereinstimmende processor-register nicht vom Interrupt Funktion zerstohrt werden?

Wer hilft mich raus?

Gruss

Henk

ogni42
25.02.2006, 16:31
Alle die variablen werden volatile deklariert, die von beiden Routinen gelesen oder geschrieben werden:

volatile uint8_t readOrWriteMe;

SprinterSB
28.02.2006, 10:52
Die lokalen Variablen brauchen nicht als volatile (flüchtig) deklariert zu werden. volatile ist eine Variable/Zugriff dann, wenn sich der Inhalt "unbemerkt" ändern kann, also wenn Hardware oder Interrupt-Service-Routine den Wert ändern. Volatile stellt sicher, daß bei jedem Zugriff die Variable wirklich gelesen/geschrieben wird.

Werden Daten zwischen einer normalen Funktion und einer ISR ausgetauscht, dann muss die globale Variable/Struktur volatile sein.

Zusätzlich muss der Zugriff atomar sein, wenn das Übergabe-Objekt länger als ein Byte ist.

Arexx-Henk
04.03.2006, 17:21
Die lokalen Variablen brauchen nicht als volatile (flüchtig) deklariert zu werden.


Ich stelle mich folgendes vor:

Eine function (A) mit 40 normalen (nicht-volatile) Variabelen.
Der compiler der ist intelligent, der nutzt fur jeden variabele ein anderes Register. Da aber die 30 Atmega Register nicht 40 unterschiedene Variabelen speichern konnen werden schon einige automatisch 'unterwasser' als volatile angewendet.

Gibt es nur 20 Variabelen dan genugen die 30 Atmega Register um jeden Variabele in ein specifischen Register zu speichern und nur die Register an zu sprechen.

Aber, wenn jetzt ein Interrupt passiert der auch viele register benutzt da kann ich mich folgendes vorstellen: die Register die im Interrupt verwendet werden werden am anfang vom Interrupt zuerst auf die Stack ge'pushed'.

Wenn meine 40-variabele function (A) ein eindere Funtion (B) anruft werden alle Register die von (B) benutzt werden zuerst in (A) in ihren originalen Speichplatze zuruck geschrieben.

Stimmt dieses so ein Bisschen?

Gruss

Henk

SprinterSB
04.03.2006, 17:43
Die Phase, in der ein Compiler sich entscheidet, welche Variable er in welches Register ablegt, nennt man Reload-Phase.

Sounds trivial, aber es ist eine der komplexesten und trickreichsten Phasen beim Compilieren -- zumindest dann, wenn man es gut machen will und die Compilerbauer nicht faul sind.

Aus einer lokalen Variable kann folgendes werden:
Sie wird in einem Register gehalten
Sie wird auf dem Stack angelegt. Bei vielen lokalen Variablen braucht die Funktion einen Frame, in dem lokale Variablen angelegt werden.
Sie wird wegoptimiert und belegt überhaupt nix, ist nicht mal von einem Debugger aus lesbar
statische lokale Variablen werden im RAM angelegt und "überleben" Funktionsaufrufe

Was Funktionen angeht, so wird in der ABI (Application Binary Interface) (https://www.roboternetz.de/wissen/index.php/Avr-gcc#Registerverwendung) beschrieben, welche Register durch eine Funktion einfach benutzt werden und verunstaltet werden dürfen (call-used bzw. call-clobbered Regs) und welche einen Funktionsaufruf unbeschadet überstehen (call-saved Regs).

Bei einer Funktion mit 40 Variablen wird keine als volatile angelegt, es sei denn, du sagst es.

Eine Interrupt Service Routine (ISR) sichert die verwendeten Register sowie das SREG, vor Verlassen der ISR werden die Regs wieder hergestellt. Teilweise werden aber mehr Register gesichert als wirklich nötig wäre, etwa wenn in einer ISR eine Funktion aufgerufen wird. Der Compiler sieht die Funktion nur als Black Box, er weiß i.d.R. nicht was darin abgeht; muss also auch alle call-used Regs sichern, weil die Funktion sie zerstören darf bzw. kann.

Ähnlich geht eine Funktion vor, wenn sie ein call-save Reg verwenden will. damit es nach ihrem Aufruf den ursprünglichen Wert hat, wird es im Prologue gesichert und im Epilogue wieder hergestellt.

Arexx-Henk
04.03.2006, 20:09
Dass ist ja alles machtig interressant und compliziert!

Kannst Du mir erklaren wass hier geschiedt?

//Mein Program

typedef volatile struct{
//mehere volatile Variabelen
volatile signed int A;
}MyStructA;

typedef volatile struct{
//mehere volatile Variabelen
volatile signed int B;
}MyStructB;

volatile MyStructA MyA;
volatile MyStructB MyB;

int main(void){

unsigned char CopyA,CopyB;

CopyA=MyA.A;
CopyB=MyB.B;

}

Das Program compiliert zu:

lds r24,MyA+20
lds r25,(MyA+20)+1
lds r24,MyA+20
lds r25,(MyA+20)+1

Die '20' is okay denn in mein Program sind A und B die 20e byte im structure.

Da kann mann doch einfach erkennen dass beide Variabelen MyA.A und MyB.B anschliessend (ohne code dazwischen) dieselben register r24/r25 benutzen???

Dieses Program (hier stark vereinfachtet) lauft nur wenn ich CopyA und CopyB wie 'volatile' declariere. Die generierten code sieht dann auch ganz anders aus!

Wo gibt's hier ein Denk Fehler???

Gruss

Henk

Arexx-Henk
04.03.2006, 20:19
Noch dazu,
wenn CopyA,CopyB wie 'volatile' definiert sind dann compiliert es sich zu:

lds r24,MyA+20
lds r25,(MyA+20)+1

std Y+1,r24

lds r24,MyA+20
lds r25,(MyA+20)+1

std Y+2,r24



Gruss

Henk

Arexx-Henk
04.03.2006, 20:22
Und nochmal

Hatte MyA geschrieben statt MyB...;-)

es sollte sein:



lds r24,MyA+20
lds r25,(MyA+20)+1
lds r24,MyB+20
lds r25,(MyB+20)+1



und


lds r24,MyA+20
lds r25,(MyA+20)+1

std Y+1,r24

lds r24,MyB+20
lds r25,(MyB+20)+1

std Y+2,r24



Gruss

Henk

SprinterSB
05.03.2006, 09:42
Ich vermite mal, für main wurde ein Frame angelegt. Y ist der Frame-Pointer, über den auf lokale Variablen zugegriffen wird.

Die Variablen im Frame liegen dann bei Y+1, Y+2, Y+3, ... etc, zumindest dann, wenn es Bytes sind. Sind die Typen größer, werden auch die Offsets größer, wobei das untere Byte des Frames immer bei Y+1 liegt. Steht dort ein int, dann fängt die nächste Variable bei Y+3 an, etc.

Der Code ist doch in Ordnung. Zu Bedenken ist nur, daß CopyX ein char ist (1 Byte), MyX.X jedoch ein int (2 Bytes). Daher wird von MyX.X nur das untere Byte gespeichert.

Wenn die Variablen nicht volatile sind und du Zuweisungen machst, die zugewiesenen Werte aber nicht benutzt werden, dann wird das wegoptimiert und es gibt keinen Code, der der den C-Anweisungen entspricht.

Es genügt übrigens volatile einmal hinzuschreiben, bei dir steht's dreimal! (Bei der Strukturkomponente, im der Typdefinition und bei der Variablendefinition...)

Arexx-Henk
10.03.2006, 16:22
Kan jemand noch etwas hierzu sagen?

Folgendes Program (etwas symbolisch).
Da laufen einige interrupts im Hintergrund.

unsigned char MeinArray[20];
unsigned char MeinIndex;
unsigned char MeinVar;

main(){

MeinIndex=12;
MeinVar=MeinArray[MeinIndex];

switch(MeinVar){
}

}

Die Wert in MeinVar ist nicht immer stabiel.
Die Variabelen werden NICHT vom Interrupt Functionen benutzt.

Wenn alle Variabelen wie
- global und volatile
oder
- local und static
deklariert werden laufts ohne Probleme.

Wie ist dass zu erklaren?

(Oder gibts hier ein bug im gcc.exe compiler???, da werde ich immer mehr archwohnisch)

gruss

Henk

SprinterSB
11.03.2006, 10:04
Da müsste man das ganze Programm sehen, um zu sagen, ob der Fehler bei dir liegt oder beim Compiler:

-- precompiled C (minimal, if appropriate)
-- cmd-Line Options
-- Assemble (-S -da -dp -fverbose-asm)

Wenn du nen Stack-Überlauf hast oder verbogene Pointer, kann das auch auf Variablen wirken, die du nicht anfasst. Ähnliche Symptome bekommst du bei falschem Zugriff.