이번 시간에는 ARM 프로세서를 리눅스 시스템 개발자가 배워야 하는 지 설명합니다.
 
브링업을 제대로 하기 위해서
 
임베디드 개발자들이 진행하는 프로젝트의 단계는 '보드 브링업 - 기능 안정화 - 유지 보수'로 분류할 수 있습니다.
3단계에서 보드 브링업은 어떤 단계를 거쳐 진행될까요? 과제에 따라 다르지만 개발 업무의 내용을 소개하면 다음과 같습니다.
 
   * 부트로더에서 스타트업 코드를 작성
   * 메모리와 같은 하드웨어를 초기화
  
먼저 스타트업 코드를 작성하는 업무에 대해서 조금 더 설명을 드려볼까요?
전원이 시스템에 들어오면 가장 먼저 실행되는 코드가 reset 익셉션 벡터 코드입니다. 
 
이 코드에서 기본적인 메모리를 설정을 초기화하고 ARM 모드 별로 스택 사이즈를 지정해야 합니다.
이 모든 코드를 제대로 작성하려면 ARM 어셈블리 명령어를 알아야 됩니다.
 
또한 DDR 와 같은 메모리를 초기화하기 위해서는 관련 ARM 어셈블리 명령어는 기본으로 알아야 겠죠.
 
와 같은 업무를 수행하려면 ARM 명령어는 기본으로 알아야 하고 ARM 아키텍처를 제대로 이해하고 있어야 합니다.
 
디바이스 드라이버 개발을 잘 하기 위해서
 
리눅스 디바이스 드라이버 개발자들은 ARM 프로세서를 잘 알면 더 안정적인 코드를 작성할 수 있습니다.
특히 많은 하드웨어 디바이스가 인터럽트를 통해 드라이버와 커뮤니케이션을 합니다. 그런데 ARM 프로세서가 인터럽트를 어떤 방식으로 처리하는지 미리 파악한 
개발자는 조금 더 안정적인 드라이버 코드를 작성할 수 있습니다.
 
무엇보다 멀티미디어 드라이버를 작성할 때는 CPU 코어의 성능을 최대한 올려는 코드를 작성할 필요가 있습니다.
ARM 프로세서가 어떻게 동작하는지 머리 속으로 그리면서 코드를 작성하면 더 성능을 올리는 코드를 작성할 수 있습니다.
 
RTOS나 리눅스 커널에 대한 깊이있는 이해를 위해서
 
리눅스 커널이나 RTOS를 깊이 있게 배우려면 ARM 프로세서의 동작 원리를 제대로 알아야 합니다.
그 이유는 커널을 구성하는 세부 시스템의 핵심 루틴은 ARM 어셈블리 명령어로 구현되어 있기 때문입니다.
 
구체적으로 리눅스 커널의 다음 서브 시스템은 어셈블리 명령어로 구현돼 있습니다.
 
   * 인터럽트 벡터
   * 컨텍스트 스위칭 루틴
   * Preemption 처리 루틴
   * 시그널을 받아 처리하는 루틴
   * 시스템 콜 핸들러를 처리하는 루틴
 
많은 분들이 리눅스 커널의 고수들은 리눅스 커널만 잘 알고 있다고 생각할 수 있는데요.
실제 제가 만나 본 많은 리눅스 커널의 고수들은 ARM 아키텍처에 대해서 정말 깊이 있게 알고 있습니다. 
 
디버깅을 잘 해 문제를 잘 해결하기 위해서
 
난이도나 복잡도가 높은 드라이버를 맡으면 커널 패닉이나 심하면 와치독 리셋과 같은 문제를 만날 가능성이 높습니다.
이런 크래시 문제를 디버깅하기 위해서는 ARM 프로세서를 제대로 알고 있어야 합니다.
 
커널 패닉이 발생하면 커널 로그는 커널 패닉이 발생한 코드의 위치를 ARM 레지스터 세트와 함꼐 출력합니다.
다음은 커널 패닉이 발생할 때의 커널 로그입니다.
 
[  262.401303] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[  262.401365] pgd = dbdc4000
[  262.401389] [00000000] *pgd=00000000
[  262.401433] Internal error: Oops: 80000005 [#1] PREEMPT SMP ARM
[  262.401459] Modules linked in:
[  262.401495] CPU: 0 PID: 7107 Comm: Framework Tainted: G        W    3.10.49-g356bd9f-00007-gadca646 #1
[  262.401522] task: da6b0540 ti: d9412000 task.ti: d9412000
[  262.401549] PC is at 0x0
[  262.401590] LR is at xfrm_local_error+0x4c/0x58
[  262.401619] pc : [<00000000>]    lr : [<c0adc274>]    psr: a00f0013
[  262.401619] sp : d9413c68  ip : c0ac6c20  fp : 0000dd86
[  262.401654] r10: 0000010e  r9 : 0000010a  r8 : de0ddc20
[  262.401678] r7 : c13ddf00  r6 : 00000500  r5 : d9094540  r4 : c13e3780
[  262.401703] r3 : 00000000  r2 : 00000001  r1 : 00000500  r0 : d9094540
 
보시다시피 ARM 코어의 레지스터 세트가 출력되는데 각 레지스터의 역할과 동작 원리를 파악하고 있어야,
디버깅을 시작할 수 있습니다.
 
또한 스택이 오염되거나 메모리 비트 플립이 발생하는 하드웨어 버그도 만날 수 있습니다.
 
  
crash> bt e5752c00
PID: 1787   TASK: e5752c00  CPU: 4   COMMAND: "net_socket"
bt: WARNING:  stack address:0xe853fa38, program counter:0xc0ee5b60
 #0 [<c0ed8b64>] (panic) from [<c0125038>]
 #1 [<c0125038>] (__stack_chk_fail) from [<c032b6cc>]
 #2 [<c032b6cc>] (sock_has_perm) from [<c0327d00>]
 #3 [<c0327d00>] (security_socket_recvmsg) from [<c0ceb1c8>]
 #4 [<c0ceb1c8>] (sock_recvmsg) from [<c0cec474>]
 #5 [<c0cec474>] (___sys_recvmsg) from [<c0ced5b4>]
 #6 [<c0ced5b4>] (__sys_recvmsg) from [<c0106820>]
 
이런 현상을 제대로 분석해서 해결책을 찾으려면 먼저 ARM 프로세서에 대해서 제대로 알고 있어야 합니다.
 
많은 고수 프로그래머들은 컴퓨터의 동작 원리가 중요하다고 강조합니다.
 
분야를 막론하고 많은 개발자들이 각 분야의 고수 프로그래머들에게 '어떻게 하면 고수가 될 수 있는지' 질문을 합니다.
이해를 돕기 위해 '코딩의 신 아샬'님이 올린 아래 동영상을 소개합니다.
 
 
54 초 정도에 이 분은 컴퓨터를 올바르게 이해하는게 중요하다고 강조합니다.
 
아, 임베디드 개발자 입장에서 여기서 말하는 컴퓨터는 무엇을 의미할까요?
전 ARM 프로세서일 수 밖에 없다고 생각합니다.
 
정리
 
가끔 '주위 선배들이나 친구들이 임베디드를 개발하는데 ARM 프로세서를 뭐 그리 잘 배울 필요가 없다'라고 하시나요?
모두 거짓말입니다. +11여간의 제 개발 경험과 외부 커뮤니티 활동을 비추어 봤을 때, 고수 임베디드 BSP 개발자 중에 ARM 프로세서를 잘 모르는 분은 한 번도 만나 본 적이 없습니다.
 

< '시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 저자>

 
 
 
이제부터 ARM 프로세서에 대한 이야기를 조금 더 해보려고 합니다.
제 개발 인생을 돌이켜 보니 대부분의 시간을 사실 ARM 프로세서와 함께 보냈더군요. 
다음과 같은 조합으로 말이죠.
 
   * 'ARM + RTOS, ARM + 리눅스'
 
ARM 프로세서를 처음 접할 때의 느낌
 
그런데 제가 ARM 프로세서를 처음 접할 때의 느낌을 잊을 수가 없습니다. 그 느낌이 어땠나면;
 
   * 목욕탕에서 가장 뜨거운 온탕에 점프해 들어갔을 때
 
음, 잘 공감이 가시나요? 대중 목욕탕에 안 가신다고요? 그럼 다음의 예는 어떤가요?
 
   * 그럼 충치가 있어서 치과를 가서 진료실에서 기다릴 때
 
아직도 공감이 안된다고요? 난 한번도 치과에 가본 적이 없다고요? 그렇다면 어쩔 수 없죠. 더 이상의 예를 들기는 어렵겠군요.
 
ARM 프로세서를 배우기 꺼려하는 이유
 
이처럼 ARM 프로세서를 처음 접하는 분들은 정말 ARM 프로세서를 배우기 싫거든요. 물론 저도 그랬죠.
여러분, 치아에 이상이 있으면 바로 치과에 가시나요? 아니죠. 조금 아파도 버티다가 나중에 너무 아파서 버틸 수 없을 때 치과에 가죠.
 
   * ARM 프로세서도 마찬가지에요.
 
안 배우려고 하다가 어쩔 수 없이 배울 수 밖에 없는 게 ARM 프로세서인 것 같아요. 어쩔 수 밖에 배워야 한다는 사실은 임베디드로 밥을 먹고 살려면 ARM 프로세서는 반드시 잘 알아야 한다고 이야기할 수도 있겠네요. 
 
그런데 치아가 안 좋은 데 버티다가 치과에 가면 어떤 결과를 맞이하나요? 대부분 치료비가 더 들거나 치아 상태가 더 안 좋아지는 경우가 많죠.
ARM 프로세서도 마찬가지에요. ARM 프로세서를 배우기 싫어서 버티다가 나중에 ARM 프로세서를 어쩔 수 없이 배우면 더 고생하는 경우가 많아요.
그래서 ARM 프로세서를 미리 배워 놓는게 중요하죠.
 
그렇다면 ARM 프로세서를 배우기 어려운 이유가 무엇일까요?  제 생각에는 ARM 프로세서의 내용이 어렵다기 보단 ARM 프로세서를 공부하는 방법에 문제가 있는 경우가 많아요. 그래서 전 언제나 ARM 프로세서의 공부 방법에 대해 고민을 한답니다. 유튜브에 가면 유명한 스타 입시 강사분들이 있죠. 많은 분들이 공부하는 방법이 중요하다고 강조합니다. ARM 프로세서도 마찬가지인 것 같아요.
 
ARM 프로세서의 이론과 내용을 단순히 이해하고 암기하는 방식으로 공부하면 그것은 '망하는 지름길'이랍니다. 그럼 ARM 프로세서를 처음 접하면 만나는 걸림돌인 어셈블리 명령어에 대해 조금 더 설명을 드려 볼게요.
 
ARM 어셈블리 명령어를 배우기 어려운 이유
 
ARM 프로세서를 공부하면 가장 먼저 만나는 주인공은 어셈블리 명령어입니다. 대부분 ARM 어셈블리 명령어를 배우기 싫어합니다. 그 이유는;
 
   * ARM 어셈블리 명령어는 배우기 어렵다.
 
라고 생각하기 때문인데요. 그런데 사실은 ARM 어셈블리 명령어는 C 언어 문법보다 쉬워요. 이건 제가 장담합니다. 
여기서 한 가지 의문이 생깁니다.
 
   * 그렇다면 ARM 어셈블리 명령어가 왜 어렵다고 느꼈을까?
 
음, 사실 ARM 어셈블리 명령어가 어려운 게 아니고요. ARM 어셈블리를 공부하는 방법이 잘못 됐기 때문이에요.
여러분, 수학 공부를 할 때 공식을 맨날 외우고 잊어 먹고 외우고 잊어 먹고 하면 수학 공부가 재미있나요? 물론 아니겠죠. 그 원리를 제대로 이해한 다음에 자연히 공식을 체득하면 더 재미있게 수학을 공부할 수 있어요.
 
그런데 ARM 어셈블리 명령어를 공부하는 패턴을 보면 ARM 어셈블리 명령어를 외우는 방식을 고집하는 것 같아요. 물론 저도 그랬죠.
 
   * 그런데 ARM 어셈블리 명령어를 외우는 방식으로 공부하는 것은 망하는 지름길이에요.
 
많은 분들은 ARM 어셈블리 명령어를 50개 정도를 무리해서 외웁니다. 영어를 공부할 때 단어를 외우 듯이 말이죠.
여러분, 이 방법으로 절대 공부하지 말기 바래요. 아무리 잘 외웠다고 느껴도 그것은 스스로 착각일 뿐이죠.
그 이유는;
 
   * ARM 어셈블리 명령어를 외우면 반드시 잊어 먹어요.
 
어짜피 잊어 먹고 개발에 써 먹지도 못할 껀데 ARM 어셈블리 명령어를 왜 외우나요? 그 시간에 게임을 하거나 유뷰트를 보시길 바래요.
 
수년동안 ARM 프로세서와 함께 보낸 제가 뼈져리게 느낀 점은 다음과 같아요. 
 
   * ARM 프로세서의 내용보다 ARM 프로세서를 공부하는 방법이 훨씬 더 중요하다.
  
이를 조금 다르게 말씀드려 볼까요?
 
   * ARM 어셈블리 명령어의 내용보다 ARM 어셈블리 명령어를 배우는 방법이 훨씬 더 중요하다.
 
이제 ARM 어셈블리 명령어를 잘 배우는 방법을 소개하려고 합니다.
 
ARM 어셈블리 명령어를 배울 때 다음과 같은 원칙을 세울 필요가 있어요.
 
   * ARM 프로세서의 작동 원리와 함께 어셈블리 명령어를 익힌다.
   * ARM 어셈블리 명령어는 반드시 디버깅을 하면서 몸소 체험한다.
   * ARM 어셈블리 명령어는 C 언어와 함께 배운다.
 
이 원칙과 함께 ARM 어셈블리 명령어를 배우면 더 빨리 배울 수 있고 훨씬 더 오랫동안 기억할 수 있어요.
이제부터 세부 원칙을 소개하려고 해요.
 

< '시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 저자>

 
 
이번 시간에는 ARM 프로세스를 왜 배워야하는지 이야기를 해보겠습니다.
 
우리가 어떤 것을 처음 배울 때는 그것을 왜 배워야 하는지 알면 좋습니다. 왜냐면, 무엇을 공부하기 전에 왜 배워야 하는지 알면 더 동기 부여가 되기 때문이죠. 다른 동료들이나 친구들이 ARM 프로세서는 잘 배워야 한다고 해서 등떠밀려 공부하면 잘 집중이 안되는 거 같습니다. 그렇지 않나요? 무엇보다 배운 내용이 어떻게 활용되는지 알면 공부할 때 더 힘이 날 겁니다.
 
소형 기기에서 ARM 프로세서를 많이 탑재
 
ARM 프로세스를 배우는 가장 큰 이유는 ARM 프로세서를 많이 쓰기 때문이에요. 대부분 휴대 기기에는 ARM 프로세서가 많이 탑재돼 있어요. 여러분들, 모두 휴대폰 1개는 다 들고 다니죠? 안드로이드 스마트폰에 탑재된 CPU가 무엇일까요? 그것은 ARM 프로세서에요.
 
휴대 전화 뿐만 아니라 사물 인터넷에도 ARM 프로세서를 많이 써요. 이렇게 소형 기기에서는 ARM 프로세서를 많이 사용하고 있어요. 여러분들은 ARM 프로세서가 저전력에 소모 전류가 적다는 사실을 알고 있죠? 그래서 소행 기기에 ARM을 많이 쓰는 거에요.
 
근데 ARM 프로세서를 소형 기기에 많이 쓴다는 사실은 조금 달리 생각해볼 수 있어요. 많은 소형 기기에서 ARM 프로세서를 사용하고 있다는 이야기는 많은 개발자들이 ARM 프로세서와 관련된 부분에 일을 하고 있따고 할 수 있죠. 많은 ARM 프로세서 개발자 풀이 있다고 볼 수 있는 거에요.
 
프로그램의 세부 동작 원리를 파악
 
이번에는 ARM 프로세서를 배워야 하는 다른 이유에 대해서 알아볼까요? ARM 프로세서의 기본 원리를 알면 프로그램을 CPU가 어떻게 돌리는지 정확히 알 수 있어요. 우리가 작성하는 코드는 C나 C++ 혹은 자바, 파이썬 언어인데요. 사실 이 코드들은 컴파일이 돼서 모두 ARM 어셈블리 코드로 돌게 돼 있어요. ARM 프로세서의 동작 원리를 알면 자연히 프로그램이 정말 어떻게 돌아가는지 알 수 있어요. 그래서 ARM 프로세서의 원리를 제대로 파악하는 개발자들은 조금 더 안정적인 코드를 작성할 수 있어요.
 
물론 ARM 프로세서의 세부 원리를 잘 몰라도 개발하는데 큰 지장은 없어요. 그런데 고급 개발자로 인정 받으려면 반드시 넘어야 하는 산이 ARM 프로세서랍니다. 혹시 '스택 오염, 스택 Overflow, 스택 Underflow' 이런 용어를 들어본 적이 있나요? 이런 문제는 ARM 프로세서의 세부 동작 원리를 모르면 문제를 분석하거나 해결할 수 없어요. 또한 Secure Monitor Call이란 소리를 들어본 적이 있나요? 'Secure Monitor Call'을 실행하고 나니 시스템이 무감이 됐다고 합니다. 음, 듣기만 해도 깝깝하군요. 모두 ARM 프로세서와 관련된 용어랍니다.
 
RTOS나 운영체제 커널의 세부 동작 원리를 파악
 
마지막으로 ARM 프로세서를 알아야 하는 이유는 RTOS나 운영체제의 세부 동작 원리를 배우고 싶을 때에요. 사실 ARM 프로세서는 RTOS을 돌리거나 리눅스와 같은 범용 운영체제와 함께 돌아가거든요. ARM 프로세서 혼자만 돌아가는 경우는 사실 없어요.
 
그런데 RTOS나 리눅스와 같은 운영체제의 핵심 원리를 알려면 ARM 프로세서를 알아야 돼요. 익셉션, 스케줄링과 같은 세부 동작은 대부분 ARM 어셈블리 코드로 돼 있기 때문이죠.
 
그리고 스타트업 코드라고 들어본 적이 있나요? 시스템이 처음 부팅할 때 실행하는 코드에요. RTOS나 리눅스 개발자들은 ARM의 스타트업 코드를 작성하기 마련인데, ARM 프로세서의 기본 원리를 알아야 관련 코드를 작성할 수 있죠.
 
사실, ARM 프로세서만을 잘하기 위해 ARM 프로세서를 배우는 사람은 많지는 않아요. 임베디드 분야의 개발자들이나 고급 시스템 개발자가 되고 싶은 분들이 ARM 프로세서를 배우려고 하죠. 
 
ARM 프로세서를 알고 모를 때 어떤 차이가 있을까?
 
ARM 프로세서를 잘 알고 모르고는 평소에 잘 들어나진 않는데요. 해결하기 어려운 문제를 만났을 때는 달라요.
 
'Secure Monitor Call'을 실행하고 나서 시스템이 무감됐다는 예시를 들었잖아요. 이 문제를 리포트 받고 ARM 프로세서를 잘 아는 분과 모르는 분이 어떻게 생각할까요? ARM 프로세서를 잘 모르는 분은 이게 뭔 소리인지 이해를 못하겠죠. 그래서 조용히 구글링을 하겠죠.
 
그런데 ARM 프로세서를 잘 아는 분은 이렇게 생각할 겁니다.
 
  * 'Secure Monitor Call'을 실행하면 트러스트 존으로 동작 모드를 변경할 텐데. 이 때 실행하는 어셈블리 코드의 아규먼트는 제대로 전달이 된 것일까?
 * Secure Mode와 Non-Secure 모드 별로 페이지 테이블의 시작주소인 TTBR 이 다른데. 혹시 이 설정이 잘못됐나?
 * 트러스트 존에 로그를 추가해서 어디까지 실행되는지 확인해볼까?
'Secure Monitor Call'을 여러 CPU가 실행하려고 시도하지는 않나? 해당 코드에 락을 좀 걸어볼까?
 
이렇게 전체 그림을 그리고 문제를 분석하는 개발자와 'Secure Monitor Call'이란 용어가 뭔 소리인지도 몰라 조용히 구글링을 하는 개발자 중 누가 먼저 빨리 문제를 해결할까요? 여러분이 관리자라면 둘 중 어느 개발자에게 문제를 맡길까요? 구글링을 하는 개발자는 아니겠죠?

< '시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 저자>

 
 
많은 분들이 ARM 프로세스를 익히기 어렵다고 말합니다. 물론 저도 마찬가지였고요.
그렇다면 ARM 프로세스가 어려운 이유가 뭘까요? 
 
ARM 프로세스 자체가 어렵기보다는 ARM 프로세스를 익히는 방법에 문제가 있기 때문입니다.
달리 말하면 ARM 프로세스 내용은 그리 어렵다는 이야기입니다.
 
자, 그럼 ARM 프로세스의 핵심 개념이 무엇인지 살펴볼까요? ARM 프로세스 범위는 넓게 보면 MMU부터 캐시까지 다양하나 핵심 내용은 다음과 같이 좁힐 수 있습니다.
- 어셈블리 명령어
- 익셉션
- ARM 모드(ARMv7/ARMv8)
- 함수 호출 규약(Calling Convention)
 
이 밖에도 MMU, Cache(L1/L2) 등등 깊게 살펴볼 주제가 있습니다. 위에서 언급한 내용만 제대로 파악하면 임베디드 개발자로써 프로그래밍이나 디버깅하는데 문제가 없습니다.
 
제가 ARM 프로세스는 그 내용 자체가 어렵다기 보다는 ARM 프로세스를 익히는 방법이 효율적이기 못하다고 말씀드렸습니다. 그러면 위에서 언급한 4가지 핵심 주제로 토대로 조금 더 설명을 드리겠습니다.
 
어셈블리 명령어
 
'어셈블리 명령어'란 말만 들어도 머리가 멍해지는 분 있나요? 한번 손 들어보세요! 
제가 예전에 ARM 프로세스의 어셈블리 명령어를 공부할 때 정말 낯설었습니다. 아무리 어셈블리 명령어를 읽어도 머리 속에 남지 않고 바로 튕겨 나가는 듯한 느낌이었어요. 그 이유는 무엇일까요?
 
   "어셈블리 명령어를 문법 위주로 익혔기 때문입니다." 
 
어셈블리 명령어를 읽으면 일단 무슨 소리인지 이해는 갑니다. 그런데 어셈블리 명령어를 알면 뭐합니까? 
 
   "실제 프로그래밍을 할 때 써 먹는게 중요하지오!"
 
그럼 어셈블리 명령어 한 개를 예를 들어볼까요?
 
ldr r2 [r3]
 
위 명령어는 r3 레지스터가 담고 있는 값을 r2 레지스터에 저장하라는 의미입니다. 
다음과 같이 r3가 0xD000C000이고 0xD000C000에 0x2가 있다고 가정하겠습니다.
 
주소              값
---------------------
0xD000C000   0x2
0xD000C004   0x4
 
이 조건에서 'ldr r2 [r3]' 명령어를 실행하면 r2는 어떻게 업데이트될까요?
 
   "0x2가 됩니다."
 
자, 우리는 'ldr' 명령어를 알게 됐습니다. '난 이제 새로운 ARM 어셈블리 명령어를 알게 됐어'라고 생각하면 자신을 칭찬할 수 있습니다. 하지만 이처럼 공부하는 것은 정말 비효율적입니다.
 
    "그 이유는 이 방식으로 공부하는 까 먹을 확률이 99.9% 이기 때문입니다."
 
'ldr' 명령어만 배우는 것은 사실 공학적으로 아무런 의미가 없습니다. 그러면 ldr 명령어를 어떻게 배워야 머리 속에 오랫 동안 남을까요?
 
   "C 프로그램으로 작성한 코드가 ldr 명령어로 어떻게 변환되는지 알아야 합니다."
 
다시 말하면 우리가 작성한 C 코드가 ldr 명령어에 어떻게 대응하는지 알아야 한다는 것입니다.
 
다음 시간에는 실제 C 코드가 ARM 어셈블리 코드로 어떻게 변환되는지 알아보겠습니다.
 

< '시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 저자>

 
 

+ Recent posts