Spread the love

volatile variable in c and embedded systemI think, you might have heard of Volatile keyword in c and embedded system frequently. 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. Go through all below tabs step by step


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.

What is the significance of volatile keyword?

Volatile keyword is used to inform the compiler not to 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. Suppose, we have two code segment like below

Code 1:

/* Optimization code snippet 1 */ 
#include<stdio.h> 

int x = 0; 

int main() 
{ 

	if (x == 0) // This condition is always true 
	{ 
		printf(" x = 0 \n"); 
	} 
	else 		// Else part will be optimized because x will never other than 0 
	{ 
		printf(" x != 0 \n"); 
	} 
	return 0; 
} 

Code 2:

/* Optimization code snippet 2 */
#include<stdio.h>

volatile int x; 	/* volatile Keyword*/

int main()
{
	
	x = 0;
	
	if (x == 0)
	{
	printf(" x = 0 \n");
	}
	else		// Now compiler never optimize else part because the variable is declared as volatile	
	{	
	printf(" x != 0 \n");
	}
	
	return 0;
}

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

In code 2, the compiler will never 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 and consider it in final executable.

Why/When do we need volatile ?

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( ) 
{
	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 (1)
		continue; 
}

How do we declare a volatile variable?

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

/* Declaring volatile variable */

volatile int var;

or

int volatile var;

/* Above both statement are same */

Pointer to a volatile variable

/* 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

/* 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

/* 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 next tab consist some cases which really makes you understand.

Usages of volatile qualifier

1). Peripheral registers

Most embedded systems consist of a handful of peripherals devices. The value of the registers of these peripheral devices may change asynchronously. Let 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 blank 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 at 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.

2). 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;

int main()
{
	// Do something	
	while (!ext_rcvd)
	{
		// Wait
	}
	// Do something	
	
	return 0;
}

interrupt void rx_isr(void)
{
	// Do something	
	if (ETX == rx_char)
	{
		etx_rcvd = TRUE;
	}
	// Do something	
}

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);
	}
	// Do something
}

void task2(void)
{
	// Do something
	cntr++;
	
	sleep(10);
	// Do something
}

                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.

/* Code snippest 1 */

{
	BOOL flag = TRUE;
	
	while( flag );
	
	repeat:
	
	jmp repeat
}

/* Code snippest 2 */
{
	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 volatile qualifier. Because no one will change value at a register, program will hang in an infinite loop (yes, all code below this block is unreachable code, and compiler such as Microsoft Visual C++ knows about it). Also this loop was optimized in equivalent program with the same infinite loop, but without involving variable initialization and fetching. ‘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 value with 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 CPU register, and it is fetched in all loop iterations. Such code is not always is an infinite loop, because another thread in 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 before compilation, while volatile tells the compiler that this value can be changed at any time external to the program.

This,

const volatile <type> <variable name>

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 pre-processor. 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 compiler 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.

Reference

http://www.eetimes.com/discussion/other/4023801/Introduction-to-the-Volatile-Keyword

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

http://en.wikibooks.org/wiki/Embedded_Systems/C_Programming

http://www.cognitus.net/html/tutorial/usingVolatile.html

http://www.netrino.com/node/80

http://icecube.wisc.edu/~dglo/c_class/const_vol.html

http://www.daniweb.com/software-development/c/threads/187964

Suggested Reading

  1. Difference between const char *p, char const *p and char *const p
  2. How to do AES-128 bit CBC mode encryption in c programming code with OpenSSL
  3. How to do Triple-DES CBC mode encryption example in c programming with OpenSSL