본문 바로가기

Embedded Programming/C/C++

volatile 변수의 쓰임

volatile 키워드는 한 마디로 얘기하면 컴파일러가 메모리 접근을 최소화 시키는 최적화를 하지 못하게 막는 

예약어이다.

 의미없는 반복문 같은 경우는 컴파일러에 따라서 캐쉬 메모리를 이용한다던가 레지스터 내에서 모든 연산을 마치고 값을 다시 메모리로 계산값을 메모리(즉, 변수)에 저장하는 최적화를 진행한다. volatile은 이러한 최적화를 금지하고 FM대로 메모리에 접근하고 레지스터로 이를 읽어와 연산 후 메모리로 다시 저장시키는 방식으로 처리하라는 선언이다.

volatile 선언은 크게 두 가지 의미가 있다.

첫째는 컴파일러가 코드를 최적화시키는 것을 방지하겠다는 것이다. 

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

시간을 끌기위해 사용된 위의 무한루프는 그냥 컴파일러는 레지스터로 초기값을 읽어와서 연산 후 결과를 

바로 iCount 변수에 대입해버린다. (iCount = 1000000)

그러므로 프로그래머가 의도한 만큼의 딜레이를 줄 수 없게된다.


아래 동영상은 위의 소스에서 

volatile unsigned int iCount = 0;

위의 iCount변수에 volatile 키워드를 삽입했을 때와 하지 않았을 때의 차이를 보여주는 실습이다.


< volatile을 사용했을 경우 >

원하던 대로 불이 깜빡였다 꺼졌다를 반복한다.


< volatile을 쓰지 않았을 경우 >

불이 한번만 들어왔다 꺼져버린다.



두번째 역할로는 

*((volatile unsigned int *)0xFFFFF410) = 1;

이 코드가 좋은 예가 되는데 이는 위의 메모리주소 0xFFFFF410 에 접근할 때 캐쉬메모리 같은

중간 단계에 값을 대입해놓지 말고 직접 접근만을 허용해라는 뜻이다.

만약 위의 주소값에 volatile을 붙여주지 않는다면, 외부의 센서같은 I/O에서 값이 변화되어도

이미 레지스터로 들어온 값만 체크하고 실제 메모리에 저장된 값을 계속 읽어오지 않으므로 

변화를 감지하지 못하는 것을 막을 수 있다.


첫번째 두번째 역할을 모두 종합해봤을 때 결론을 내리자면, 

volatile 키워드는 메모리에서 cpu 레지스터로 불러왔다 연산 후 다시 메모리에 

그 결과값을 저장하는 과정을 컴파일러나 시스템에서 생략하지 못하도록 막아서

원칙적인 수행과정을 거치게 만드는 키워드라고 할 수 있다.