13 Apr 2017

Usage of Volatile keyword in Embedded Systems

I think, you might have heard of Volatile keyword in programming languages. But, when it comes to writing code especially in Embedded programming the use of “volatile” keyword is very often.

But now the question is,

    What is it (volatile)?

    What it does?

    Why do we need it?

    Does my program/code really need this keyword?

All these and more I will cover in this article.

What does volatile mean?

Ok, let’s find out the meaning of volatile in dictionary.

VOLATILE means: – UNSTABLE, UNPREDICTABLE…etc.

So, the basic meaning of volatile is we can’t predict what is going to happen next.

Now,

What is the significance of volatile keyword?

The significance of volatile keyword in programming language is to inform/tell the compiler not to pre-predict/assume/believe/presume the value of the particular variable which has been declared as volatile.

What is code optimization?

I think this is the particular section is missing everywhere. So now we will see, what is code optimization….

Look at the figure 1 and Figure 2, below.

Suppose:-

Figure 1

int x = 0;

main()

{

if (x == 0) /* This condition is always true */

{

printf(“x = 0 \n”);

}

else /*Else part will be optimized because x will never ever other than 0*/

{

printf(“x != 0 \n”);

}

}

Figure 2

volatile int x; /* volatile Keyword*/

main()

{

x = 0;

if (x == 0)

{

printf(“x = 0 \n”);

}

else

/*Else part will never ever be optimized

because the variable is declared as volatile*/

{

printf(“x != 0 \n”);

}

In fig-1, compiler will optimize the code it will ignore the else part, because the variable “x” will never ever become other than 0.

In fig-2, the compiler will never ever optimized the code (else part) because, by declaring x as volatile compiler comes to know that this variable can change at any point of time. So compiler does not ignore the else part.

Why/When do we need it?

In following case we need to use volatile variable.

    Memory-mapped peripheral registers

    Global variables modified by an interrupt service routine

    Global variables within a multi-threaded application

If we do not use volatile qualifier the following problems may arise:

    Code that works fine-until you turn optimization on

    Code that works fine-as long as interrupts are disabled

    Flaky hardware drivers

    Tasks that work fine in isolation-yet crash when another task is enabled

e.g

static int var; void test(void) {

var = 1; while (var != 10) continue;

}

The above code sets the value in var to 1. It then starts to poll that value in a loop until the value of var becomes 10.

An optimizing compiler will notice that no other code can possibly change the value stored in ‘var’, and therefore assume that it will remain equal to 0 at all times. The compiler will then replace the function body with an infinite loop,similar to this:

void test_opt(void) {

var = 0; while (TRUE)/*#define TRUE 1*/ continue; }

How do we declare a volatile variable?

Include the keyword volatile before or after the data type in the variable.

volatile int var;

or

int volatile var;

Pointer to a volatile variable

volatile int * var;

int volatile * var;

Above statements implicate ‘var’ is a pointer to a volatile integer.

Volatile pointers to non-volatile variables

int * volatile var; –> Here var is a volatile pointer to a non-volatile variable/object. This type of pointer are very rarely used in embedded programming.

Volatile pointers to volatile variables

int volatile * volatile var;

If we qualify a struct or union with a volatile qualifier, then the entire contents of the struct/union becomes volatile. We can also apply the volatile qualifier to the individual members of the struct/union.

Does my program/code really need this keyword?

As we have already seen above, when do we need volatile keyword, then below are some cases which really makes you understand.

Usages of volatile qualifier

Peripheral registers

I would like to insist you to visit this below website for better understanding

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html

Most embedded systems consist of a handful of peripherals devices. The value of the registers of these peripheral devices may change asynchronously. Lets say there is an 8-bit status register at address 0x1234 in any hypothetical device. What we need to do is to poll this status register until it becomes non-zero. The following code snippet is an incorrect implementation of this scenario/requirement:

UINT1 * ptr = (UINT1 *) 0x1234;

// Wait for register to become non-zero.

while (*ptr == 0);

// Do something else.

Now no code in proximity attempts to change the value in the register whose address(0x1234) is kept in the ‘ptr’ pointer. A typical optimizing compiler(if optimization is turned ON) will optimize the above code as below:

mov ptr, #0x1234 -> move address 0x1234 to ptr

mov a, @ptr -> move whatever stored at ‘ptr’ to accumulator

loop bz loop -> go into infinite loop

What the assumes while optimizing the code is easy to interpret. It simply takes the value stored at the address location 0x1234(which is stored in ‘ptr’) into accumulator and it never updates this value as because apparently the value at the address 0x1234 never gets changed(by any nearby code). So, as the code suggests, the compiler replaces it with an infinite loop (comparing the initial zero value stored at the address 0x1234 with a constant ‘zero’). As the value stored at this address would initially be zero and it is never updated, this loop goes forever. The code beyond this point would never get executed and the system would go into a hanged state.

So what we essentially need to do here is to force the compiler to update the value stored at the address 0x1234 whenever it does the comparison operation. The volatile qualifier does the trick for us. Look at the code snippet below:

UINT1 volatile * ptr = (UINT1 volatile *) 0x1234;

The assembly for the above code should be:

mov ptr, #0x1234 -> move the address 0x1234 to ptr

loop mov a, @ptr -> move whatever stored @address to accumulator

bz loop -> branch to loop if accumulator is zero

So now at every loop the actual value stored at the address 0x1234(which is stored in the ‘ptr’) is fetched from the peripheral memory and checked whether it’s zero or non-zero; as soon as the code finds the value to be non-zero the loop breaks. And that’s what we wanted.

Subtler problems tend to arise with registers that have special properties. For instance, a lot of peripherals contain registers that are cleared simply by reading them. Extra (or fewer) reads than you are intending can cause quite unexpected results in these cases.

ISR(Interrupt Service Routine)

Sometimes we check a global variable in the main code and the variable is only changed by the interrupt service routine. Lets say a serial port interrupt tests each received character to see if it is an ETX character (presumably signifying the end of a message). If the character is an ETX, the serial port ISR sets a particular variable, say ‘etx_rcvd’. And from the main code somewhere else this ‘etx_rcvd’ is checked in a loop and untill it becomes TRUE the code waits at this loop. Now lets check the code snippet below:

int etx_rcvd = FALSE;

void main()

{



while (!ext_rcvd)

{

// Wait

}



}

interrupt void rx_isr(void)

{



if (ETX == rx_char)

{

etx_rcvd = TRUE;

}



}

This code may work with optimization turned off. But almost all the optimizing compiler would optimize this code

to something which is not intended here. Because the compiler doesn’t even have any hint that etx_rcvd can be

changed outside the code somewhere( as we saw within the serial port ISR). So the compiler assumes the

expression !ext_rcvd would always be true and would replace the code with infinite loop. Consequently the

system would never be able to exit the while loop. All the code after the while loop may even be removed by the

optimizer or never be reached by the program. Some compiler may throw a warning, or some may not, depends

completely on the particular compiler.

The solution is to declare the variable etx_rcvd to be volatile. Then all of your problems (well, some of them

anyway) will disappear.

Multi-threaded applications

Often tasks/threads involved in a multi-threaded application communicate via a shared memory location i.e.

through a global variable. Well, a compiler does not have any idea about preemptive scheduling or to say, context

switching or whatsoever. So this is sort of same problem as we discussed in the case of an interrupt service routine

changing the peripheral memory register. Embedded Systems Programmer has to take care that all shared global

variables in an multi threaded environment be declared volatile. For example:

int cntr;

void task1(void)

{

cntr = 0;

while (cntr == 0)

{

sleep(1);

}



}

void task2(void)

{



cntr++;

sleep(10);



}

This code will likely fail once the compiler’s optimizer is enabled. Declaring ‘cntr’ to be volatile is the proper way to solve the problem.

Some compilers allow you to implicitly declare all variables as volatile. Resist this temptation, since it is essentially a substitute for thought. It also leads to potentially less efficient code.

Can you have constant volatile variable? You can have a constant pointer to a volatile variable but not a constant volatile variable.

Consider the following two blocks of a program, where second block is the same as first but with volatile keyword. Gray text between lines of C code means i386/AMD64 assembler compiled from this code.

{

BOOL flag = TRUE;

while( flag );

repeat:

jmp repeat

}

{

volatile BOOL flag = TRUE;

mov dword ptr [flag], 1

while( flag );

repeat:

mov eax, dword ptr [flag]

test eax, eax

jne repeat

}

In first block variable ‘flag’ could be cached by compiler into a CPU register, because it does not have volatilequalifier. Because no one will change value at a register, program will hang in an infinite loop (yes, all code belowthis block is unreachable code, and compiler such as Microsoft Visual C++ knows about it). Also this loop wasoptimized in equivalent program with the same infinite loop, but without involving variable initialization andfetching. ‘jmp label’ means the same as ‘goto label’ in C code.

Second block have volatile qualifier and have more complex assembler output (initializing ‘flag’ with ‘mov’instruction, in a loop fetching this flag into CPU register ‘eax’ with a ‘mov’ instruction, comparing fetched valuewith zero with ‘test’ instruction, and returning to the beginning of the loop if ‘flag’ was not equal to zero. ‘jne’ means’goto if not equal’). This is all because volatile keyword prohibits compiler to cache variable value into CPUregister, and it is fetched in all loop iterations. Such code is not always is an infinite loop, because another threadin the same program potentially could change value of variable ‘flag’ and first thread will exit the loop.

It is important to understand that volatile keyword is just a directive for compiler and it works only at a compile-time.

For example, the fact of using interlocked operation differs from just a compiler option, since special assembler commands are produced. Thus, interlocked instructions are most like to hardware directives, and they work at a run-time.

Can A Variable be both Volatile and Const?

const means the program can not modify the value

volatile means the value may be arbitrarily modified outside the program.

the two are separate and not mutually exclusive.

use them together, for instance, in the case of reading a hardware status register. const prevents the value from being stomped on, while volatile tells the compiler that this value can be changed at any time external to the program.

This,

const volatile <type> <variablename>

will thus satisfy both requirements and prevent an optimizing compiler from incorrectly optimizing the code, that it would do if only “const” were used.

Conclusion

1. A volatile variable can be changed by the background routine of preprocessor. This background routine may be interrupt signals by microprocessor, threads, real times clocks etc.

2. In simple word we can say a value volatile variable which has stored in the memory can be by any external sources.

3. Whenever compiler encounter any reference of volatile variable is always load the value of variable from memory so that if any external source has modified the value in the memory complier will get its updated value.

4. Working principle of volatile variable is opposite to the register variable in c. Hence volatile variables take more execution time than non-volatile variables.

I hope this article might have helped you in understanding volatile keyword.

No comments:

Post a Comment

commnet here