본문 바로가기

무언가 만들기 위한 지식/ARM Processor

Boot Loader(Boot Code+CRT Start up Code)


Embedded Target Board를 처음 접하면서 가장 궁금했던 것은 코드가 어떻게 실행되고, 또한 main()이 존재하느냐 였다.
즉 시작코드가 어디서인지가 가장 궁금하였다.
그것에 대해 알기전에 기본적으로 정의해야 할 것이 있다.
 
Embedded Target Board에서 부팅되는 기본적인 절차는 다음과 같다.
1. Boot Code를 통한 하드웨어 셋팅
2. CRT(C-RunTime) Startup Code 실행(C언어 실행환경 구축)
3. main()으로 branch하여 application 수행

여기서 Boot CodeStartUp Code를 합쳐서 Boot Loader라고 부른다.(사실 이 개념이 약간 모호하다.)
컴파일러가 CRT StartUp을 작성해 주는 방식도 있지만 보통, 개발자가 직접 작성하고는 한다.
보통 Boot CodeStartUp Code는 따로 작성하지 않고 crt0.s라고 하나의 파일에 저장한다고 한다.
(필자가 갖고 있는 부트로더도 crt0.s로 작성되어 있다. C-Runtime StartUp코드, 즉 C 실행환경을 구축하기 때문에 어셈블리어 파일로 이루어진다.)

crt0.s상의 StartUp Code에서 해야할 일은 다음과 같다.
1. 인터럽트 Disable 설정
    - 스타트업 코드 실행중 인터럽트가 발생하면 서비스 루틴으로 가기 때문에 준비가 완료되지 않은 상태에서 분기하면 문제가 생길 수 있다. 또한 와치독 타이머도 disable해야한다.(계속 재시작될 수 있음.)

2. RW_DATA ROM에서 RAM으로 복사
    - 프로그램의 실행은 크게 RAM에서 실행하는 방법ROM에서 실행하는 방법이 있다. ROM에서 실행하는 방법을 XIP(eXecute In Place)라고 하고 RAM에서 실행하는 방법을 Ram Loading이라고 지칭한다.

   - RAM-Loading의 경우 ROM에 기록되어있는 이미지를 RAM에 모두 복사한 후 실행한다.


그림으로 확인해 보면 위와 같다.
RAM으로 각종 변수 및 코드가 복사되면 Read속도가 ROM에서보다 빨라지는 장점이 있고, NAND FLASH를 이용할 경우 OR FLASH처럼 XIP가 이뤄지지 않기 때문에 이 RAM-Loding을 이용하게 된다.
오른쪽 변수들은 메모리 영역의 시작과 끝을 나타내는 심볼들이다.

RAM-Loading의 경우 메모리의 모든 주소가 이어져 있기 때문에 컴파일할때 처음 주소인 RO_BASE의 주소만 알려주면 된다.
실제 코드상에서는 RO_LIMIT, RW_BASE, ZI_BASE, ZI_LIMIT값을 받아올 수 있다. 그리고 RW_BASE와 RO_LIMIT값이 같은지 다른지를 확인해 보아, RAM-Loading으로 구동되었는지 ROM execute로 구동되었는지 알 수 있다. (같으면 RAM-Loading이다.)

   - XIP(eXecute In Place)는 ROM상에서 그대로 프로그램이 수행되는 개념이다. 하지만 이때는 사용될 변수 영역(RW-DATA)과 BSS(Block Started by Symbol) 영역을 RAM으로 복사한후 사용되어야 한다. 변수이기 때문에 값을 저장할 공간이 할당되어야 하기 때문이다.


정확히 DATA 영역의 RW-DATA와 BSS영역에 기록되어 있던 공간이 0으로 초기화되어 ZI영역에 할당된다.
(DATA 및 BSS, TEXT 영역에 대한 Memory Map은 필자의 SPARC ASSEMBLER에 자세히 언급되었습니다^_^)
이 방법을 사용시 컴파일시에 RO_BASERW_BASE의 정보를 알려주어야 한다. 그리고 코드상에서는 RO_LIMIT, ZI_BASE, ZI_LIMIT정보를 가져온후 ZI영역 초기화를 수행하게 된다.
XIP구조일 경우 부트로더상에서 DATA영역의 RW_DATA를 RAM으로 복사하는 코드가 존재하여야 한다.
(ZI 영역 할당은 둘다 공통으로 한다.) crt0.s 부트로더상에 이러한 코드가 존재한다.

※ 요약 : RAM-Loading의 경우 Data영역복사를 따로할 필요가 없다.(이미 컴파일시 RAM영역에 올라가도록 지정하므로)
             XIP구조의 경우 RW-Data영역을 RAM주소로 복사해 주어야 한다!
※ 컴파일 당시 옵션으로, 각 영역이 잡힐 메모리 구조상의 주소를 설정해주게 된다.

3. ZI영역 Clear
    - BSS영역에는 초기화하지 않은 변수들에 대한 정보가 저장되어 있다.(즉 실제 할당되지는 않은 것이다.) 그러한 정보들에 의해 부트로더의 준비과정에서 실제로 초기화를 수행하며 할당된다. ZIZero Initialize의 약자이다. 이 코드는 바로 위에서 설명한 DATA영역 복사하는 코드와 같은 위치에 존재하며, RAM-Loading이나 XIP 방식에 상관없이 무조건 수행된다.

4. Mode별 Stack 생성
    - ARM Processor에는 총 7개의 모드가 존재한다. 특이하게도 ARM상에서 Stack은 총 6개가 존재한다. 이는 모드별로 Stack이 각각 존재한다는 것을 의미한다. ARM의 모드에 대해서는 따로 포스팅 예정이다. (모드가 7개인데 Stack이 6개인 이유는 User와 System의 경우 같은 Stack을 사용하기 때문이다.) 위 그림에서 Heap과 Stack을 RAM공간에 따로 처리하지 않은 이유는 이 부분에서 Stack을 따로 할당해주기 때문이다. (즉 stack은 컴파일이 아니라 실행시 c환경준비 코드에서 설정한다는 것.)

5. 힙생성
    - Heap영역을 사용하기 위해서 따로 공간을 할당해 주어야 한다. 할당을 하지 않았다면 역시 malloc등 함수를 사용할 수 없다.

6. 인터럽트 Enable 설정
    - 기본적인 설정이 끝났으면 main으로 가기전에 disable했던 인터럽트를 다시 활성화 시킨다.

7. main()호출
    - crt0.s에서 Boot Code와 CRT Start Up Code가 모두 마치게 되면 이제 C언어의 main으로 가게 된다. 이때 무조건 main()으로 가는 것은 아니고 사용자의 설정에 따라 Main(), go()등 자유롭게 바꿀 수 있다. (결국 BL로 분기하기 때문이다.)
컴파일러에 의해 StartUp Code가 작성될 경우 주로 main으로 되지만 프로그래머가 부트로더를 직접 작성할 경우 사용자의 마음에 달렸다.


위 7가지는 crt0.s 내의 Start Up Code의 내용이고, 그외 Boot Code의 내용으로

1. 엔트리 포인트지정
   - 가장 먼저 시작되는 포인트를 정해준다.

2. 예외처리벡터 설정
    - ROM의 시작주소(0x0)에는 항상 예외처리 벡터가 위치한다. 각 예외에 할당한 메모리가 4 byte이므로 명령어하나로 예외를 처리해야 한다. 그렇기 때문에 그 내용은 주로 Branch문으로 되어 있다. 후에 자세한 포스팅 예정.

3. 시스템 클록설정

4. 메모리 초기화

를 하게 된다.

필자의 경우 Exception Vector Table을 이용하여  인터럽트 처리루틴을 수정을 하여 사용했었다.
앞으로도 그 부분에 대해 주로 다룰 것이다. (아는게 그거니 -.,-;;)

필자가 가장 많이 손보고 짱구를 굴린 파일이 바로 crt0.s 부트로더 였지만, (개념 잡느라 시간이 꽤나 걸림)
모든 부분(하드웨어적 셋팅부분)까지 손본것은 아니고 주로 예외처리 루틴과 스택을 이용하는 부분이었다.
근디 다시 소스들을 보고 관련책을 읽어보니 하드웨어 셋팅과 startup Code가 조금 흥미로워지고는 있다.

결론은 하드웨어적 부트코드는 아직 젬병이고 개념만 알지 손대기 어렵다는 거 ㅠ.ㅜ;
그리고 코드를 올려서 공유하고 싶은데, 회사코드라 필자가 직접 수정한 것 이외에는 올리지 않을 것이라는 것^_^

'무언가 만들기 위한 지식 > ARM Processor' 카테고리의 다른 글

ARM Processor의 7가지 Exception  (2) 2010.03.19
ARM Processor의 7개 동작 Mode  (0) 2010.02.24
JTAG && UART  (1) 2010.02.20
Embedded(내장형) System  (2) 2010.02.20
SOC(System On Chip)  (0) 2010.02.20