Join our subscribers list to get the latest news, updates and special offers directly in your inbox
Overview
The volatile keyword in C is used to inform the compiler that a variable's value may change at any time, without any action being taken by the code in which it appears. This is crucial when working with hardware registers, memory-mapped I/O, or variables that are modified by an interrupt service routine (ISR) or another thread in a multi-threaded environment.
volatile
Suppose you are working with a microcontroller and you need to read the status of a hardware register that is memory-mapped. The value of this register can change at any time due to external events, such as hardware signals.
#include "stdio.h" #define STATUS_REGISTER_ADDRESS 0x40001000U volatile uint32_t *status_register = (volatile uint32_t *)STATUS_REGISTER_ADDRESS; int main(void) { // Wait until a specific bit in the status register is set while ((*status_register & 0x01) == 0) { // Do nothing, just wait } // Proceed when the bit is set // For example, start processing the received data // ... return 0; }
volatile uint32_t *status_register: The volatile keyword is used here to tell the compiler that the value of *status_register may change at any time, even if the program itself does not modify it. This prevents the compiler from optimizing out the while loop based on an assumption that the value of *status_register does not change.
volatile uint32_t *status_register
*status_register
while
while ((*status_register & 0x01) == 0): The program continuously checks whether the least significant bit of the status register is set. If the volatile keyword were not used, the compiler might assume that *status_register does not change and optimize the loop in a way that prevents it from working as intended.
while ((*status_register & 0x01) == 0)
When the *status_register is accessed in the while loop, the CPU will read the value stored at the memory address 0x40001000. This read operation might involve loading the value into a CPU register (e.g., r0 or r1 in ARM assembly) before performing the bitwise AND operation to check if the specific bit is set.
ldr r0, = STATUS_REGISTER_ADDRESS ; Load the address of the status register into r0ldr r1, [r0] ; Load the value at the address (status register) into r1tst r1, #0x01 ; Test if the least significant bit is setbeq wait ; If the bit is not set, branch to wait
1. Without the volatile keyword, the compiler might optimize the code by assuming that the value of *status_register does not change within the loop, possibly storing it in a register and reusing the value without re-reading it from memory. This would cause the loop to potentially never exit if the hardware updates the status register but the CPU does not re-read it.
2. By declaring status_register as volatile, you ensure that each iteration of the loop reads the current value of the status register directly from the memory-mapped address, reflecting any changes made by the hardware.
Volatile
Without the volatile keyword, the compiler might optimize the loop to something like this:
if ((*status_register & 0x01) == 0) { while (1) { // Infinite loop, never checking the register again } }
This would result in a bug where the program never detects changes in the status register, because the compiler assumes the register's value does not change within the loop.
EmbeddedWala
EmbeddedWala Apr 27, 2023 0 17.1K
EmbeddedWala Jun 14, 2023 0 16.1K
EmbeddedWala Apr 26, 2023 0 15.3K
EmbeddedWala Aug 30, 2022 0 13.9K
EmbeddedWala Apr 27, 2023 0 13.7K
EmbeddedWala Jun 19, 2022 0 4.2K
This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies Find out more here