You are here

C - Volatile

Eine gute Erklärung von VOLATILE kann unter https://blog.regehr.org/archives/28 gefunden werden. Hier ein kleiner Auszug:

For every read from a volatile variable by the abstract machine, the actual machine must load from the memory address corresponding to that variable. Also, each read may return a different value. For every write to a volatile variable by the abstract machine, the actual machine must store to the corresponding address. Otherwise, the address should not be accessed (with some exceptions) and also accesses to volatiles should not be reordered (with some exceptions).

Der generelle Gebrauch wird unter diesen Links gut beschrieben:
// NEGATIVE EXAMPLE
    // Main
        void main()
        {
            foo = 0;
 
            for(;;)
            {
                while(!foo);       // could be replace by while(true)
                foo = 0;           // -> endless loop
            }
        }
 
    // ISR
        int foo;                   // globally shared variable
 
        void isr()
        {
            // ...
 
            foo = 1;
        }
 
// BAD PROGRAMMING STYLE
    // Main
        /* The function would need to load the variable from the
           original location in case of external function declaration.
           It doesn't know the preceding program code. Thus no
           optimizations can be made.
           Anyway in case the compiler optimization is more
           sophisticated it may fail.
        */
        void wait()
        {
            while(!foo);
        }
 
        // static void main()
        // inline void wait()
        /* In case of inline or static function declaration the
           compiler already knows all preceding program code and
           could optimize something.
        */
 
        void main()
        {
            foo = 0;
 
            for(;;)
            {
                wait();
                foo = 0;
            }
        }
 
    // ISR
        int foo;                   // globally shared variable
 
        void isr()
        {
            // ...
 
            foo = 1;
        }
// volatile integer
    volatile int foo;
    int volatile foo;
 
// pointer to volatile integer
    volatile int * pFoo;
    int volatile * pFoo;
 
// volatile pointer to volatile integer
    volatile int * volatile pFoo;
    int volatile * volatile pFoo;
 
// pointer to volatile struct
    // separate volatile declaration for each variable
        typedef struct
        {
        } foo_s;
        foo_s volatile *pFoo = 0x10000;
 
        // disadvantage: have to be placed within
        //               function declarations too
        void doSmth(foo_s volatile *pFoo);
 
    // each variable declared volatile
        typedef volatile struct
        {
        } foo_s;
        foo_s *pFoo = 0x10000;
 
        // advantage: automatically added at each declaration
        void doSmth(foo_s *pFoo);
 
// volatile struct member
    typedef struct
    {
        volatile int a;
        int          b;
    } foo_s;
 
    typedef struct
    {
        int volatile a;
        int          b;
    } foo_s;
 
    // advantage: all other accesses can be optimized
 
// volatile const variable
    const volatile int * p;
 
    // usage: the program isn't allowed to store to it,
    //        however the register value may change from HW side

 

Beispiel für einen non-volatilen Buffer-Zugriff zw. ISR und Main

Bei einem Austausch über Double-Buffering passiert der Zugriff immer nur von einer Seite (Synchronisation vorausgesetzt). Das ermöglicht Compiler-Optimierungen, da ein direkter Lese/Schreib-Zugriff nicht mehr immer notwendig ist.

// Main
    void main()
    {
        int * local;
 
        collect  = &a;
        process  = &b;
        new_data = 0;
 
        IE();               // interrupts enabled
 
        for(;;)
        {
            while(!new_data);
            local = process;
            new_data = 0;
 
            // read/write access
            x = *local;
            y = *local;     // could be optimized
            *local = x/2;
        }
    }
 
// ISR
    int a,b;                // double buffering
    int * volatile collect;
    int * volatile process;
 
    int volatile new_data;  // sync variable
 
    void isr()
    {
        int * local = collect;
 
        // ...
 
        *local  = y;
        *local += 2;       // could be optimized
 
        // ...
 
        // swap buffer and inform main
        // (main should already be within while-loop)
        if (process == &a)
        {
            process = &b;
            collect = &a
        }
        else
        {
            process = &a;
            collect = &b;
        }
       new_data = 1;
    }

 

Beispiel für einen volatilen Zugriff von non-volatile Variablen

Eine Variable, welche nur in der ISR verwendet wird, kann grundsätzlich non-volatile deklariert werden. Wird sie aber während der Initialisierung in der Main-Routine beschrieben, so muss sichergestellt werden, dass der Zugriff volatile passiert (direkter Zugriff an genau diesem Zeitpunkt) und die ISR noch nicht aufgerufen werden kann. Weiterführendes über volatile-casts kann unter https://bytes.com/topic/c/answers/221923-cast-volatile nachgelesen werden

// Main
    void main()
    {
        *((int volatile *)&foo) = 1;
 
        IE();               // interrupts enabled
 
        // ...
    }
 
// ISR
    int foo = 0;        // globally shared variable
 
    void isr()
    {
        if (foo == 0)
        {
            foo = 1;
        }
        else
        {
            foo = 0;
        }
 
        // ...
    }

 

Unterschiede bei der Kernel-Programmierung:

https://web.archive.org/web/20160304053622/https:/www.kernel.org/doc/Documentation/volatile-considered-harmful.txt