본문 바로가기

시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리/1장: Arm 프로세서 소개

[ARM프로세서] 소프트웨어 개발자가 ARM 프로세서를 배워야 하는 이유

2005년도에 제가 처음 개발을 시작한 프로젝트는 ARM7 프로세서 기반의 시스템이었습니다. 프로젝트에 투입된 소프트웨어 개발자들은 모두 ARM 프로세서 교육을 들었습니다. ARM 프로세서를 왜 배워야 하는지 의문을 품었던 개발자는 거의 없었던 것 같은데요. 2021년인 지금 가끔 ARM 프로세서 학습의 필요성에 대해 의문을 품는 분들이 종종 있는 것 같습니다. 
 
이번 포스트에서 임베디드 개발자가 ARM 프로세서를 왜 배워야 하는지 살펴보겠습니다. 
 
브링업을 제대로 하기 위해서
 
임베디드 개발자들이 진행하는 프로젝트의 단계는 '보드 브링업 - 기능 안정화 - 유지 보수'로 분류될 수 있습니다. 3단계 중 보드 브링업 단계에서 구체적으로 어떤 일을 할까요? 과제에 따라 다르지만 개발 업무의 내용을 요약하면 다음과 같습니다.
 
    ● 부트로더에서 스타트 업 코드를 작성
    ● 메모리와 같은 하드웨어를 초기화
  
먼저 스타트업 코드가 무엇인지 알아볼까요?
전원이 시스템에 들어오면 가장 먼저 실행되는 주소에 코드를 위치시켜서 시스템을 초기화하는 코드를 “스타트 업 코드”라고 부릅니다. 스타트 업 코드는 기본적인 메모리를 설정을 초기화하고 ARM 모드 별로 스택 사이즈를 지정해야 합니다. 
 
그런데 스타트 업 코드를 제대로 작성하려면 ARM 어셈블리 명령어를 알아야 됩니다. ARM 동작 모드 별로 스택 주소를 설정하고, ARM 프로세서를 초기화하는 코드는 C 언어가 아니라 어셈블리를 이용해 설정할 수 밖에 없기 때문입니다. 또한 MMU(Memory Management Unit)과 같은 ARM의 구성 요소도 어셈블리 명령어로만 설정할 수 있습니다. 
 
ARM 어셈블리 명령어뿐만 아니라 ARM의 동작 모드와 MMU의 동작 원리도 알아야 합니다. ARM 명령어는 기본으로 알아야 하고 ARM 아키텍처를 제대로 이해하고 있어야 합니다.
디바이스 드라이버 개발을 잘 하기 위해서
 
리눅스 디바이스 드라이버 개발자들은 ARM 프로세서를 잘 알면 다양한 방식으로 디바이스를 제어할 수 있습니다. 특히 많은 하드웨어 디바이스가 인터럽트를 통해 드라이버와 커뮤니케이션을 합니다. 그런데 GIC(Generic Interrupt Controller)와 같이 ARM 프로세서가 인터럽트를 처리하는 하드웨어의 처리 방식을 잘 알면 인터럽트의 우선순위와 세부 속성을 세밀하게 제어할 수 있습니다.
 
또한 멀티미디어와 관련된 드라이버를 작성할 때는 성능이 중요합니다. CPU 코어의 성능을 최대한 올리는 코드를 작성할 필요가 있습니다. ARM 프로세서의 내부에서 구현된 코드가 어떤 방식으로 처리되는 지 알면 성능을 더 올리는 코드를 작성할 수 있습니다. 
 
RTOS나 리눅스 커널에 대한 깊이있는 이해를 위해서
 
리눅스 커널이나 RTOS를 깊이 있게 배우려면 ARM 프로세서의 동작 원리를 제대로 알아야 합니다. 커널을 구성하는 세부 시스템의 핵심 루틴은 ARM 어셈블리 명령어로 구현되어 있기 때문입니다. 더 구체적으로는 리눅스 운영체제나 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 코어의 레지스터 세트가 출력되는데 각 레지스터의 역할과 동작 원리를 파악하고 있어야, 커널 크래시가 발생한 원인을 분석할 수 있습니다. 
 
Reference: ARM 프로세서의 주요 기능
 
ARM 프로세서는 왜 배워야 할까
ARM 프로세서 학습하는 방법의 문제점
ARM 프로세서 소개  
ARM 아키텍처를 구성하는 주요 기능
   ● 어셈블리 명령어란  
   ● ARM의 동작 모드와 익셉션 레벨   
 
Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자