2014년 2월 11일 화요일

ARM용 GNUC 툴체인 삽질

NanoQplus는 실제 산업현장부터 많이 적용되다 보니, ARM 프로세서용 툴체인으로는 IAR EWARM과 Keil MDK-ARM을 먼저 지원하게 되었다. 그 둘은 한 카피 당 가격이 500~600만원을 호가하는 관계로 개인용으로는 적합하지 않다. 그 동안 과제 준비와 업체 지원으로 정신이 하나도 없었는데, 약간 짬이 되는 요새를 틈타 그 동안 미뤄왔던 bare metal용 ARM GCC를 적용해보았다.

툴체인은 GNU Tools for ARM Embedded Processors 4.8. LaunchPad에 둥지를 틀고 있는 프로젝트인데, 가장 최근까지 업데이트를 계속 이어가는 것으로 보인다. 게다가 다운로드 섹션을 보면, Linux 뿐만 아니라, Windows, Mac OS X 버전까지 지원하고 있는 것으로 봐서 믿음까지 간다.

타겟 보드는 STM32 기반 Mango EToI. 조만간 공개될 NanoQplus 공개버전에서 레퍼런스 보드로 활약할 녀석이다. IAR EWARM으로는 잘 동작하는 것을 이미 확인하였으므로, 보드에는 거의 문제가 없다고 봐도 된다.

일단, Makefile은 GCC 패키지 내 샘플을 참조하였다. Linker script는 STM32F10x standard periph library에 있는 예제와 템플릿을 참조하였다.

이제부터 문제 시작!

1. OS interface

시작부터 컴파일 에러를 뱉지만, 당황하지 않고 newlib manual에 나온대로 minimal implementation으로, 끝!

2. main() 진입 전 멈춤

뭐, 쉽게 될꺼라 생각하진 않았지만, main() 조차 들어가지 않으니 당혹스러웠다. Linker script에서는 시작 지점을 Reset_Handler로 지정한 상태. Reset_Handler에서는 data 영역 및 bss 영역 초기화 후, 클럭 설정을 위한 SystemInit()main()을 차례로 호출하게 되는 것... 으로만 생각했으나, STM32 라이브러리에 있는 startup 어셈 코드를 잘 보니 그 사이에 __libc_init_array() 를 한번 다녀온다. 구글링을 해보니 C++ constructors를 위한 초기화 코드를 부르는 영역이란다. 헌데, NanoQplus는 C++을 지원하지 않으니 이 부분을 주석 처리했다. 안되는 것도 이상하긴 하지만, 일단 main() 이라도 들어가보자는 정신으로 과감히 제거! LED를 깜빡거리는 데까지, 끝!

3. printf() 호출 시 멈춤

역시 한번에 넘어가주면 섭섭하다. 다시 구글링 결과, printf() 호출 전에는 setvbuf()stdin, stdout, stderr용 IO 버퍼를 설정해야 한단다. 버퍼를 사용하지 않으므로, 세번째 인자를 _IONBF로 지정하여, 끝!

4. setvbuf() 호출 시 멈춤

이때부터 제대로 멘붕. 찾다 찾다 비슷한 사례로 data 영역이 초기화 되지 않을 경우, malloc()에서 멈추는 현상이 있다고 해서 의심해보기도 하고, QEMU에서 에뮬레이팅 하려다 실패하기도 하고, OpenOCD로 디버깅 연결을 시도하기도 하고, map 파일과 list 파일 들여다보기를 수차례, 거의 포기 직전까지 갔다. 그러다 map 파일을 보고 '아!' 하는 순간이 왔다. 바로 링킹된 libc.a가 이상한 것을 발견했다. 다시 한번 컴파일러 옵션과 링킹 옵션을 확인해보니 컴파일러에는 -mcpu=cortex-m3 -mthumb가 되어 있는데, 링커에는 -mthumb가 빠져있었다. 그래서 Thumb이 아닌 ARM mode 버전의 libc.a가 연결된 것. 결국 이것도 해결하고, 2번 문제도 원상복구했다. 이것 찾으려고 하루가 꼬박 걸렸네. 암튼, 끝!

5. 멀티쓰레드 컨텍스트 스위칭 중 멈춤

NanoQplus는 멀티쓰레드를 지원하기 때문에 새 MCU를 포팅하거나 새 툴체인을 적용할 때 반드시 통과해야 하는 관문이 멀티쓰레드이다. 특히, 컨텍스트 스위칭 부분은 어셈블리 코드 등으로 구성되며 프로세서나 툴체인에 의존적인 부분이 많아 늘 한번에 통과되지 않는 부분이다. 이번에도 역시 쓰레드간 컨텍스트 스위칭 중 HardFault가 발생하며 멈췄다. 컨텍스트 스위칭 부분의 함수는 어셈블리로 구성된 함수로 보통 함수의 진입과 종료시 붙는 스택관련 시퀀스가 붙으면 안된다. 따라서, __attribute__ ((naked))를 함수선언부에 붙여줘야 한다. 이걸로 끝!

우여곡절 끝에 적용이 끝났다. 물론 다른 것들도 테스트해봐야 하지만, 대부분 STM32 StdPeriph Library에 의존적인 부분이라 잘 통과될 것 같다. ARM GCC가 standard library로 newlib을 이용하기 때문에 혹시나 용량이 너무 커지지 않을까 걱정했는데, 최근에 추가된 newlib-nano (--specs=nano.specs)를 사용하게 하니 용량도 걱정 없을 듯 싶다. 당분간은 ARM GCC만 쓰게 될 듯...

아, 그리고 공개버전용 소스코드도 조만간 공개 예정!