The volatile keyword is a qualifier it has a lot of importance in the programming language but problem is that many programmers are not aware of how to use the volatile keyword and when need to qualify a variable from the volatile keyword. Most of the textbooks don’t give importance to this topic either and hence it remains partially unexplained most of the time.

In this article, I will try to clear the concept of volatile keyword and describe the benefits of the volatile qualifier in c language. So let’s get started.

A volatile keyword is a qualifier that prevents the objects, from compiler optimization and tells the compiler that the value of the object can change at any time without any action being taken by the code. It prevents the cache from a variable into a register and ensures that every access variable is fetched from the memory.

According to C standard, an object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects.

The volatile keyword is mainly used where we directly deal with GPIO, interrupt, or flag Register. It is also used where a global variable or buffer is shared between the threads.

Declaration of volatile in C:

Like const, volatile is also a qualifier. So we only need to put the volatile keyword after or before the data type for the volatile variable at the time of variable declaration.

// Behavior of both variables should be same int volatile data1; volatile int data2;

Note: We can also use the volatile keyword with pointers.

A volatile keyword with pointers:

We can use volatile keyword with pointers in many ways. In the below section, I am describing some situations where pointer and volatile keyword come together. So let’s see the situation.

1.) Pointer to a volatile variable:

//piData is a pointer to volatile integer int volatile *piData1; volatile int *piData2;

Now both pointers become a pointer to a volatile integer variable. In this situation, the compiler assumes that the value of the variable, which is pointed by a pointer can be change unexpectedly at any time. So whenever we access *piData, the compiler is always aware of unexpected changes in the variable value.

//Volatile variable volatile int iValue; //pointer to volatile variable volatile int* piData = &iValue;

Note: If an attempt is made to refer to an object defined with a volatile-qualified type through the use of an lvalue with non-volatile-qualified type, the behavior is undefined.

If you declare a T(data type) type variable as volatile, then you should not use T * to point this variable. If you will do behavior is undefined. Let’s see below the example where T is an integer.

Wrong-way to access volatile variable using a pointer,

//volatile integer variable volatile int iValue; //integer pointer int* piData = &iValue;

Correct-way to access volatile variable using a pointer,

//Volatile variable volatile int iValue; //pointer to volatile variable volatile int* piData = &iValue;

2.) Volatile pointer to the non-volatile variable:

Here pointer is volatile and pointing to a non-volatile object. It is rarely used.

//Non Volatile variable int iValue; //pointer to volatile variable int* volatile piData = &iValue;

3.) Volatile pointer to the volatile variable:

Here volatile pointer is pointing to a volatile object. Here optimization not applicable to pointer and variable both.

//Volatile variable volatile int iValue; //volatile pointer to volatile variable volatile int * volatile piData = &iValue;

How to use volatile qualifier with structure?

We can use volatile keyword with user-defined data types like structure. Some times it is useful to use volatile keyword with user-defined data types.

If we used a volatile keyword at the time of declaration of a structure variable, then all members of the structure qualified with a volatile qualifier. But sometime in the program, we need only some specific member as volatile so in that situation, we have to declare this member explicitly volatile.

It totally depends on requirements on how to use volatile keyword with structure. Let’s see some examples where I have used volatile keyword with structure.

Example 1:

typedef struct { unsigned int PortReg; unsigned int FlagReg; } My_Reg; //Use of volatile with structure variable My_Reg volatile sMyHardwareReg;

In the above example, all member of the sMyHardwareReg is volatile.

Example 2.

typedef volatile struct { unsigned int PortReg; unsigned int FlagReg; } My_Reg; My_Reg sMyHardwareReg;

Similar to example 1, sMyHardwareReg is also a variable of the user-defined data type. All members of sMyHardwareReg are volatile.

Example 3

typedef struct { //volatile attribute unsigned int volatile PortReg; unsigned int FlagReg; } My_Reg; My_Reg sMyHardwareReg;

In the above example, PortReg is only a volatile member of the structure variable sMyHardwareReg.

struct variable as volatile vs marking individual fields volatile:

Let’s see a few examples to understand the difference when making struct variables as volatile and when making individual members volatile.

typedef struct { int *piPortReg; int TimerReg; } MyReg; volatile MyReg sMyReg;

That would act like,

typedef struct { int * volatile piPortReg; int volatile TimerReg; } MyReg;

And not like,

typedef struct { volatile int *piPortReg; int volatile TimerReg; } MyReg;

So if a structure contains a pointer and you want to use this pointer to point volatile variable, then you have to implicitly use volatile with this pointer. Like the below expression,

typedef struct { volatile int *piPortReg; int TimerReg; } MyReg;

If you want to learn more about the c language, here 10 Free days (up to 200 minutes) C video course for you.

Your free trial is waiting

When need to use the volatile keyword?

It is very important to understand when to use the volatile keyword in the program. Many programmers know the concept of volatile but they are not aware of the situation where using a volatile keyword is beneficial. So here I am explaining situations where using a volatile keyword is beneficial.

1.) The program works fine but when increase the optimization level of compiler its behavior change and not work as per the desire.

2.) Everything is going good but as soon as you enable the interrupt, code behavior change and does not work as per the expectation.

3.) Flaky hardware drivers.

4.) Tasks that work fine in isolation but crash when another task is enabled.

How does volatile keyword affect the program?

The volatile keyword is used to restrain the compiler from making the assumption about the object value. It tells the compiler to re-read the value of the object in every execution.

For better understanding, I am taking a small program to describe the importance of volatile keyword.

// Hardware flag register unsigned char FLAG_REG; void fun (void) { // Repeat while bit 0 is set while (FLAG_REG & 0x01) { //Perform any operation } }

If we increase the compiler optimization level, then for the better performance compiler load the FLAG_REG value in a register and does not re-read again although the value of FLAG_REG change by the hardware. In that situation, your code would not be work as per your expectation.

But if you qualify the FLAG_REG from the volatile keyword, then compiler understands that the value of FLAG_REG can change by the outer word so it avoids implementing any optimization on it.

// Hardware flag register volatile unsigned char FLAG_REG; void fun(void) { // Repeat while bit 0 is set while (FLAG_REG & 0x01) { //Perform any operation } }

What is the proper place to use a volatile qualifier?

A variable should declare volatile when its value can change unexpectedly. In practice, you must declare a variable as volatile whenever you are:

1.) Accessing the memory-mapped peripherals register.

2.) Accessing the global variables in an interrupt routine or signal handler.

3.) Sharing the global variables or buffers between the multiple threads.

Let’s describe mentioned three cases where we must use a volatile qualifier.

Accessing Peripheral Register

In the embedded system, all peripherals are located at a specific memory address. Peripherals have the registers, the value of these register can change asynchronously to code flow.

In a program, to access the peripherals register in a convenient way, we have to map the peripherals register with the C variable and access this variable using the pointer.

Note: In mapping not only care about the size and address of the registers but also need to care about its alignment in memory.

Consider a simple example, here a 32-bit flag status register at an address 0x40000000 and you have to monitor its first bit and wait in the loop until its first bit is one. Here if you will not use volatile qualifier, then you will not get the proper result.

#define STATUS_REG (unsigned int*)(0x40000000) volatile unsigned int *const puiData = STATUS_REG; // Wait until first bit is set while((*puiData) & 0x01) { //do some work }

Accessing the global variables Interrupt service routine (ISR)

Often a global variable is shared between ISR and function. In the below example, a global variable (giFlag) is shared between ISR and the main() function. Let’s see an example code,

//Global flag int giFlag = 0; ISR(void) { giFlag = 1; } int main(void) { while (!giFlag) { //do some work } return 0; }

In the above code, ISR is setting the value of the global flag and the main() function is monitoring the value of the global flag. The main() function does some other task until the global flag value is zero.

Everything will be might ok until you not turn on your compiler optimization. If you will turn on your compiler optimization, then might this code stop to work properly. Because it is unaware of the value changes by the ISR. So it assumes that while loop is always true and it never exits from the loop.

You can solve this problem by just using the volatile qualifier with the global flag. It prevents the compiler from applying any optimization on the global flag and tells the compiler that the value of this flag can change by the external event at any time without any action being taken by the code.

//Global volatile flag volatile int giFlag = 0; ISR(void) { giFlag = 1; } int main(void) { while (!giFlag) { //do some work } return 0; }

Accessing the global variables between two are more threads (multi-thread application):

In a multithread application, two thread communicate to each other using the pipes or message queue but beside it, there is one more technique through which thread can communicate to each other this technique is shared location (shared buffer or global variable).

Generally, the thread is executed in an asynchronous way. If we do not declare these shared locations with the volatile keyword and we increase the optimization level of the compiler then the compiler will store these values in a local variable of thread context and always read the value from theses local variables. So for the desired operation, we have to declare a shared buffer or global variable as volatile.

//Global flag int gValue; void Task_1(void) { gValue = 0; while (gValue == 0) { sleep(1); } ... } void Task_2(void) { ... gValue++; sleep(10); ... }

This code will likely fail once the compiler’s optimizer is enabled. We can resolve the problem by declaring gValue with volatile qualifier.





const and volatile qualifier together

The const volatile are fascinating keywords that make many people confuse. It is very interesting to use volatile and const keyword together because the quality of the volatile (“any time-changing”) and const (“read-only”) seems like opposed, but sometimes it is useful to use this keyword together with a variable.

I have already written a detailed article ” const and volatile together” you can check this article.

In the below section, I am describing some scenarios where you can use volatile and const together.

Access the GPIO Register ( Constant address):

One of the great use of volatile and const keyword together is at the time of accessing the GPIO registers. In the case of GPIO, its value will be changed by the ‘external factors’ (if a switch or any output device is attached with GPIO), if it is configured as an input. In that situation, volatile plays an important role and ensures that the compiler always read the value from the GPIO address and avoid to make any assumption.

After using the volatile keyword, you will get the proper value whenever you are accessing the ports but still here is one more problem because the pointer is not const type so it might be your program change the pointing address of the pointer. So we have to create a constant pointer with a volatile keyword.

Let’s see an example,

unsigned int volatile * const pLcdReg = (unsigned int volatile *) 0x00020000;

In the above syntax, pLcdReg is a constant pointer to a volatile unsigned integer. The pLcdReg is pointing to a memory location 0x00020000 (GPIO Address).

Using the pLcdReg, we can read from or write to value from the pointing GPIO address.

//Writing to memory location *pLcdReg = WRITE_DATA; // to write data on LCD //Reading from memory location READ_DATA = *pLcdReg; //to read data from the LCD

Read-Only Shared-Memory Location:

If two processor communicates to each other using the shared memory location and processor uses the location only to the read the data, we have to make the location read-only type using the const keyword.

unsigned int const volatile gSharedFlag; unsigned char const volatile acSharedBuffer[BUFFER_SIZE];

Read from the status register

There are a lot of registers which are used to reflect the status of the device at the different stage of the hardware. These registers are read-only types and their value changes by asynchronously by the other events. If you want to access these register, you have to use const and volatile together with pointers.

Example,

unsigned int const volatile * const pStatusFlagReg = (uint8_t *) 0x20000000; //to read status from the status register READ_DATA = * pStatusFlagReg; // Not possible because address qualify by const keyword *pStatusFlagReg = WRITE_DATA;

Your opinion matters:

Although here, I have tried to discuss a lot of points on volatile keyword and about its use but I would like to know your opinion on the volatile keyword. So please don’t forget to write a comment in the comment box.

Recommended Post





