Forum: Mikrocontroller und Digitale Elektronik 'volatile' nur für Interrupt Variablen?


von Jens A. (Gast)


Lesenswert?

Hi,

bei DMA-Arrays scheint es nicht üblich zu sein, diese als 'volatile' zu 
deklarieren - warum?

von Daniel V. (danvet)


Lesenswert?

volatile weißt den Compiler an, den Zugriff auf diese Variable nicht zu 
optimieren, sondern davon auszugehen, dass zwischen zwei Zugriffen (z.B. 
in einer While-Schleife), der Wert sich geändert haben könnte, z.B. 
durch einen Interruptzugriff.
Falls die DMA-Arrays in der main-loop verwendet werden (beispielsweise) 
würde ich sie schon als volatile definieren.

von (prx) A. K. (prx)


Lesenswert?

Einige Leute gehen implizit davon aus, dass man das bei Arrays nicht 
benötigt, weil Compiler darauf weniger Optimierungen anwenden als auf 
Skalare. Es ist also meist kein reales Problem, "volatile" wegzulassen. 
Korrekt ist das freilich nicht.

von Peter D. (peda)


Lesenswert?

Daniel V. schrieb:
> Falls die DMA-Arrays in der main-loop verwendet werden (beispielsweise)
> würde ich sie schon als volatile definieren.

Ohne volatile könnte der Compiler denken, der DMA-RAM ist write only und 
könnte es wegoptimieren. Er wird es zwar selten tun, aber es ist 
unsauber. Korrekter Weise muß der DMA-RAM also volatile sein, wie auch 
jedes IO-Register.

von (prx) A. K. (prx)


Lesenswert?

Bei Arrays, die im Programm und per DMA angesprochen werden, wird 
volatile allein nicht unbedingt ausreichen, will man Probleme vermeiden. 
Je nach Controller-Architektur kann es auch wichtig sein, das Array per 
Software so anzusprechen, dass Caches/Puffer nicht in die Quere kommen. 
Sei es, dass der gleiche Speicher mit unterschiedlichen Eigenschaften 
mehrfach im Adressraum eingeblendet ist. Sei es, dass man im Adressraum 
diesbezügliche Eigenschaften einstellen muss. Sei es, dass man spezielle 
Befehle benötigt, um Konsistenz herzustellen (memory barriers).

: Bearbeitet durch User
von Jim M. (turboj)


Lesenswert?

Peter D. schrieb:
> Ohne volatile könnte der Compiler denken, der DMA-RAM ist write only und
> könnte es wegoptimieren.

Äußerst unwahrscheinlich, denn dann würde auch sowas wie puts() nicht 
mehr korrekt tun. Dort sieht der Compiler den Lesezugriff i.d.R. nicht 
direkt, weil das eine Kernel Funktion ist.

von (prx) A. K. (prx)


Lesenswert?

Schreiboperationen dürfen genau dann als unnötig wegoptimiert werden, 
wenn der Compiler den vollständigen Überblick über alle im C-Programm 
möglichen Operationen auf diese Daten hat. Bei Daten, die 
ausschliesslich lokal in der Funktion definiert sind, ist das 
beispielsweise der Fall, es sei denn deren Adresse wird an Funktionen 
oder Pointer übergeben.

Solange man Quellfiles in traditioneller Manier einzeln übersetzt, hat 
der Compiler über den Datenbestand von puts() keinen Überblick, wird 
also nichts wegoptimieren. Wird jedoch das Programm mitsamt Library aus 
Zwischencode vollständig in einem Vorgang übersetzt, dann hat er den 
Überblick. Nur sieht er dann auch den Lesezugriff darauf, oder die 
Übergabe des Arrays als Parameter an einen Systemaufruf, und wird es 
deshalb nicht wegoptimieren.

DMA-Operationen jedoch sind für den Compiler stets unsichtbar, egal ob 
er zusammen oder getrennt übersetzt. Dementsprechend muss man das 
Programm ausrichten.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

A. K. schrieb:
> Solange man Quellfiles in traditioneller Manier einzeln übersetzt, hat
> der Compiler über den Datenbestand von puts() keinen Überblick, wird
> also nichts wegoptimieren.

Es sei denn man verwendet LTO, dann wird modulübergreifend optimiert.

von (prx) A. K. (prx)


Lesenswert?

Bernd K. schrieb:
>> Solange man Quellfiles in traditioneller Manier einzeln übersetzt, hat
>> der Compiler über den Datenbestand von puts() keinen Überblick, wird
>> also nichts wegoptimieren.
>
> Es sei denn man verwendet LTO, dann wird modulübergreifend optimiert.

Das hatte ich direkt auf diesen Satz folgend in anderen Worten bereits 
geschrieben. ;-)

A. K. schrieb:
> Wird jedoch das Programm mitsamt Library aus
> Zwischencode vollständig in einem Vorgang übersetzt,

Wobei im Fall von puts() die Library in gleicher Weise mit übersetzt 
werden muss, denn sonst ist das eine unbekannte Funktion. Ob normale LTO 
das tut?

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Bernd K. schrieb:
> Es sei denn man verwendet LTO, dann wird modulübergreifend optimiert.

Da aber syscalls eh in (Inline-) Assembler sind, kann da nix 
wegoptimiert werden.

von Bernd K. (prof7bit)


Lesenswert?

Daniel V. schrieb:
> volatile weißt den Compiler an, den Zugriff auf diese Variable nicht zu
> optimieren, sondern davon auszugehen, dass zwischen zwei Zugriffen (z.B.
> in einer While-Schleife), der Wert sich geändert haben könnte, z.B.
> durch einen Interruptzugriff.

Das ist falsch/unvollständig formuliert und teilweise irreführend.

Volatile weist den Compiler an daß alle Zugriffe auf die betreffende 
Variable als beobachtbarer Seiteneffekt (also als Eingabe/Ausgabe) des 
Programms zu betrachten sind.

Für beobachtbare Seiteneffekte gilt die zwingende Regel daß sie genau in 
der im Programm vorgeschriebenen Anzahl und Reihenfolge auszuführen 
sind. Dies hat zur Folge daß manche Optimierungen (nämlich nur solche 
die die Anzahl oder die Reihenfolge der Seiteneffekte ändern würden) 
nicht ausgeführt werden können.

Volatile ist also ein Sprachmittel um einen gewünschten Seiteneffekt, 
eine Interaktion mit der Außenwelt zu deklarieren. Alles was kein 
Seiteneffekt ist  unterliegt der freien Willkür des Compilers und er 
kann es nach belieben umbauen solange die Seiteneffekte gleich bleiben, 
umgangssprachlich "solange das Programm das gleiche tut".

Mit volatile wird also definiert was in diesem Zusammenhang mit "tun" 
eigentlich gemeint ist, alles was es nicht explizit tun soll muss es 
auch nicht tun.

Volatile ist das Sprachmittel mit dem deklariert wird an welchen Stellen 
die abstrakte Maschine mit der realen Maschine verbunden ist.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> Da aber syscalls eh in (Inline-) Assembler sind, kann da nix
> wegoptimiert werden.

Es ist nicht wichtig, ob die Syscalls in Assembler oder in C selbst 
aufgerufen werden. Wichtig ist nur, dass dabei die Adresse von Daten an 
eine Funktion übergeben wird, deren Code dem Compiler unbekannt ist.

von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Wichtig ist nur, dass dabei die Adresse von Daten an
> eine Funktion übergeben wird, deren Code dem Compiler unbekannt ist.

In Standard-C kann man keine Syscalls machen. Den Code dem Compiler 
unbekannt zu machen ist eine Möglichkeit; das ist aber keine zwingende 
Voraussetzung. Wenn der Code bekannt ist kann man mit entsprechender 
Konstruktion dafür sorgen dass nicht wegoptimiert wird. Bei kompletten 
Assembler-Funktionen wird sowieso nicht optimiert, selbst wenn bekannt. 
Wenn man den Syscall als Inline-Assembler umsetzt, der in einem Makro 
oder einer Inline-Funktion auftaucht, kann er ge-inlined werden 
(Effizienz) und durch Angabe entsprechender Constraints kann 
sichergestellt werden dass nicht zu viel optimiert wird.
z.B. so:
https://stackoverflow.com/a/10831844

von Peter D. (peda)


Lesenswert?

Jim M. schrieb:
> Äußerst unwahrscheinlich, denn dann würde auch sowas wie puts() nicht
> mehr korrekt tun.

Und genau das ist mir mal passiert. Der Compiler ist nämlich nicht dumm.
Wenn puts() das Array nacheinander an eine RAM-Stelle schreibt und der 
Compiler sieht nirgends einen Lesezugriff zwischendurch (macht ja ein 
Interrupt), dann schreibt er einfach nur das letzte Byte des Arrays da 
hinein. Diese RAM-Stelle muß also volatile sein.

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> In Standard-C kann man keine Syscalls machen.

Das hängt davon ab, wie ein Syscall aussieht. Grundsätzlich könnte ein 
Syscall wie eine normale C Funktion aussehen, bei der nur die Adresse 
den Unterschied ergibt. Die x86 Call Gates stellen so etwas dar, die in 
segmentiertem Code nicht von normalen Funktionen mit gleicher 
Übergabecharakteristik unterscheidbar sind.

Aber das geht allmählich ins Erbsenzählen über. Ich will damit nur 
ausdrücken, dass der Unterschied nicht so sehr im Aufrufverfahren liegt, 
sondern darin, wieviel der Compiler über das weiss, was aufgerufen wird.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> bei der nur die Adresse
> den Unterschied ergibt

Das Angeben fixer Adressen ist auch Nicht-Standard... :o) Aber okay, da 
hat man dann kein Problem.

A. K. schrieb:
> Ich will damit nur
> ausdrücken, dass der Unterschied nicht so sehr im Aufrufverfahren liegt,
> sondern darin, wieviel der Compiler über das weiss, was aufgerufen wird.
Wobei man entsprechendes Wissen nachreichen kann, sodass es eben auch 
dann geht, wenn der Compiler den Aufruf sieht.

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> Das Angeben fixer Adressen ist auch Nicht-Standard... :o)

Die festen Adressen muss erst der Program-Loader kennen. Woher der sein 
Wissen bezieht ist nicht Sache des C Standards, verletzt ihn auch nicht.

: Bearbeitet durch User
von Teo D. (teoderix)


Lesenswert?

Volatile zwingt den Compiler doch nur dazu, bei JEDEM zugriff auf die 
Variable, diese aus dem Speicher zu holen und keinesfalls die Register 
für Zwischenergebnisse nutzen.
Das dies natürlich die Optimierung erheblich eingeschränkt, ist wohl 
selbsterklärend.

von (prx) A. K. (prx)


Lesenswert?

Teo D. schrieb:
> Volatile zwingt den Compiler doch nur dazu, bei JEDEM zugriff auf die
> Variable, diese aus dem Speicher zu holen und keinesfalls die Register
> für Zwischenergebnisse nutzen.

Nicht als Zwischenspeicher über den Moment hinaus. Nur betrifft das 
ausschliesslich die Sicht des Compilers. Um irgendwelche Puffer oder 
Caches des Prozessors kümmert er sich nicht. Das ist bei I/O-Registern 
kein Problem, da die von der Hardware konsistent abgesprochen werden. 
Bei von DMA genutzem RAM kann das jedoch je nach Controller Aufgabe des 
Programmierers sein. Da ist "volatile" zwar notwendig, aber 
möglicherweise nicht ausreichend.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Da ist "volatile" zwar notwendig, aber
> möglicherweise nicht ausreichend.

Genau... Bei Cortex-A mit mehreren Cache-Ebenen muss man ein "Cache 
Clean" durchführen, und das kann etwas fummelig werden, denn entweder 
macht man das auf dem gesamten Cache (braucht 3 verschachtelte 
Schleifen) oder auf einem Block (einfacher, aber u.U. tatsächlich 
langsamer). Das ganze dann typischerweise noch in Assembler aufgrund der 
CP15-Spezial-Instruktionen. Wenn man das nicht macht, sieht man z.B. bei 
einem Display-Controller schön an den Artefakten welche Cache-Lines 
geleert wurden und welche RAM-Stellen noch die alten Daten haben :-)

von (prx) A. K. (prx)


Lesenswert?

Nicht bloss beim Cortex A. So gibt es beispielsweise beim STR9 
Microcontroller mit ARM9 ohne Cache 3 verschiedene Adressräume für das 
gleiche RAM, aber mit unterschiedlichen Eigenschaften.

von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Nicht bloss beim Cortex A
Logisch.

A. K. schrieb:
> 3 verschiedene Adressräume für das
> gleiche RAM, aber mit unterschiedlichen Eigenschaften.
Kann je nach Konfiguration der MMU bei Cortex-A auch auftreten...

von (prx) A. K. (prx)


Lesenswert?

Manche Cortex M Microcontroller haben mehrere verschieden eingebundene 
RAM Banks mit unterschiedlichen Eigenschaften bzgl DMA und Konsistenz.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.