임베디드 레시피

Egloos | Log-in





스타트업(Startup.S) 파일이 뭐죠?

뭘 알아야 이해를 하지
호스트 피시에 전원을 넣으면 한참 동안 뭔가를 하다가 윈도우가 나타나죠. 이런 과정을 부팅이라고 말합니다. 이처럼 임베디드 시스템도 부팅을 하는데, 부팅 과정 중에서 가장 첫 번째 단계를 맡고 있는 파일을 일반적으로 스타트업 파일(Startup.s)이라고 해요.
암 시피유를 가지고 제품을 개발할 때 스타트업 파일은 *.c 가 아닌 *.s 파이로 만든답니다. (확장자가 *.c 파일들은 씨 언어로 만든 프로그램 소스이고 *.s는 어셈블리언어로 만든 소스랍니다.) 
 
 


그럼 왜 스타트업 파일은 씨 언어에서 하지 않고 어셈블리언어에서 할까요? 씨 언어에서 하면 안될까요? 왜 그럴까요? 많은 이유가 있겠지만 여기선 2가지로 요약해 볼게요.

첫 번째, 인터럽트 처리 때문입니다. 앞서 말씀 드린 바와 같이 인터럽트는 임베디드 시스템의 핵심이라고 말할 수 있죠. 인터럽트를 처리하기 위해 암 프로세서 내에서는 아이알큐(IRQ)와 에프아이큐(FIQ) 모드가 존재하며 각 모드에서 인터럽트를 어떻게 처리해야 될지를 개발자가 만들어야 한답니다. 인터럽트는 자주 발생되기 때문에 빠르게 처리해야 하고 빠르게 동작 되려면 실행되는 코드 수를 최대한으로 적게 만들어야 좋겠죠. 실행 코드 수를 줄이는 방법이 여러 가지가 있겠지만 우선 씨 언어보다 어셈블리언어가 휠씬 실행 코드 수가 적답니다.
두 번째, 메모리 컨트롤러 레지스터 설정과 스택 어드레스 할당이랍니다. 씨 언어로 프로그램 한 프로그램이 동작하려면 스택이라는 메모리 영역이 필요하답니다. 스택 메모리란 로컬변수나 리턴 어드레스와 같은 내용이 저장이 되는 공간을 말하고, 이러한 내용이 저장되려면 우선 읽기, 쓰기가 가능한 램(RAM)이여야지 가능하답니다. 즉, 노어플래시 메모리, 낸드 플래시 메모리에는 스택 영역으로 사용할 수가 없다는 뜻이기도 하죠.  씨 언어로 프로그램을 해 보셨다면 보통 메인()-main() 이라는 함수에 대해 잘 아실 겁니다. 씨 언어로 1000라인 이상의 프로그램을 만들었다고 해도 가장 먼저 시작하는 곳은 메인 함수랍니다. 하지만 이 함수도 씨 언어로 만들었기 때문에 무조건 스택 영역이 필요합니다. 메인 함수가 동작을 하려면 스택 영역이 어디에선가 할당이 되어야 해요. 그럼 어디서 할당 해 줘야 할까요? 바로 스타트업 파일인 어셈블리언어에서 스택 영역을 할당한답니다. 위에서도 설명 드린 바와 같이 스택 영역을 할당하기 전에 반드시 해야 될 일이 바로 메모리 컨트롤러 레지스터 설정이랍니다. 즉, 스타트업 파일에서 스택 영역을 할당 한다고 해도 시피유가 에스디램에 접근이 안 된다면 아무런 소용이 없어요. 시피유가 에스디램에 접근이 가능하도록 메모리 컨트롤러 레지스터에 데이타 값을 넣어 초기화를 해야 한답니다.
 
그럼 본격적으로 스타트 파일에 대해 자세히 알아보죠.
 스타트 파일에서는 익셉션벡터, 인터럽트 디세이블(Disable), 피엘엘(PLL), 에스디램 초기화, 스택 영역 할당, 변수 초기화, 씨 언어의 메인 함수 진입 등을 어셈블리 언어로 프로그램을 만든답니다.
스타트업 파일 구조는 다음과 같아요.
  
 


 
익셉션벡터란 임베디드 시스템이 리셋이 되거나 인터럽트가 발생했을 때 암 코어는 고정된 어드레스로 점프한 후 개발자가 지정한 어드레스로 다시 점프해서 프로그램이 실행하게 되요. 이때 개발자가 지정한 어드레스를 정의하는 곳이랍니다.
모든 인터럽트 디세이블은 임베디드 시스템이 부팅 초기 때 매우 중요한 순간이기 때문에 그 순간만큼은 인터럽트를 받지 않도록 하기 위해서랍니다. 피엘엘 설정은 시피유와 각종 디바이스에 클럭 설정하기 위해서 하며, 메모리 컨트롤러 설정은 시피유가 에스디램 메모리를 읽기, 쓰기 가능하도록 하기 위해서 해 준답니다. 시피유가 에스디램에 읽기 쓰기가 가능해 지면 씨 프로그램이 동작하기 위한 스택 영역을 지정해 주고요 마지막으로 씨 언어의 메인 함수를 점프 한답니다.
 
예제를 살펴보죠.


코드 Startup.s


.global main @ a-1
.global _start @ b-1

.text
.code 32

_start: @ c-1
@ 익셉션벡터 지정
b ResetHandler @ resethandler
b HandlerUndef @ handlerUndef
b HandlerSWI @ SWI interrupt handler
b HandlerPabort @ handlerPAbort
b HandlerDabort @ handlerDAbort
b . @ handlerReserved
b HandlerIRQ @ handlerIRQ
b HandlerFIQ @ handlerFIQ

ResetHandler:
bl Watchdog_Disable @모든 인터럽트 및 와치독 타이머 디세이블
bl Clock_Management @피엘엘 설정
bl Memory_Initialization @메모리 컨트롤러 설정
bl Stack_Initialization @스택영역 할당
bl InitVariables @변수초기화
bl Branch_Main @씨 언어 메인 함수 콜
B .

....<중간 생략>.....

Branch_Main:
LDR pc, =main




▶코드 main.c


#include
static int extern_static_one;
int main()
{
int i, value=0;
static int inter_static_star;
while(1)
{
value++;
if(value>=19)value=0;

extern_static_one++;
inter_static_star++;
}
return 0;
}



여기서 중요한 사실 한가지를 밝히자면, 타겟의 플래시 메모리가 어떤 것을 사용하느냐에 따라 Startup.s 파일이 있어야 할 곳이 달라진답니다. 즉, 낸드 플래시 메모리는 노어 플래시 메모리와 달리 반드시 부트로더가 있어야 하고 실제 타겟을 동작시키는 메인 바이너리가 있어요. 노어 플래시 메모리는 부트로더 없이 메인 바이너리만 있으면 되지만 낸드 플래시 메모리는 반드시 부트로더가 필요하답니다. 그 이유는 낸드 플래시 메모리에서는 바이너리가 동작되지 않고 반드시 에스디램으로 복사를 한 후 에스디램에서 바이너리가 동작하기 때문이죠.
 그래서 낸드 플래시 메모리를 사용한다면 Startup.s파일은 부트로더에 있어야 해요.
 
하드웨어 디버거로 startup.s 에 해당 되는 심볼정보를 올려보면 다음과 같아요.


▶코드


___addr/line__|code_____|label______________|mnemonic________________|comment
|@-------------------------------------------------
|@ @ Initialize Global Variables , Initialize Data Regien
|@-------------------------------------------------
|InitVariables:
| _Lzero_bss:
164| ldr r0, _Lbss_start
SR:000000E4|E59F003C InitVariables: ldr r0,0x128 ; r0,_Lbss
165| ldr r1, _Lbss_end
SR:000000E8|E59F103C ldr r1,0x12C ; r1,_Lbss
166| cmp r0,r1
SR:000000EC|E1500001 cmp r0,r1
167| beq L2
SR:000000F0|0A00000B beq 0x124 ; L2
168| sub r2,r2,r2
SR:000000F4|E0422002 sub r2,r2,r2
|/* copy byte by byte if not aligned */
170| tst r0, #0x3
SR:000000F8|E3100003 tst r0,#0x3
171| bne _Lzero_bytes
SR:000000FC|1A000005 bne 0x118 ; _Lzero_b
172| tst r1, #0x3
SR:00000100|E3110003 tst r1,#0x3
173| bne _Lzero_bytes
SR:00000104|1A000003 bne 0x118 ; _Lzero_b



이제 마지막으로 한 가지만 더 살펴봐요.
Startup.s와 main.c 파일을 컴파일 하면 반드시 Startup.s 파일이 먼저 실행될까요? main.c 가 먼저 실행되지는 않을까요? 둘 중에 누가 먼저 실행해야 하는지는 개발자가 직접 결정한답니다. 링크 스크립트 파일에 가장 먼저 시작해야 하는 부분을 지정하게 되어 있답니다.
 


▶코드 linker.ld
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
그리고 Startup.s 예제 파일에서 레이블 선언을 하고(b-1), _start:(c-1) 라고 레이블을 만들어 주면 바로 이곳부터 시작한다는 의미랍니다.
 
Startup.s 파일은 어셈블리 언어로 프로그램을 만들어야 하는데 씨 언어 문법과 전혀 달라서 별도로 공부를 해야 하죠. 임베디스 시스템을 처음 접하시는 분들은 대부분 독학을 하거나 프로세서 강좌를 듣는답니다. 암 시피유를 가지고 개발을 한다면 암 프로세서 강좌를 들으시면 되죠. 이러한 강좌의 커리큘럼은 암 어셈블리 언어 문법과 암 프로세서 특징, Startup.s 파일 프로그램 방법 등이며 임베디드 시스템 개발자라면 누구나 이러한 강좌를 수강하시기를 권합니다.
 

 

by 히언 | 2010/05/16 22:06 | SOTO Story | 트랙백 | 핑백(1) | 덧글(10)

Linked at 친절한 임베디드 시스템 개발자.. at 2010/05/16 22:13

... p; 300 크로스컴파일러 (Cross Compiler)가 뭐죠? 301 스타트업(Startup.s) 파일이 뭐죠? 302 메이크(Make)파일이 뭐죠? 303 씨(C) ... more

Commented by khan at 2010/05/18 12:10
항상 수고가 많으시네요.
고맙습니다.
Commented by soto at 2010/05/18 13:05
저의 팬이 생겼군요. 감사합니다. ^^
Commented by ruring at 2010/05/18 17:01
new!!!!!!!!!!!!!!!!
Commented by soto at 2010/05/19 18:28
어려운 부분은 없어요? ㅎㅎ
Commented by star at 2010/05/19 03:04
또 한 명의 팬 여기 있습니다. ㅋㅋㅋ
다음이 벌써 기다려 지네요.
Commented by soto at 2010/05/19 18:29
하하 ^^;
Commented by dong at 2010/05/19 22:58
이 홈페이지를 찾아서 참 좋습니다.
많은 도움 받고 있습니다.
고맙습니다.
Commented by soto at 2010/05/25 21:30
네. 감사합니다. ^^
Commented by at 2010/05/20 00:07
스타트업이라... 나랑 똑같네요.
나도 지금부터 스타트
Commented by soto at 2010/05/25 21:31
시작이 반이라는 말이 있죠. ㅋㅋ
조그만 더 하시면 끝이 보이죠잉~~!!
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.

◀ 이전 페이지          다음 페이지 ▶