Hi, bei DMA-Arrays scheint es nicht üblich zu sein, diese als 'volatile' zu deklarieren - warum?
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.
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.
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.
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
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.
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
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.
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
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.
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
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.
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
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.
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
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.
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
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.
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
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 :-)
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.
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...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.