
[메모리를 절약하기 위해서]
메가바이트 단위의 메모리를 갖고 있는 데스크톱 시스템과는 달리 언제가 다루었던 것처럼 임베디드 시스템은 제한된 메모리를 가지고 있습니다. 메모리 공간을 절약하는 것은 몇 장에 걸쳐서 다뤄야 할 주제인데요 지금은 그저 임베디드 시스템에 한정된 몇 가지 고려사항에 대해서만 얘기해봅시다.
임베디드 시스템에서는 코드 공간을 짧게 할 수도 있고, 데이터 공간을 짧게 하는 것도 가능하고, 둘 다 짧게 해 버리는 일도 가능합니다. 코드는 ROM에 저장되고 데이터는 RAM에 저장되는 탓에 두 가지는 서로 교환될 수 없죠. 메모리를 줄이는 작업을 한다면 정확한 종류의 메모리를 절약하도록 해야 합니다. 가령 데이터 구조를 압축하는 일은 데이터 공간을 절약하기는 하지만 실행할 때 데이터를 다시 풀어야 하기 때문에 코드 공간이 늘어날 것입니다. 데이터 공간을 절약하는 친숙한 방법 중의 하나는 효율적인 데이터 구조로 데이터를 포개서 넣는 겁니다. RTOS를 사용한다면, 특별한 고려 중의 하나는 각 태스크는 스택을 위한 메모리가 필요하다는 겁니다. 그러므로 시스템이 필요한 양만큼의 스택을 할당하도록 해야 합니다. 태스크가 얼마나 스택 메모리를 필요로 하는지 알기 위한 시작은 코드를 조사하는 겁니다. 각 함수의 호출, 함수의 인자와 지역 변수들은 사용하는 마이크로프로세서와 컴파일러에 따라 어떤 정해진 수의 바이트를 스택에서 사용합니다. 또한, 더 깊은 연관을 갖는 함수의 중첩과 인자 그러고 지역 변수 등을 알아보기 위해서 코드를 조사할 수도 있습니다. 그러고 나서 최악의 경우에는 인터럽트 루틴의 중첩과 알토스 매뉴얼에서 찾을 수 있는 알토스 자신을 위한 공간을 더해야 합니다. 기본 이론은 단순합니다. 그러나 이 방법을 수행하기 위해서는 정말 엄청난 어려움이 있다는 것을 쉽게 알 수 있습니다. 두 번째 방법은 실험을 통하는 겁니다. 처음에 스택에 미리 알고 있는 패턴으로 데이터를 채워 넣고 주기적으로 시스템을 실행시켰다가 멈추고, 얼마나 많은 데이터가 스택에 쓰였는지를 조사하는 겁니다. 이 방법은 수행하기에는 쉽지만, 실험 도중에 최악의 경우가 꼭 발생하리라는 보장은 없습니다.
그러면 코드 공간을 절약하는 7가지 방법을 말해보겠습니다. 이 중 분명한 단점을 가지고 있는 방법도 있습니다. 그러니 코드를 어떻게라도 롬에다가 넣을 필요가 있을 때만 사용하는 것이 바람직하다고 미리 말씀드립니다.
- 같은 일을 하는 두 개의 함수를 사용하지 않는다는 것을 확인해야 함. 코드가 C의 표준 라이브러리인 memcpy 함수를 28번 호출하고, 매우 비슷한 표준 라이브러리인 memmove 함수를 한 번 호출하는 경우, memmove 함수를 memcpy 함수 호출로 바꿀 수 있는지 확인하기 위해서 memmove 함수를 호출하는 부분을 제거해야 함. 반대로 memcpy를 28번 호출하는 부분을 memmove로 바꾸고 memcpy를 호출하는 부분을 제거하는 것도 가능함. 사용하는 링커나 로케이터로부터의 리스트를 보고 이런 방법으로 어떤 함수를 제거할 가치가 있는지 찾아봐야 함
- 개발 툴이 협조를 안 하고 있는지 않은지 점검해야 함. memcpy를 호출하는 것은 memmove, memset, memcmp, strcpy, strncpy, strset 아니면 다른 함수들을 전혀 사용하지 않더라도 그룹으로 같이 묶어서 첨가시킬 수도 있음. 개발 툴의 매뉴얼에는 이것을 방지하는 방법이 나와 있을 것임. 그렇지 안다면, mymemcpy처럼 memcpy와 같은 기능을 하는 독자적인 함수를 만들어서 하용하는 것을 고려해야 함. 실제 임베디드 환경에서 C의 표준 라이브러리를 사용하지 못하는 경우가 많음. 특히 메모리 관련 함수들은 거의 사용하지 못한다고 봄. 임베디드 시스템을 개발하려고 마음먹었다면 필요한 함수들은 스스로 만들어서 사용할 각오를 어느 정도 하고 있어야 편함.
- 필요로 하는 기능만 담도록 RTOS를 설정해야 함. 가령 프로그램이 파이프를 사용하지 않는데, RTOS의 파이프 관련 함수를 남겨 놓으면 코드 공간과, 정적인 데이터를 필요로 하는 경우는 데이터 공간마저도 낭비할 가능성이 생김.
- 크로스 컴파일러가 생성하는 어셈블리 언어의 리스트를 보고 특정한 C구문이 많은 수의 명령어로 변환되지 않는지 조사해야 함. 이런 조사를 해보면 종종 놀라운 사실이 발견됨. 특정 코드는 a_sMyData 구조체 배열의 iMember를 초기화하는 세 가지 방법을 보여주는데 세 가지 모두 같은 것을 하는 것인데도, 컴파일러는 극히 다른 양의 코드를 만들어냄. 어느 것이 가장 좋은 방법인지 유추하려고 하지 말고 그냥 컴파일하고 리스트를 조사하는 게 이로움.
- 스택에 있는 변수를 사용하는 대신 정적 변수 사용을 고려해야 함. 많은 마이크로프로세서는 스택 변수보다 정적 변수를 읽고 쓸 때 더 적은 명령어를 사용할 수 있음. 만약에 이런 종류의 마이크로프로세서를 사용한다면 지역 변수를 정적 변수로 선언해서 공간을 줄일 수 있음. 코드가 함수에서 빈번하게 사용되는 구조체에 대한 포인터를 함수의 인자로 받는 경우 그 구조체를 정적인 구조체에 복사하는 것이 코드 공간을 절약해 줄 것임.
- 만약에 8비트 프로세서를 사용한다면, int형 변수 대신 char형 변수를 사용하는 것을 고려해야 함. 간단히 8비트 프로세서에게는 int형 변수의 연산이 char형 변수의 연산보다 훨씬 더 복잡하기 때문임. for 구문과 배열 참조, 그리고 -1로 곱하는 모든 것이 연산을 필요로 함.
- 모든 것이 실패한다면, 많은 두통의 대가로 많은 공간을 절약할 수 있는 어셈블리 언어로 코드를 재작성하는 방법이 있음. 이렇게 하기 전에, 약간의 코드를 어셈블리 언어로 변경시켜서 얼마나 많은 공간을 절약할 수 있는지 그리고 코드를 쓰고 유지하는데 얼마의 노력이 드는지도 감을 얻도록 해보길 권함.
[파워 절약하기]
어떤 임베디드 시스템은 배터리 전력으로 구동되고, 이런 시스템에서는 배터리 수명이 가장 중요한 문제가 되는 경우가 많음. 배터리 전력을 절감하는 가장 기본적인 방법은 가능한 시스템의 모든 부분을 끄는 것입니다. 마이크로프로세서 역시 포함됨. 이런 것을 수행하기 위한 방법은 시스템마다 굉장히 다르기 때문에 이번에는 일반적으로 생각되는 부분만 언급할 겁니다.
대부분의 임베디드 시스템 마이크로프로세서는 적어도 하나의 전력 절약 모드를 가지고 있습니다. 많은 마이크로프로세서는 하나 이상을 가지고 있고요. 일반적으로 소프트웨어가 특별한 명령을 사용하거나 마이크로프로세서의 제어 레지스터에 값을 써넣어서 이런 모드로 들어가는 것이 가능합니다. 모드는 슬립, 저전력, 대기, 스탠바이 따위의 이름을 가지고 있습니다. 각 마이크로프로세서마다 다르기 때문에 사용하는 전력 절약 모드가 어떤 특징을 가지고 있는지 알려면 매뉴얼을 꼭 봐야 합니다. 매뉴얼 가끔 한 번씩 나오는데 이토록 중요하니 뭘 사더라도 설명서는 꼭 버리지 않기를 바랍니다.
가장 일반적인 전력 절약 모드는 마이크로프로세서가 명령을 수행하는 것을 멈추고, 내장된 장치들도 멈추고, 클럭 회로를 멈추는 겁니다. 이 모드는 많은 전력을 절약하긴 하지만, 일반적으로 이 모드에서 빠져나오려면 리셋을 해서 마이크로프로세서를 다시 시작시키는 방법이 유일합니다. 이건 다시 말해서 하드웨어 엔지니어가 적절한 순간에 이런 일을 하도록 약간의 부가 회로를 설계해야 한다는 겁니다. 또한 마이크로프로세서가 전력 절약 모드를 벗어나면, 프로그램이 다시 처음부터 실행되어야 한다는 것을 의미합니다. 소프트웨어는 처음 실행되는 것인지 아니면 전력 절약 모드에서 복귀한 것인지를 판단해야 합니다. 간단한 방법 중의 하나는 0x0100번지에 0x9283 ab3c를 쓰는 것처럼 식별할 수 있는 값을 램에다가 써넣는 것이죠. 시스템이 시작할 때마다, 프로그램은 0x100 번지를 점검하게 됩니다. 시스템이 꺼지면 0x0100은 쓰레기 값을 가지게 될 텐데요. 시스템이 전력 절약 모드에서 깨어났다면, 프로그램은 0x9283ab3c 값을 발견하게 될 겁니다. 더 복잡한 방법도 얘기해볼까요? 정적 램은 마이크로프로세서가 명령어를 수행하지 않을 때 매우 적은 전력을 사용합니다. 그래서 마이크로프로세서를 슬립모드로 만들어도 정적 램은 그대로 두는 것이 일반적인 형태입니다.
또 다른 일반적인 전력 절약 모드는 마이크로프로세서가 명령을 실행시키는 것을 중지시키고 보드에 있는 주변 장치들은 계속 작동하게 두는 겁니다. 어떤 인터럽트든지 마이크로프로세서를 다시 시작하게 하고, 마이크로프로세서는 관련된 인터럽트 루틴을 실행한 후에 태스크 코드에서 슬립 모드로 들어가기 직전의 명령어의 다음 명령어를 수행합니다. 이 모드는 위에서 다뤘던 것보다 전력을 덜 절약하지만 다른 특별한 하드웨어가 필요하지 않으며 소프트웨어를 다시 실행시키는 수고를 하지 않아도 됩니다. 다른 것들이 동작하고 있을 때도 이 전력 절약 모드를 사용할 수 있습니다. 내장된 DMA 채널은 데이터를 계속 UART에 보낼 수 있고, 타이머는 계속 실행되다가 인터럽트를 발생시킬 것이고, 마이크로프로세서를 깨울 겁니다.
마이크로프로세서를 전력 절약 모드로 들어가게 할 계획이라면, 빠른 소프트웨어를 작성하면 좋습니다. 소프트웨어가 더 빨리 작업을 마치는 경우, 더 일찍 마이크로프로세서를 전력 절약 모드에 넣고 배터리를 절약할 테죠.
또 다른 방법은 전체 시스템의 전원을 끄고 사용자가 필요할 때 다시 켜도록 만드는 겁니다. 무선 바코드 스캐너는 그런 시스템의 한 예입니다. 스캐너는 사용자가 다시 방아쇠를 당길 때까지 스스로 전원을 차단합니다. 방아쇠를 당기면 전체 시스템은 다시 돌아옵니다. 이런 시스템을 계획한다면, 하드웨어 엔지니어는 스프트웨어가 전체 시스템의 전원을 끌 수 있도록 하고 사용자가 다시 켤 수 있도록 하는 방법을 제공해야 합니다. 이 방법은 전력 소모를 거의 없게 만들 수 있긴 하지만 전원이 차단되면 램은 데이터를 잃어버리기 때문에, 스프트웨어는 EEPROM이나 플래시에 시스템이 다시 시작할 때 필요한 정보를 넣어둬야 합니다.
시스템이 마이크로프로세서 이외의 부품을 끌 필요가 있는 경우, 하드웨어 엔지니어는 소프트웨어가 그렇게 하도록 하는 방법을 제공해야 합니다. 부품들의 데이터 시트에는 어떤 것이 전원을 차단할 가치가 있을 정도로 전력을 많이 먹는지 나와 있습니다. 일반적으로 하이에서 로우로 반복되는 신호들과 전원을 다시 공급받을 때 대부분의 전력을 소모하곤 합니다.
'IT > 임베디드 시스템' 카테고리의 다른 글
임베디드 소프트웨어 - 링커/로케이터1 (주소 재지정, 프로그램 컴포넌트 배치) (0) | 2020.06.19 |
---|---|
호스트와 타깃 시스템 - 크로스 컴파일러, 크로스 어셈블러, 툴 체인 (0) | 2020.06.19 |
세마포어와 큐의 캡슐화 & 하드리얼타임스케줄링 짚고 넘어가기 (0) | 2020.06.19 |
임베디드 시스템 설계 예제3 - 시스템/공유데이터 처리 그리고 결론 (0) | 2020.06.18 |
임베디드 시스템 설계 예제2 - RTOS 사용 결정과 태스크로 작업 분배 (0) | 2020.06.18 |
댓글