1. cstartup.s
- Exception Vector Table
- reset_handler / irq_handler 구현
- lowlevel_init으로 분기 : 클럭 설정, 각 인터럽트 소스에 디폴트 핸들러 등록, Watchdog Disable (분석이 더 필요)
- Remap SRAM
- Stack 설정 : Abort, IRQ, SVC 모드일 때 각각 스택 영역 설정
- Data 영역을 RAM에 적재
- Bss 영역을 0으로 초기화
2. gnu 어셈블리 지시어
- .global <symbol> : 심볼을 외부 링크로 제공
- .align {<expression>, {<offset>}} : 다음 명령어의 주소를 <expression> + <offset> 형태로 정렬
ARM은 기본적으로 거의 모든 명령이 4 byte로 구성되어 진다. 즉
4의 배수로 실행 코드를 쪼개서 기계어 하나하나를 해석해 낸다고 보면 된다. 그런데 asm 코드들 중간에 address나 변수 등을 정의할 때 1 byte나 2 byte로 정의를 해버리면 4의 배수가 않되는 경우가 발생한다. 이때 오류가 발생할 수 있다. 즉 4의 배수를 맞추기 위하여 0으로
초기화되는 byte들을 추가로 패딩해주는 것이 필요한데 이것을 해주는
directive이다.
- .section <section_name> {, "<flags>"} : 새로운 코드 색션 또는 데이터 색션을 시작한다. 보통 코드 색션 .text, 초기화 된 데이터 색션 .data, 초기화 되지 않은 데이터 색션 .bss를 호출해야 한다. 이것들은 디폴트 플래그를 가지고 있으며 링커는 이 디폴트 이름들을 이해하고 있다.
- .word <word1> {,<word2>}... : ARM asm에서의 DCD와 같이 데이터로 32비트 워드값 목록들을 어셈블리에 삽입한다.
3. cstartup.S 코드
#include "project.h"
#define TOP_OF_MEMORY (AT91C_ISRAM + AT91C_ISRAM_SIZE)
#define ABT_STACK_SIZE 8*3*4
#define IRQ_STACK_SIZE 8*3*4
#define ARM_MODE_ABT 0x17
#define ARM_MODE_FIQ 0x11
#define ARM_MODE_IRQ 0x12
#define ARM_MODE_SVC 0x13
#define I_BIT 0x80
#define F_BIT 0x40
/* Application startup entry point */
.globl reset_handler // reset_handler 심볼을 외부에 보이도록 한다.
.align 4 // 4 바이트 단위로 정렬하겠다는 의미
.section .vectors /* .vectors 색션 시작 */
.arm
/// 1. start ////////////////////////////////////////////////////////////////////////
/* Exception vectors (should be a branch to be detected as a valid code by the rom */
/* exception vector table */
_exception_vectors:
reset_vector:
ldr pc, =reset_handler /* Reset vector : pc := reselt_handler 0x00000000 */
/* reset_handler로 분기됨 */
undef_vector:
b undef_vector /* Undefined Instruction : 미구현 0x00000004 */
swi_vector:
b swi_vector /* Software Interrupt : 미구현 0x00000008 */
pabt_vector:
b pabt_vector /* Prefetch Abort : 미구현 0x0000000C */
dabt_vector:
b dabt_vector /* Data Abort : 미구현 0x00000010 */
rsvd_vector:
b rsvd_vector /* reserved : 예약되어 있는 공간 0x00000014 */
irq_vector:
b irq_handler /* IRQ : read the AIC 0x00000018 */
fiq_vector:
/*------------------------------------------------------------------------------
*- Function : fiq_handler
*- Treatments : FIQ Interrupt Handler.
*- Called Functions :
*------------------------------------------------------------------------------*/
fiq_handler:
b fiq_handler /* FIQ : 미구현 0x0000001C */
/* exception vector table end */
/* x86의 경우엔 해당 벡터에 address를 넣어 두면, 인터럽트 발생시에 해당 주소를 가져다가
PC(Program Counter)에 넣어 주는 일이 발생하지만, ARM7의 경우엔 그냥 해당 벡터로 점프한다.
예를 들어 IRQ가 발생했다면 다음 순간의 PC값은 0x00000018이 된다. 따라서 해당 벡터 번지에는
단순히 번지가 들어가는 것이 아니라 점프 명령 같은 것이 들어간다. */
/// 1. end //////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------------------
*- Function : irq_handler
*- Treatments : IRQ Controller Interrupt Handler.
*- Called Functions : AIC_IVR[interrupt]
*------------------------------------------------------------------------------*/
irq_handler:
/*- Manage Exception Entry */
/*- Adjust and save LR_irq in IRQ stack */
sub lr, lr, #4 /* lr := lr - 4 / 돌아갈 주소 교정 */
stmfd sp!, {lr} /* *sp := lr / 스택에 lr 저장 */
/*- Save r0 and SPSR in IRQ stack */
mrs r14, SPSR /* r14 := SPSR */
stmfd sp!, {r0,r14} /* *sp <- r0, r14 */
/*- Write in the IVR to support Protect Mode */
/*- No effect in Normal Mode */
/*- De-assert the NIRQ and clear the source in Protect Mode */
ldr r14, =AT91C_BASE_AIC /* r14 := AT91C_BASE_AIC */
ldr r0 , [r14, #AIC_IVR] /* r0 := *(r14 + AIC_IVR) */
str r14, [r14, #AIC_IVR] /* *(r14 + AIC_IVR) := r14 */
/*- Enable Interrupt and Switch in Supervisor Mode */
msr CPSR_c, #ARM_MODE_SVC
/*- Save scratch/used registers and LR in User Stack */
stmfd sp!, {r1-r3, r12, r14}
/*- Branch to the routine pointed by the AIC_IVR */
mov r14, pc
bx r0
/*- Restore scratch/used registers and LR from User Stack */
ldmia sp!, {r1-r3, r12, r14}
/*- Disable Interrupt and switch back in IRQ mode */
msr CPSR_c, #ARM_MODE_IRQ | I_BIT
/*- Mark the End of Interrupt on the AIC */
ldr r14, =AT91C_BASE_AIC
str r14, [r14, #AIC_EOICR]
/*- Restore SPSR_irq and r0 from IRQ stack */
ldmia sp!, {r0,r14}
msr SPSR_cxsf, r14
/*- Restore adjusted LR_irq from IRQ stack directly in the PC */
ldmia sp!, {pc}^
/// 2. start ////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------------------
*- Function : reset_handler
*- Treatments : Reset Interrupt Handler.
*- Called Functions : lowlevel_init
* main
*------------------------------------------------------------------------------*/
.section .text /* text 색션 시작 */
reset_handler:
ldr pc, =_low_level_init /* pc := _low_level_init */
/* _low_level_init 으로 분기됨 */
/*------------------------------------------------------------------------------
*- Low level Init is performed in a C function: lowlevel_init
*- Init Stack Pointer to a valid memory area before calling lowlevel_init
*------------------------------------------------------------------------------*/
/*- Temporary stack in internal RAM for Low Level Init execution */
_low_level_init:
ldr r2, =_lp_ll_init /* r2 := _lp_ll_init */
ldmia r2, {r0, r1} /* r0 := lowlevel_init, r1 := TOP_OF_MEMORY */
mov sp, r1 /* sp := r1 sp := TOP_OF_MEMORY / 스택 위치 설정 */
mov lr, pc /* lr := pc return address를 저장 */
bx r0 /* Branch on C function (interworking) */
/* lowlevel_init으로 분기됨, ARM상태 */
/* #define TOP_OF_MEMORY ((char *) 0x00200000 + (0x00010000)) */
/// 2. end //////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/// 3. start ////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------------------
*- Remap SRAM at 0x0
*------------------------------------------------------------------------------*/
/* AT91C_MC_RCR : (MC) MC Remap Control Register */
_remap:
ldr r2, _lp_remap /* r2 := _lp_remap / r2 := AT91C_MC_RCR */
mov r0, #AT91C_MC_RCB /* r0 := AT91C_MC_RCB / AT91C_MC_RCB:1 */
str r0, [r2] /* *r2 := r0 / MC_RCR 레지스터에 1을 세팅 */
/* SRAM 영역이 0x00000000으로 리맵핑 된다 */
/*------------------------------------------------------------------------------
*- Setup the stack for each mode
*------------------------------------------------------------------------------*/
_stack_init:
ldr r2, =_lp_stack_init /* r2 := _lp_stack_init */
ldmia r2, {r0, r1, r2} /* r0 := TOP_OF_MEMORY / r1 := ABT_STACK_SIZE / r2 := IRQ_STACK_SIZE */
/* #define ABT_STACK_SIZE 8*3*4
#define IRQ_STACK_SIZE 8*3*4 */
/*- Set up Abort Mode and set ABT Mode Stack */
msr CPSR_c, #ARM_MODE_ABT | I_BIT | F_BIT /* Abort mode로 변경, IRQ/FIQ disable */
mov sp, r0 /* sp := r0 / sp := TOP_OF_MEMORY : Abort mode 스택 포인터 설정 */
sub r0, r0, r1 /* r0 := r0 - r1 */
/*- Set up Interrupt Mode and set IRQ Mode Stack */
msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT /* IRQ mode로 변경, IRQ/FIQ disable */
mov sp, r0 /* sp := r0 / sp = TOP_OF_MEMORY - 8*3*4 : IRQ mode 스택 포인터 설정 */
sub r0, r0, r2 /* r0 := r0 - r2 */
/*- Enable interrupt & Set up Supervisor Mode and set Supervisor Mode Stack */
msr CPSR_c, #ARM_MODE_SVC | F_BIT /* SVC mode로 변경, FIQ disable */
mov sp, r0 /* sp := r0 / sp = TOP_OF_MEMORY - 8*3*4 - 8*3*4 : SVC mode 스택 포인터 설정 */
/* Abort, IRQ, SVC mode들의 스택 포인터들을 설정 */
/* 각 모드 마다 스택 포인터가 각각 독립적으로 존재함으로 각 모드의 스택 영역을 설정한 것이다 */
/*------------------------------------------------------------------------------
*- Segments initialization
*------------------------------------------------------------------------------*/
/* Copy the data section in RAM at 0x0000 */
_init_data:
ldr r2, =_lp_data /* r2 := _lp_data */
ldmia r2, {r1, r3, r4} /* r1 := _etext / r3 := _sdata / r4 := _edata */
1:
cmp r3, r4 /* CPSR flag := r3 - r4 */
ldrcc r2, [r1], #4 /* C flag clear 이면 r2 := *r1 / r1 := r1 + 4 */
strcc r2, [r3], #4 /* C flag clear 이면 *r3 := r2 / r3 := r3 + 4 */
bcc 1b /* C flag clear 이면 1로 분기 */
/* _etext : 코드 영역의 끝을 가리킨다 */
/* _sdata : 데이터 영역의 시작을 가리킨다 */
/* _edata : 데이터 영역의 끝을 가리킨다 */
/* Clear the bss segment */
_init_bss:
ldr r2, =_lp_bss /* r2 := _lp_bss */
ldmia r2, {r3, r4} /* r3 := _sbss / r3 := _ebss */
mov r2, #0 /* r2 := 0 */
1:
cmp r3, r4 /* CPSR flag := r3 - r4 */
strcc r2, [r3], #4 /* *r3 := r2 / r3 := r3 + 4 */
bcc 1b /* C flag clear 이면 1로 분기 */
/* bss 영역을 0으로 초기화 */
/* _sbss : bss 영역의 시작을 가리킨다 */
/* _ebss : bss 영역의 끝을 가리킨다 */
/*------------------------------------------------------------------------------
*- Branch to the main
*------------------------------------------------------------------------------*/
_branch_main:
ldr r0, =main /* r0 := main */
mov lr, pc /* lr := pc / return address 저장 */
bx r0 /* main으로 분기 / ARM 상태 */
/// 3. end //////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------------------
*- Litteral pools
*------------------------------------------------------------------------------*/
_lp_ll_init:
.word lowlevel_init
.word TOP_OF_MEMORY /* Default SVC stack after power up */
_lp_remap:
.word AT91C_MC_RCR
_lp_stack_init:
.word TOP_OF_MEMORY /* Top of the stack */
.word ABT_STACK_SIZE /* ABT stack size */
.word IRQ_STACK_SIZE /* IRQ stack size */
_lp_bss:
.word _sbss
.word _ebss
_lp_data:
.word _etext
.word _sdata
.word _edata