volatile
- 기본적으로 C/C++ 컴파일러는 최적화의 기능을 가지고 있다. 비효율적이거나 불필요한 연산은 생략하고 코드를 최적화시킨다.
- 변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 만든다.
사용방법
기본적인 변수 사용
int a
static int a
volatile 변수 사용
volatile int a
static volatile int a
사용하는 이유
1. 컴파일러에서 최적화를 방지한다.
2. 후속 프로그램이 변수를 다룰 때 레지스터가 아닌 메모리에서 읽어오도록 한다.
3. 만약 16비트 마이크로 컨트롤러에서 32비트(int, long) 변수를 읽어오려면 두 번에 걸쳐 메모리를 읽어야 하는데, 그 변수를 읽는 동안에 갑자기 값이 바뀔 수 있으므로 읽히는 동안 비트를 건드리지 못하게 한다.
C 소스코드
int main()
{
int i = 0;
while (i < 10)
{
i++;
}
printf("%d\n", i);
}
위와 같이 작성된 코드는 컴파일러의 최적화로 인해 어셈블리어로 아래와 같은 구조로 진행한다.
int main()
{
int i = 10; //while문을 제거하고 10을 할당
printf("%d\n", i);
}
예시 1
int main()
{
volatile unsigned int iCount = 0;
*((volatile unsigned int *)0xFFFFF410) = 1; // output activate PIO_OER
*((volatile unsigned int *)0xFFFFF400) = 1; // pin activate PIO_PER
while (1)
{
for(iCount=0;1000000 >= iCount; ++iCount);
*((volatile unsigned int *)0xFFFFF434) = 1; // Enable 0V output PIO_CODR
for(iCount=0;1000000 >= iCount; ++iCount);
*((volatile unsigned int *)0xFFFFF430) = 1; // Enable 3.3V output PIO_SODR
}
return 0;
}
위의 코드에서 iCount에 volatile 키워드를 없애버린다면,
for(iCount=0;1000000 >= iCount; ++iCount);
시간을 끌기 위해 사용된 위의 무한루프를 컴파일러는 레지스터로 초기값을 읽어와 연산 후 결과를 바로 iCoutn 변수에 대입해버린다.
(iCount = 1000000)
그러므로 사용자가 의도한 만큼의 딜레이를 줄 수 없게 된다.
예시 2
*((volatile unsigned int *)0xFFFFF410) = 1;
위의 0xFFFFF410에 접근할 때 캐쉬메모리같은 중간 단계에 값을 대입해놓지말고 직접접근만을 허용하라는 뜻이다.
위의 주소값에 volatile을 붙여주지 않는다면, 외부의 센서같은 I/O에서 값이 변화되어도 이미 레지스터로 들어온 값만 체크하고 실제 메모리에 저장된 값을 계속 읽어오지 않으므로 변화를 감지하지 못한다.
결론: volatile 키워드는 값을 메모리에서 cpu 레지스터로 불러왔다 연산 후 다시 메모리에 그 결괏값을 저장하는 과정을 컴파일러나 시스템에서 생략(메모리 접근 최소화)하지 못하도록 최적화에서 제외시킨다.