상세 컨텐츠

본문 제목

[C] 임베디드 시스템에서 volatile 키워드를 사용하는 이유

C

by Wanderer Kim 2019. 7. 16. 14:44

본문

728x90

MCU 프로그래밍을 하다 보면 레지스터를 제어하기 위해 같은 주소에 여러번 값을 여러번 쓰는 경우가 빈번하고, 이런 경우 코드를 보면 앞에 volatile 키워드를 사용하는 경우를 볼 수 있다. 

처음 이런 코드를 보면 언뜻 C언어를 배울 때 배웠던 volatile 키워드의 용법과 연결이 되지 않는다. 그래서 volatile이 왜 쓰이는지 같이 살펴보도록 하자.

 

volatile?

 

우선 volatile 키워드가 무엇인지 알아보자. 변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 한다. 즉, volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다.

 

<syntax>

volatile [type] [variable_name];

 

사용법은 위에서 볼 수 있다시피 매우 간단하다. 

그럼 이제부터 왜 volatile 키워드가 임베디드 시스템 프로그래밍에서 필요한지 알아보자.

 

volatile의 필요성

 

volatile 키워드가 왜 필요한지 이해하려면 먼저 일반적인 C/C++ 컴파일러가 어떤 종류의 최적화를 수행하는지 알아야 한다. 가상의 하드웨어를 제어하기 위한 다음 코드를 살펴보자.

 

*(unsigned int *)0x8C0F = 0x8001;

*(unsigned int *)0x8C0F = 0x8002;

*(unsigned int *)0x8C0F = 0x8003;

*(unsigned int *)0x8C0F = 0x8004;

*(unsigned int *)0x8C0F = 0x8005;

 

위 코드를 부면 5번의 메모리 쓰기가 모두 같은 주소인 0x8C0F에 이루어짐을 알 수 있다. 따라서 이 코드를 수행한 수에는 최종적으로 0x8C0F 주소에 0x8005값만 남아있을 것이다. 이 경우 컴파일러는 최적화를 위해 이전의 코드를 실행 안하고 맨마지막 코드만 실행 시키는데, 일박적인 코드라면 이런 최적화를 통해 수행 속도 면에서 이득을 보게 된다.

 

하지만 이 코드가 메모리 주소에 연결된 하드웨어 레지스터에 값을 쓰는 프로그램이라면 이야기가 달라진다. 각각의 쓰기가 하드웨어에 특정 명령을 전달하는 것이므로, 주소가 같다는 이유만으로 중복되는 쓰기 명령을 없애 버리면 하드웨어가 오동작하게 될 것이다. 이런 경우 유용하게 사용할 수 있는 키워드가 volatile이다. 변수를 volatile 타입으로 지정하면 앞서 설명한 최적화를 수행하지 않고 모든 메모리 쓰기를 지정한 대로 수행한다.

 

*(volatile unsigned int *)0x8C0F = 0x8001;

*(volatile unsigned int *)0x8C0F = 0x8002;

*(volatile unsigned int *)0x8C0F = 0x8003;

*(volatile unsigned int *)0x8C0F = 0x8004;

*(volatile unsigned int *)0x8C0F = 0x8005;

 

 

또한 특정 메모리 수조에서 하드웨어 레지스터 값을 읽어오는 프로그램의 경우도 마찬가지다. 아래 코드의 경우 같은 주소에서 반복적으로 메모리를 읽으므로, 최적화 후에는 buf[i] = *p;에서 *p를 한 번만 읽어온 후에 그 값을 반복해 사용할 것이다. 하지만 volatile 키워드가 있는 경우 *p를 참조할 때마다 직접 메모리에서  새 값을 가져온다.

이 경우는 하드웨어가 메모리 0x8C0F 번지 값을 새롭게 변경해 주는 경우이다.

 

1
2
3
4
5
6
7
8
9
10
void foo(char *buf, int size)
{
    int i;
    volatile char *= (volatile char*)0x8C0F;
    for(i=0; i<size; i++)
    {
        buf[i] = *p;
        ...
    }
}
cs

 

volatile 키워드는 크게 3가지 경우에 흔히 사용된다.

(1) MIMO(Memory-mapped I/O)

(2) 인터럽트 서비스 루틴(Interrupt Service Routine)의 사용

(3) 멀티 쓰레드 환경

 

세 가지 모드 공통접은 현재 프로그램의 수행 흐름과 상관없이 외부 요인이 변수 값을 변경할 수 있다는 점이다. 인터럽트 서비스 루틴이나 멀티 쓰레드 프로그램의 경우 일반적으로 스택에 할당하는 지역 변수는 공유하지 않으므로, 서로 공유되는 전역 변수의 경우에만 필요에 따라 volatile을 사용하면 된다.

 

정리

 

지금까지 C/C++의 volatile 키워드가 왜 임베디드 시스템 프로그래밍에서 사용되는지 살펴보았다.

volatile 키워드는 미묘한 키워드라 잘 알고 쓰면 큰 도움이 될 수 있지만, 또한 여러 가지 문제를 일으키는 근원이 되기도 한다. 특히 명확한 표준이 없으므로 본인이 사용하는 C/C++ 컴파일러의 메뉴얼을 꼼꼼히 읽고 volatile을 어떻게 지원하는지 파악하는 게 중요하다.

반응형

'C' 카테고리의 다른 글

[C] 2차원 배열 동적할당  (0) 2019.07.16

관련글 더보기

댓글 영역