Hi,
fange gerade an mit gcc. Jetzt habe ich schon gelernt, dass man
Variablen, die man in ISR's ändern will, volatile deklarieren muss,
damit sie nicht in Registerbänke wegoptimiert werden, sondern im SRAM
liegen.
Das bereitet mir aber ein ungutes Gefühl, weil der Zugriff auf den SRAM
ja langsamer ist als auf interne Register. Ich befürchte Probleme bei
zeitkritischen Anwendungen.
Ist das berechtigt? Oder unproblematisch?
schöne Grüsse
Hans
Das ist aber eben genau das, was man für die Kommunkation zwischen
den beiden Threads (wenn man sie so nennen will) braucht. Allerdings
sollte man die "volatile" deklarierte Variable wirklich nur für den
Datenaustausch nehmen. Wenn man mit den Dingern mehr als eine einzelne
Abfrage/Zuweisung macht, sollte man sie besser in lokalen Variablen
cachen (die dann wieder optimiert werden können).
Klar kann man für wirklich zeitkritische Dinge auch noch reine
Registervariablen zwischen ISR und main() zur Kommunikation benutzen,
aber das ist eher ein Sonderfall.
Nun ja, wenn "volatile" erforderlich ist, ist es erforderlich. Schneller
geht es nicht, denn ohne funktioniert das Programm nicht mehr so, wie es
soll.
Aber ich würde mal sagen, du machst dir unbegründete Sorgen. Solltest du
jemals (was ich nicht glaube) an eine Anwendung geraten, die so
zeitkritisch ist, daß es auf jeden Prozessorzyklus ankommt, gibt es
immer noch Assembler, oder einen schnelleren Quarz.
Oliver
OliverSo wrote:
> zeitkritisch ist, daß es auf jeden Prozessorzyklus ankommt, gibt es> immer noch Assembler, oder einen schnelleren Quarz.
Die zweite Variante sollte man aber nur im alleräussersten Notfall in
Betracht ziehen ;)
Ansonsten lohnt es sich bei einigermassen hochfrequent aufgerufenen ISRs
das Disassembly zu prüfen. Dann sieht man auch, ob volatile Zugriffe ein
Problem sein können oder nicht.
@Jörg:
Cachen ist eine gute Idee, danke.
> Klar kann man für wirklich zeitkritische Dinge auch noch reine> Registervariablen zwischen ISR und main() zur Kommunikation benutzen,> aber das ist eher ein Sonderfall.
Die kann man allerdings leider nicht volatile machen.
Rolf Magnus wrote:
>> Klar kann man für wirklich zeitkritische Dinge auch noch reine>> Registervariablen zwischen ISR und main() zur Kommunikation benutzen,>> aber das ist eher ein Sonderfall.>> Die kann man allerdings leider nicht volatile machen.
Und mittlerweile gibt's nichtmal mehr mit -Wall eine Warnung dafür. :-(
Jörg Wunsch wrote:
> Wenn man mit den Dingern mehr als eine einzelne> Abfrage/Zuweisung macht, sollte man sie besser in lokalen Variablen> cachen (die dann wieder optimiert werden können).
Ob das in dieser Allgemeinheit ein guter Ratschlag ist, möchte ich stark
bezweifeln, denn dann definiert man die betreffende Variable besser
nicht volatile.
volatile garantiert, daß jeder Lesezugriff auf die betreffende Variable
immer ihren aktuellen Wert benutzt, nicht irgendeinen früheren. Es ist
kein Ersatz für Synchronisationsmechanisman!
Uhu Uhuhu wrote:
> Ob das in dieser Allgemeinheit ein guter Ratschlag ist, möchte ich stark> bezweifeln, denn dann definiert man die betreffende Variable besser> nicht volatile.
Warum willst du die Variable zwingend als volatile behandeln, wenn
klar ist, dass sie im Moment von niemandem sonst angefasst werden
kann, bspw. innerhalb der ISR? Trotzdem ist die Deklaration als
volatile der sinnvolle und richtige Weg.
Jörg Wunsch wrote:
> Warum willst du die Variable zwingend als volatile behandeln, wenn> klar ist, dass sie im Moment von niemandem sonst angefasst werden> kann, bspw. innerhalb der ISR?
Das ist ein Spezialfall: Die Variable ist in einer ISR durch einen
Synchronisationsmechanismus geschützt - die Interruptsperre.
Wenn man im Hauptprogramm entsprechend Ausdrücke, die auf die Variable
zugreifen, ebenfalls unter Interruptsperre berechnet, dann braucht man
kein volatile.
Uhu Uhuhu wrote:
> Jörg Wunsch wrote:>> Warum willst du die Variable zwingend als volatile behandeln, wenn>> klar ist, dass sie im Moment von niemandem sonst angefasst werden>> kann, bspw. innerhalb der ISR?>> Das ist ein Spezialfall: Die Variable ist in einer ISR durch einen> Synchronisationsmechanismus geschützt - die Interruptsperre.
Ich glaube ihr redet aneinander vorbei.
Was Jörg ursprünglich angesprochen hatte, ist dieser Fall
volatile uint8_t Wert;
ISR( ... )
{
Wert = 8 * Wert + 75;
y = 23 * Wert;
x = y * Wert / Wert;
}
int main()
{
...
while( Wert ) {
...
}
}
also einfach irgendwelche Berechnungen, in denen die volatile
Variable mehrfach vorkommt. (Obige Berechnungen sind sinnlos,
sollen einfach nur verdeutlichen, dass die volatile Variable
mehrfach vorkommt).
Da hier das volatile aber notwendig ist, ensteht die Situation
dass das voltaile innerhalb der ISR kontraproduktiv ist, weil
es den Compiler am Register Optimieren hindert.
Mit einer gecachten Version der Variable umugeht man das Problem
volatile uint8_t Wert;
ISR( ... )
{
uint8_t WertCache;
WertCache = Wert = 8 * Wert + 75;
y = 23 * WertCache ;
x = y * WertCache / WertCache ;
}
int main()
{
...
while( Wert ) {
...
}
}
>> Wenn man im Hauptprogramm entsprechend Ausdrücke, die auf die Variable> zugreifen, ebenfalls unter Interruptsperre berechnet, dann braucht man> kein volatile.
Dieser Satz macht für mich keinen Sinn, weil volatile und
Interruptsperre
ja verschiedene Problemstellungen lösen.
Karl heinz Buchegger wrote:
>> Wenn man im Hauptprogramm entsprechend Ausdrücke, die auf die Variable>> zugreifen, ebenfalls unter Interruptsperre berechnet, dann braucht man>> kein volatile.>> Dieser Satz macht für mich keinen Sinn, weil volatile und> Interruptsperre ja verschiedene Problemstellungen lösen.
Das ist richtig. volatile ist kein Synchronisationsmechanismus.
Aber: Wenn man durch Synchronisationsmechanismen sicherstellt, daß eine
asynchron veränderliche Variable sich in den Abschnitten nicht verändern
kann, in denen mit ihr gerechnet wird, dann braucht man sie nicht
als volatile zu deklarieren.
Globale Interruptsperre ist so ein Synchronisationsmechanismus. Das
Semaphor-Konzept ist ein anderer.
@ Jörg Wunsch (dl8dtl)
>> Die kann man allerdings leider nicht volatile machen.>Und mittlerweile gibt's nichtmal mehr mit -Wall eine Warnung dafür. :-(
Naja, aber mal im Ernst, wo wird denn WIRKLICH ne globale, volatile
Variable im Register gebraucht? Mit register soll man AFAIK sowieso
eher sparsam bis gar nicht umgehen, der Compiler ist (meist) schlau
genug das selber hinzufummeln. Und für das Handshake zwischen Interrupt
und Main ist eine volatile Variable im RAM allemal schnell genug.
MFG
Falk
Uhu Uhuhu wrote:
> Aber: Wenn man durch Synchronisationsmechanismen sicherstellt, daß eine> asynchron veränderliche Variable sich in den Abschnitten nicht verändern> kann, in denen mit ihr gerechnet wird, dann braucht man sie /nicht/> als volatile zu deklarieren.
Dumm ist nur, wenn der Compiler die Variable dann wegoptimiert, da er
denkt, dass diese nirgends verändert wird.
Es funktioniert zwar ab und zu, aber es ist Pfusch und eine extreme
Fehlerquelle. Lieber einmal volatile zuviel geschrieben, dafür das
Programm um 1% ausgebremst, als 2 Stunden Fehlersuche.
Benedikt K. wrote:
> Uhu Uhuhu wrote:>>> Aber: Wenn man durch Synchronisationsmechanismen sicherstellt, daß eine>> asynchron veränderliche Variable sich in den Abschnitten nicht verändern>> kann, in denen mit ihr gerechnet wird, dann braucht man sie>> nicht als volatile zu deklarieren.>> Dumm ist nur, wenn der Compiler die Variable dann wegoptimiert, da er> denkt, dass diese nirgends verändert wird.
Der Compiler optimiert deshalb mit Sicherheit keine Variable weg - ob er
überhaupt Variablen wegoptimierten darf, möchte ich stark bezweifeln. Er
kann zwar die Berechnung von Ausdrücken optimieren, aber nicht Daten.
> Es funktioniert zwar ab und zu, aber es ist Pfusch und eine extreme> Fehlerquelle.
Mir scheint, du hast nicht begriffen, worum es sich handelt: Einzelne
durch Synchronisationsmechanismen geschützte Programmabschitte, in
denen sich die Variable nicht verändern kann. Das heißt noch lange
nicht, daß sie sich außerhalb der geschützten Abschnitte nicht ändern
kann - dann wurde sie in einem anderen geschützten Abschnitt
geschrieben.
> Lieber einmal volatile zuviel geschrieben, dafür das Programm um 1%> ausgebremst, als 2 Stunden Fehlersuche.
Wenn das mal so einfach wäre...
Falk Brunner wrote:
>>Und mittlerweile gibt's nichtmal mehr mit -Wall eine Warnung dafür. :-(> Naja, aber mal im Ernst, wo wird denn WIRKLICH ne globale, volatile> Variable im Register gebraucht?
Für genau den hier besprochenen Fall: Kommunikation zwischen ISR und
Hauptkontext, aus Geschwindigkeitsgründen über ein permanent dafür
belegtes Register. Das spart der ISR nämlich das Retten eines
entsprechenden Registers, Auslesen und Rückschreiben des Speichers
und Rückspeichern des alten Registers.
> Mit register soll man AFAIK sowieso> eher sparsam bis gar nicht umgehen, ...
Wir reden nicht über das praktisch obsolete Schlüsselwort "register"
allein, sondern über sowas:
Uhu Uhuhu wrote:
> Aber: Wenn man durch Synchronisationsmechanismen sicherstellt, daß eine> asynchron veränderliche Variable sich in den Abschnitten nicht verändern> kann, in denen mit ihr gerechnet wird, dann braucht man sie /nicht/> als volatile zu deklarieren.
Eine Variable auf die von verschiedenen "Threads" bzw.
Mainloop/Interrupt zugegriffen wird muss immer volatile deklariert
sein, völlig unabhängig von Synchronisationsmechanismen. Sonst würde zum
Beispiel folgendes nicht funktionieren:
1
interruptbla{
2
x=1;
3
}
4
5
...
6
7
x=0;
8
while(x==0);// warten auf Ereignis
Wie Jörg schon geschrieben hat sind volatile und Semaphoren o.ä.
Lösungen für zwei verschiedene Probleme, das eine lässt sich nicht durch
das andere ersetzen.
Uhu Uhuhu wrote:
> Benedikt K. wrote:>> Uhu Uhuhu wrote:>>>>> Aber: Wenn man durch Synchronisationsmechanismen sicherstellt, daß eine>>> asynchron veränderliche Variable sich in den Abschnitten nicht verändern>>> kann, in denen mit ihr gerechnet wird, dann braucht man sie>>> nicht als volatile zu deklarieren.>>>> Dumm ist nur, wenn der Compiler die Variable dann wegoptimiert, da er>> denkt, dass diese nirgends verändert wird.>> Der Compiler optimiert deshalb mit Sicherheit keine Variable weg - ob er> überhaupt Variablen wegoptimierten darf, möchte ich stark bezweifeln. Er> kann zwar die Berechnung von Ausdrücken optimieren, aber nicht Daten.>
Benedikt hat einen lesenden Zugriff auf eine Variable gemeint.
Mir scheint du sprichst von einem ganz andern Problem-Fall als
dem um den es Jörg ursprünglich ging.
Schau dir noch mal die Programm-Skizze an, die ich weiter oben
gepostet habe. Das volatile ist dort notwendig und wichtig.
Das Dumme ist nur, dass genau dieses volatile innerhalb der
ISR den Optimizer in seiner Arbeit behindert.
Karl heinz Buchegger wrote:
> Benedikt hat einen lesenden Zugriff auf eine Variable gemeint.
Ja, so ist es. Das Beispiel hat Andreas ja schon geliefert. Ohne
volatile wird x wegoptimiert, und die Warte Schleife durch eine
Endlosschleife ersetzt.
Benedikt K. wrote:
> Karl heinz Buchegger wrote:>>> Benedikt hat einen lesenden Zugriff auf eine Variable gemeint.>> Ja, so ist es. Das Beispiel hat Andreas ja schon geliefert. Ohne> volatile wird x wegoptimiert, und die Warte Schleife durch eine> Endlosschleife ersetzt.
Nein, nicht x wird wegoptimiert, sondern 'while (!x)' wird gegen
'while (1)' ausgetauscht. Die Variable x existiert trotzdem weiter.
Uhu Uhuhu wrote:
> Benedikt K. wrote:>> Karl heinz Buchegger wrote:>>>>> Benedikt hat einen lesenden Zugriff auf eine Variable gemeint.>>>> Ja, so ist es. Das Beispiel hat Andreas ja schon geliefert. Ohne>> volatile wird x wegoptimiert, und die Warte Schleife durch eine>> Endlosschleife ersetzt.>> Nein, nicht x wird wegoptimiert, sondern 'while (!x)' wird gegen> 'while (1)' ausgetauscht. Die Variable x existiert trotzdem weiter.
Lass uns jetzt bitte nicht Haare spalten.
Genauso hat es Benedikt gemeint.
Das ist genauso Pfusch. volatile
>> Dumm ist nur, wenn der Compiler die Variable dann wegoptimiert, da er>> denkt, dass diese nirgends verändert wird.>> Der Compiler optimiert deshalb mit Sicherheit keine Variable weg - ob> er überhaupt Variablen wegoptimierten darf, möchte ich stark> bezweifeln.
Er darf alles wegoptimieren, was sich nicht auf I/O oder
volatile-Zugriffe auswirkt. Wenn eine Variable nicht volatile ist, darf
sie auch komplett wegoptimiert werden. Bei globalen Variablen wird der
Compiler das aber vermutlich nie tun, außer wenn sie static deklariert
sind.
> Er kann zwar die Berechnung von Ausdrücken optimieren, aber nicht> Daten.
Doch, kann er. Bei lokalen Variablen passiert das auch oft.
> Mir scheint, du hast nicht begriffen, worum es sich handelt: Einzelne> durch Synchronisationsmechanismen geschützte Programmabschitte, in> denen sich die Variable nicht verändern kann. Das heißt noch lange> nicht, daß sie sich außerhalb der geschützten Abschnitte nicht ändern> kann - dann wurde sie in einem anderen geschützten Abschnitt> geschrieben.
Daß sie wirklich geschrieben und gelesen wird, kann man aber nur mit
volatile erzwingen, denn genau dazu ist volatile da.
Ich hoffe ja, daß nun endlich klar ist, daß volatile und atomar 2 völlig
verschiedene Dinge sind, die auch manchmal zusammen benötigt werden.
Zurück zum Thema volatile:
Jörg hatte mal eine sehr elegante Methode gepostet, indem man die
Variable im Main castet, um den Zugriff zu erzwingen. Damit kann der
Interrupt voll optimieren.
Geht bloß nicht direkt, sondern über die Brust ins Auge per Pointer.
Hier mal ein Beispiel:
@Peter
Was bringt das atomar in dem Beispiel ? Ein Bytes aus dem SRAM lesen
ist ein Takt, da kann doch nichts schiefgehen.
Bei einer 16 oder 32bit Zahl würden auch die Makros nichts bringen, ohne
zusätzliches cli/sei.
>Wir reden nicht über das praktisch obsolete Schlüsselwort "register">allein, sondern über sowas:>register uint8_t some_ISR_state asm("r2");
Frage an den Jörg, ist das wirklich so einfach ?
Wenn die Variable dann wirklich im r2 gehalten wird, dann wäre das
wirklich genial und man könnte locker auf volatile verzichten.
Das muß ich mal ausprobieren
Gruß Sebastian
Benedikt K. wrote:
> @Peter> Was bringt das atomar in dem Beispiel ? Ein Bytes aus dem SRAM lesen> ist ein Takt, da kann doch nichts schiefgehen.
Das bringt nur den Beweis, daß volatile und atomar nichts miteinander zu
tun haben.
Ich habs einzig und allein wegen der obigen Diskussionen mit
reingestellt.
> Bei einer 16 oder 32bit Zahl würden auch die Makros nichts bringen, ohne> zusätzliches cli/sei.
Volatile erzwingt kein atomar und umgekehrt.
Man muß, wenn nötig, beides verwenden.
Peter
Sebastian wrote:
>>register uint8_t some_ISR_state asm("r2");> Frage an den Jörg, ist das wirklich so einfach?
Antwort vom Sender Jerewan: im Prinzip ja. Allerdings müssen
natürlich alle translation units dieselbe Deklaration dafür
sehen, einschließlich der translation units, die die (System-)
Bibliothek bilden.
> Wenn die Variable dann wirklich im r2 gehalten wird, dann wäre das> wirklich genial und man könnte locker auf volatile verzichten.
Nein, kann man nicht. Wenn man im nicht-ISR-Kontext eine Abfrage
à la
1
while(!some_ISR_state)
2
/* wait */;
hat, kann der Compiler ohne volatile wieder schlussfolgern, dass
some_ISR_state sich gar nicht mehr ändern kann, und die Abfrage
auf r2 nur einmal einbauen. Nur funktioniert eben volatile register
im GCC dummerweise nicht.
Hallo Jörg,
danke für die Antwort.
Das mit verzicht auf volatile war etwas unüberlegt von mir,
natürlich kann man das nicht weglassen.
>Nur funktioniert eben volatile register>im GCC dummerweise nicht.
Schade, sonst könnte man die 'unteren' Register sinvoll nutzen
Gruß Sebastian