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
















+ Recent posts