Hallo,
ich hab mal wieder eine Frage zum UART. Diesmal geht es um volatile. Mir
ist hier nicht ganz klar, ob ich alle Variablen, die von der ISR
geändert werden können volatile deklariert werden müssen.
Ich habe 3 globale Variablen die von der ISR geändert werden:
1
uint8_tUSARTInBuf[0x10];
2
fifo_tUSART_RX_Buf;
3
volatileCtrlInterface_tCtrlInterface;
Das ist ein CtrlInterface welches das USART_RX_Buf FIFO beinhaltet.
Das USART_RX_Buf FIFO beinhaltet wiederum ein uint8_t Array.
Wie weiter unten im Code zu sehen kann die ISR Routine alle drei Objekte
verändern.
Die Member "Status" und USART_RX_Buf FIFO (und dadurch dann natürlich
dessen uint8_t buffer).
1
ISR(USART_RX_vect)
2
{
3
uint8_tuartRX=UDR0;
4
switch(CtrlInterface.state)
5
{
6
caseRXSTATE_IDLE:
7
// waiting for start of transmission
8
if(uartRX==ASCII_STX)
9
CtrlInterface.state=RXSTATE_RECEIVE;
10
break;
11
caseRXSTATE_COMPLETE:
12
// trash data
13
break;
14
caseRXSTATE_RECEIVE:
15
// receiving data
16
fifo_push(CtrlInterface.pUSART_RX_Buf,uartRX);
17
if(uartRX==ASCII_ETX)
18
CtrlInterface.state=RXSTATE_COMPLETE;
19
}
20
}
Zuerst bin ich davon ausgegangen, das alles volatile deklariert werden
muss, da sich ja alles ändern kann.
Als ich dann alles als volatile deklariert hatte, hat jedoch der
Compiler angemerkt, dass meine Funktionen die ich für das FIFO
geschrieben habe eigentlich ihre Parameter (also das FIFO das ich zum
bearbeiten übergebe) volatile deklariert haben sollten, da sie nun ja
eine volatile Variable übergeben bekommen.
Das geht mir aber eigentlich einen Schritt zu weit, weil ich die FIFO
Funktionen ja allgemein nutze, nicht nur für das FIFO, dass eventuell
volatile sein muss.
Also die Frage die sich mir jetzt stellt: genügt es das CtrlInterface
Objekt als volatile zu deklarieren, weil das FIFO und dessen Array sich
ja als Member im CtrlInterface befinden, oder muss alles volatile sein.
Wenn alles volatile sein muss, wie geh ich dann damit um, dass der
Compiler über Funktionsparameter meckert die in den FIFO Funktionen
nicht als volatile deklariert sind.
Stefan schrieb:
> Also die Frage die sich mir jetzt stellt:
Die Frage, die sich dir eigentlich stellen sollte, lautet:
Was bewirkt eigentlich volatile?
Daraus ergibt sich dann nämlich, welche Variablen volatile sein müssen
und welche nicht.
Stefan schrieb:
> Als ich dann alles als volatile deklariert hatte, hat jedoch der> Compiler angemerkt, dass meine Funktionen die ich für das FIFO> geschrieben habe eigentlich ihre Parameter (also das FIFO das ich zum> bearbeiten übergebe) volatile deklariert haben sollten, da sie nun ja> eine volatile Variable übergeben bekommen.
Das hört sich für mich so an, als hättest du eine Funktion
1
voidfoo(char*daten);
und möchtest dieser einen volatile char fifo[xx] übergeben und bekommst
dann eine warning vom compiler.
Die warning ist völlig korrekt aber unbedenklich.
Mach einfach einen cast beim aufruf
Hallo,
wenn ich es richtig verstanden habe bewirkt volatile, dass die Variable
vom Optimieren geschützt ist.
Am Beispiel des Members CtrlInterface->state. Dieses überprüfe ich in
einer Loop in der nichts passiert solange "state" nicht auf "complete"
steht. Da sonst nichts passiert würde der Compiler ohne volatile für
CtrlInterface wohl sagen "state wird nie auf complete gesetzt -> also
mach ich einfach nie was".
Also ich denke im Prinzip ist mir schon klar was volatile macht.
Stefan schrieb:
> Hallo,>> wenn ich es richtig verstanden habe bewirkt volatile, dass die Variable> vom Optimieren geschützt ist.
Ja. Darauf läuft es hinaus.
> Am Beispiel des Members CtrlInterface->state. Dieses überprüfe ich in> einer Loop in der nichts passiert solange "state" nicht auf "complete"> steht. Da sonst nichts passiert würde der Compiler ohne volatile für> CtrlInterface wohl sagen "state wird nie auf complete gesetzt -> also> mach ich einfach nie was".> Also ich denke im Prinzip ist mir schon klar was volatile macht.
Gut.
> Mir ist hier nicht ganz klar, ob ich alle Variablen, die von der> ISR geändert werden können volatile deklariert werden müssen.
Angewendet auf diese Aussage:
Du musst jene Variablen volatile machen, die sowohl in einer ISR als
auch ausserhalb der ISR benutzt werden. Eine ISR stellt für den Compiler
in dieser Hinsicht etwas 'nicht-existentes' dar. Wenn der Compiler
optimiert, geht er davon aus, dass eine Variable nicht magisch ihren
Wert ändert.
i = 5;
... mach irgendwas wo i nicht vorkommt
// an dieser Stelle muss i immer noch 5 sein
Der Compiler benutzt dieses Wissen um Speicherzugriffe einzusparen, wenn
der Wert von i noch irgendwo in einem Register rumlungert.
Jetzt kommen aber ISR ins Spiel, von denen der Compiler nichts weiß,
wenn es um die Optimierung dieser Codestelle geht. Wird i in einer ISR
verändert, dann KANN i an der mit // markierten Stelle sehr wohl einen
anderen Wert als 5 haben.
volatile teilt dem Compiler mit, dass eine Variable ihren Wert ändern
kann und zwar ohne dass der Compiler dies selbst rausfinden könnte. Die
Konsequenz daraus ist, dass der Compiler diese Variable aus seinen
Optimierschemata ausnehmen muss, weil er bei seinen Datenflussanalysen
zu falschen Ergebnissen kommen würde.
Also:
Alle Variablen, die sowohl in einer ISR als auch ausserhalb benutzt
werden, müssen volatile sein.
uart schrieb:
> Die warning ist völlig korrekt aber unbedenklich.
Sie kann unbedenklich sein, muss es aber nicht
> Mach einfach einen cast beim aufruf>
1
>foo((char*)fifo);
2
>
was ist wenn sich der Inhalt auf den fifo zeigt, tatsächlich während der
Ausführung von foo ändert?
Karl heinz Buchegger schrieb:
> was ist wenn sich der Inhalt auf den fifo zeigt, tatsächlich während der> Ausführung von foo ändert?
dann hätte man ein mächtiges Problem :)
deswegen schalte ich bei der Abarbeitung immer den entsprechenden IRQ
aus
uart schrieb:
> Karl heinz Buchegger schrieb:>> was ist wenn sich der Inhalt auf den fifo zeigt, tatsächlich während der>> Ausführung von foo ändert?>> dann hätte man ein mächtiges Problem :)> deswegen schalte ich bei der Abarbeitung immer den entsprechenden IRQ> aus
Eben. Und deshalb braucht fifo nicht volatile sein, weil sichergestellt
ist, dass es sich nicht unbemerkt ändert.
Es muß also nicht das ganze CtrlInterface volatile sein, sondern nur
innerhalb der struct das state Element.
Hallo,
vielen Dank für Eure Anmerkungen.
Ich hätte gedacht, dass eventuell durch die Deklaration des
CtrlInterface auch seine Member geschützt werden, also implizit als
volatile angesehen.
Nachdem ich jetzt noch einmal mit Optimierung durchsimuliert habe
scheint dies jedoch nicht der Fall zu sein.
Bleibt nur noch die Frage mit den FIFO Funktionen. Im aktuellen Fall ist
es so, dass sich der FIFO Inhalt nicht ändern wird. Er wird nur während
der ISR geändert bis der Status "complete" erreicht wird, danach wird
dich der Inhalt nicht mehr durch eine ISR ändern, sondern nur noch durch
den zyklischen Programmablauf.
Aber sauberer erscheint mir die Deklaration der Parameter mit volatile,
da sich das im Laufe der Entwicklung ja noch ändern könnte.
Wäre dann eine Überladung der Funktion sinnvoll?
Also quasi:
1
bool_tfifo_push(fifo_t*constfifo,uint8_tdata)
2
{
3
if((fifo->count<fifo->size)&&(fifo->pBuffer!=0))// buffer full or uninitialized?
4
{
5
fifo->count++;// increase number of stored bytes
6
*(fifo->pBuffer+fifo->posWrite)=data;// save byte on next position in buffer
7
8
if(fifo->posWrite==fifo->size-1)// did we reach the end of the buffer?
9
fifo->posWrite=0;// if so restart at beginning
10
else
11
fifo->posWrite++;// otherwise increase write pointer position
Die Sache ist die:
Wenn du ein volatile hinzufügst, ändert sich erst mal nichts an der
Logik. Man kann es auch so sagen: pessimistischer zu sein ist immer
gefahrlos (solange es sich um nichts zeitkritisches handelt)
Das schlimmste was einem passieren kann, ist ein Zuwachs in der
Laufzeit.
Du hast aber auch schon mitgekriegt, dass man mit solchen
Pauschalaussagen ( wie volatile nimmt man da und da) immer etwas
vorsichtig sein soll.
Auch der Rat volatile wegzucasten, kann gut gehen, muss es aber nicht.
Was man in deiner Situation aber machen kann: Die Situation so
herstellen, wie sie der Compiler erwartet. Sprich, keine Interrupts
zulassen.
Was man auch machen kann, zunächst mit einer Kopie arbeiten und so die
Bearbeitung zunächst von der globalen Variablen wegziehen und damit die
Gefahr loswerden, dass einem die globale Variable unter dem A...
umgedreht wird.
Das trickreiche an der Sache ist: Jeder Fall ist etwas anders gelagert
und man muss sich jeden Fall einzeln ansehen. Drum funktionieren auch
die Pauschalempfehlungen nicht immer.
ist unsinnig.
Außerdem :
Angenommen, Du hast CtrlInterface_t folgendermaßen deklariert:
1
typedefstruct{
2
intstate;
3
fifo_t*pFifo;
4
}CtrlInterface_t;
und du machst CtrlInterface volatile :
1
volatileCtrlInterface_tCtrlInterface;
so bedeutet das für den Compiler nicht, dass das fifo volatile ist,
sondern dass der Pointer pFifo in der ISR eventuell von einem fifo auf
ein anders umgesetzt werden kann. Der Pointer ist volatile, nicht das
FIFO selbst.
Karl heinz Buchegger schrieb:
> Auch der Rat volatile wegzucasten, kann gut gehen, muss es aber nicht.> Was man in deiner Situation aber machen kann: Die Situation so> herstellen, wie sie der Compiler erwartet. Sprich, keine Interrupts> zulassen.
So ist die Situation momentan. Das FIFO wird durch die ISR gefüllt,
sobald die Übertragung beendet ist wird der FIFO Inhalt ausgelesen und
verarbeitet. Währenddessen wird der Interrupt abgeschaltet bis alle
Daten des FIFO verarbeitet wurden.
Dennoch ist es so, dass bei der Simulation Dinge die mit dem FIFO zu tun
haben wegoptimiert werden.
Beschrieben wird das FIFO ja nur in der ISR, also wird der Compiler wohl
denken dass es nie gefüllt wurde.
Klaus Falser schrieb:
> bool_t fifo_push(volatile fifo_t* const fifo, uint8_t data);> ist unsinnig.
Das versteh ich nicht so recht. Wenn das FIFO volatile ist meckert der
Compiler über die Deklaration und ist zufrieden, wenn ich es so
hinschreibe.
Klaus Falser schrieb:
> Der Pointer ist volatile, nicht das> FIFO selbst.
Das hab ich jetzt verstanden.