메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

설계와 구현을 통한 임베디드 OS의 이해와 응용(2) - 개발환경 구성 및 부트로더 작성(1)

한빛미디어

|

2005-12-07

|

by HANBIT

20,555

저자: 서민우
출처: 임베디드월드

* 설계와 구현을 통한 임베디드 OS의 이해와 응용(1) - Overview

이번 기사에서는 개발 환경을 구성하는 방법을 알아보고, 부트로더의 앞 부분을 작성해 보기로 하자. 먼저 cuteOS를 개발해 나갈 환경은 다음과 같다.

호스트 시스템은 다음과 같다.

• i386 PC
• 와우리눅스 7.3 Paran R2
• cross-2.95.3.tar.bz2(크로스 컴파일러)

다음으로 타겟 시스템은 다음과 같다.

• s3c2440A SOC가 장착된 보드(ARM920T)
• 1M NOR flash ROM (0x00000000에 위치)
• 64M SDRAM(32M x 2) (0x30000000에 위치)

이외에 실행 이미지를 타겟 시스템으로 다운로드하기 위한 JTAG와 디버깅을 위해서 시리얼을 사용한다.

앞으로 작성할 소스 코드는 C와 ARM 어셈블리어를 사용하며, 이 두 가지 언어는 이미 숙지하고 있다고 가정하고 기사를 진행하기로 하겠다. 또한 ARM 프로세서내의 레지스터의 구조와 용도, 인터럽트의 처리 방식 등, ARM 프로세서의 programmer’s model에 대해 잘 이해하고 있다고 가정한다. 또한 리눅스 사용법에 대해서도 어느 정도 익숙하다고 가정한다.

그러면 먼저 i386 PC에 와우리눅스 7.3 Paran R2 OS가 설치되어 있는 환경에서 ARM 용 크로스 툴체인을 설치하는 과정을 살펴보기로 하자. 우리가 사용할 ARM 용 크로스 툴체인 cross-2.95.3.tar.bz2 파일은 다음 사이트에서 구할 수 있다.

http://ftp.arm.linux.org.uk/pub/armlinux/toolchain/

참고로 리눅스를 기반으로 한 ARM과 관련된 개발을 할 경우 다음 사이트를 추천한다.

http://www.arm.linux.org.uk/

cross-2.95.3.tar.bz2 파일을 설치하기 위해 리눅스 상에서 먼저 다음과 같은 작업을 한다. 파일의 설치는 반드시 root 사용자 권한으로 해야 한다.

# cd /usr/local/
# mkdir arm
# cd arm/

즉 /usr/local/ 디렉토리로 들어가 arm 디렉토리를 생성한 후, arm/ 디렉토리로 들어간다. 그리고 cross-2.95.3.tar.bz2 파일을 위의 사이트에서 다운로드 받아 /usr/local/arm/ 디렉토리로 가져온다. 그리고 /usr/local/arm/ 디렉토리에서 tar 명령어를 이용하여 다음과 같이 파일을 설치한다.

# tar xvjf cross-2.95.3.tar.bz2

이와 같이 하면 /usr/local/arm/2.95.3/ 디렉토리에 크로스 툴체인이 설치된다. 설치한 툴체인을 사용하기 위해 홈 디렉토리 밑에 있는 .bash_profile 파일의 마지막 부분에 다음과 같이 두 문장을 추가해 준다.

PATH=/usr/local/arm/2.95.3/bin:$PATH
export PATH

참고로 cuteOS의 개발은 일반 사용자의 권한으로도 할 수 있으니, 일반 사용자 계정을 추가한 후 일반 사용자의 홈 디렉토리 밑에 있는 .bash_profile 파일에 위와 같은 내용을 추가하고 개발을 진행해도 되겠다. 참고로 필자는 일반 사용자의 계정으로 cuteOS를 개발해 나갈 것이다.

이렇게 .bash_profile 파일을 수정해 준 후 수정한 내용을 적용하기 위해서는 다시 로그인을 하거나 또는 다음과 같은 리눅스 명령어를 이용하여 .bash_profile 파일의 내용을 현재 쉘에 적용 시켜야 한다.

# source ~/.bash_profile

앞으로 우리가 사용할 명령어들은 /usr/local/arm/2.95.3/bin/ 디렉토리 밑에 존재하며 다음과 같은 명령어들을 주로 사용한다.

arm-linux-gcc
arm-linux-ld
arm-linux-objcopy
arm-linux-nm
arm-linux-objdump

이러한 툴들에 대한 설명은 실제 개발을 진행해 가면서 적절한 부분에서 설명하기로 하겠다.

이상에서 ARM용 크로스 툴체인을 설치하는 과정을 살펴보았다.

다음은 부트로더의 앞 부분을 어떻게 작성하는지, 최종적으로 실행 이미지를 어떻게 만드는지 보기로 하자. 먼저 지금 우리가 작성할 부트로더는 물리적으로 0x00000000 번지에 위치하며, 다음에 작성할 cuteOS는 물리적으로 0x30000000 번지에 위치하게 된다.

이번 글에서 작성할 예제는 세 파일로 구성된다. 즉, 부트로더의 최초 시작 지점인 start.S, 부팅시 디버깅용으로 사용할 led를 제어하기 위한 led.S, 그리고 Makefile로 구성된다.

먼저 start.S의 내용은 다음과 같다.

start.S
#define pWTCON  0x53000000
#define INTMSK  0x4a000008
#define INTSUBMSK   0x4a00001c

.globl _start
_start:
    b   reset

    .balignl 16,0xdeadbeef

reset:
    mrs r0,cpsr
    bic r0,r0,#0x1f
    orr r0,r0,#0xd3
    msr cpsr,r0
    
    ldr r0,=pWTCON
    mov r1,#0x0
    str r1,[r0]
    
    mov r1,#0xffffffff
    ldr r0,=INTMSK
    str r1,[r0]

    ldr r1,=0x7fff
    ldr r0,=INTSUBMSK
    str r1,[r0]

    bl  led_init

    bl  led_off

    mov r0,#0x00000010
    bl  led_on

1:  b   1b  

start.S 파일에서 하는 일은 다음과 같다.

1. CPU를 SVC32 mode로 전환한다.
2. watchdog timer를 끈다.
3. 인터럽트 컨트롤러의 모든 인터럽트를 소스를 mask 처리한다.

그럼 구체적으로 start.S 파일의 내용을 들여다보자.

.globl _start
_start:
    b   reset

이 부분은 파일의 시작 지점으로 물리적으로 0x00000000 번지에 놓이게 된다. 0x00000000 번지에는 1MB 크기의 flash 롬이 놓여 있다. 즉, _start 지점이 0x00000000 번지에 놓이게 되며, mmlt;b resetmmgt; 명령어는 0x00000000 번지에 놓이는 최초의 명령어가 된다. 이 명령어에 의해 CPU는 reset 위치로 뛴다. mmlt;.globl _startmmgt;에서 .globl은 전체 목적 파일을 링크할 때 _start 심벌을 링커 ld가 볼 수 있도록 하는 역할을 한다. _start 지점이 0x00000000 번지에 놓이는지는 뒤에서 arm-linux-nm 이라는 명령어를 이용하여 확인해 보기로 한다.

.balignl 16,0xdeadbeef

이 부분은 다음에 올 코드를 16 바이트 경계에 놓고 중간에 비게 되는 부분을 4 바이트 크기의 0xdeadbeef 값으로 채우라는 어셈블러 지시어이다. 즉, reset의 위치는 0x00000010 번지가 되며, mmlt;b resetmmgt; 명령어와 reset 번지의 사이는 0xdeadbeef로 채워진다. 이 부분 역시 뒤에서 arm-linux-objdump 라는 명령어를 이용하여 확인하기로 한다.

reset:
    mrs r0,cpsr
    bic r0,r0,#0x1f
    orr r0,r0,#0xd3
    msr cpsr,r0

여기는 cpsr 레지스터의 값을 r0 레지스터로 읽어온 후, r0 레지스터의 하위 다섯 비트를 이진수 00000으로 채우고, 다시 하위 5 비트와 5,6,7 번 비트를 이진수 11010011 값으로 채운 후, 수정한 r0 레지스터의 값을 cpsr 레지스터로 쓰는 부분이다. 이 부분은 CPU를 SVC32 mode로 전환을 하며, IRQ와 FIQ를 끄는 역할을 한다. 참고로 CPSR의 포맷은 다음 그림과 같다.



[그림 1] CPSR 포맷


ldr r0,=pWTCON
mov r1,#0x0
str r1,[r0]

이 부분은 watchdog timer를 끄는 역할을 한다. watchdog timer의 위치는 다음 문장에서 나타내는 것처럼 0x53000000 번지에 있다.

#define pWTCON 0x53000000

watchdog timer의 주된 역할은, 예를 들어 OS가 deadlock 등에 의해 정상적으로 동작하지 않을 경우 CPU에 reset 신호를 보내주어, 시스템을 재 부팅하는 역할을 한다. 좀 더 자세히 살펴보면, 여기서는 WTCON 레지스터의 0 번 비트와 2 번 비트를 0으로 만들어 줌으로써 watchdog timer의 reset 신호를 발생시키는 기능과 인터럽트 발생시키는 기능을 비활성화 시킨다. watchdog timer의 블록 다이어그램은 다음과 같다.



[그림 2] watchdog timer 블록 다이어그램

[그림 2]에서 WTCON[0]과 WTCON[2]에 해당하는 부분을 0으로 만듦으로써 앞에서 설명한 것과 같은 동작을 수행한다.

mov r1,#0xffffffff
ldr r0,=INTMSK
str r1,[r0]

ldr r1,=0x7fff
ldr r0,=INTSUBMSK
str r1,[r0]

이 부분은 인터럽트 컨트롤러에 연결되어 있는 모든 인터럽트 소스를 mask 처리하는 부분이다. S3C2440A SOC에는 60 개의 인터럽트 소스가 존재한다. S3C2440A SOC의 인터럽트 컨트롤러 블록 다이어그램은 다음과 같다.



[그림 3] s3c2440A 인터럽트 컨트롤러 블록 다이어그램

[그림 3]에서 인터럽트 컨트롤러의 SUBMASK 레지스터와 MASK 레지스터를 위의 소스와 같이 처리함으로써 SOC 내부 모듈과 외부에서 오는 모든 인터럽트 소스를 mask 처리한다.

bl led_init

bl led_off

mov r0,#0x00000010
bl led_on

이 예제를 수행하는 타겟 보드에는 네 개의 led가 있으며, 부팅시에 디버깅용으로 사용한다. 여기서는 각각 led를 초기화하고 led를 끈 후에 첫번째 led를 켠다. mmlt;mov r0,#0x00000010mmgt; 명령어는 켜고자 하는 해당 led의 번호를 r0 레지스터로 넘겨주는 부분이다. led에 대한 구체적인 설명은 led.S 파일에서 다루겠다.

1: b 1b

이 부분을 C로 표현하면 논리적으로 다음과 같다.

while(1);

위의 명령어의 1b에서 b는 backward의 약자로 명령어 앞에 있는 숫자 1의 위치를 의미한다. 즉, 무한히 1의 위치로 뛰는 동작을 수행한다.

이상에서 start.S 파일의 내용을 알아보았다.
TAG :
댓글 입력
자료실

최근 본 책0