레이블이 arm인 게시물을 표시합니다. 모든 게시물 표시
레이블이 arm인 게시물을 표시합니다. 모든 게시물 표시

2014년 10월 21일 화요일

C main design

일반적으로 bootup code는 startup.S로 작성한다.
startup.S에는 다음 내용이 들어간다.
- vector table
- stack pointer 초기화
- 전역변수 초기화
- 기타 이미지 로딩
- pc를 main으로 이동, 꼭 main이 아니어도 되며, startup.S에서 branch 해주기만 하면 된다. 이 때 부터 C 코드를 사용 가능함.
- C code를 사용하려면 기본적으로 변수 초기화, sp 초기화 등을 해야 한다. 하지 않으면 당연히 data abort!

변수의 class
- register variable
- 예약어는 register이며, 쓴다고 항상 적용되지 않고, 성능에 영향을 준다고 판단하면 그냥 stack에 저장한다.
- 단, register를 사용하면, 주소값을 읽을 수 없다. 원래 register에 저장하라고 하는 것이니깐.
- local variable
- auto 예약어인데 일반적으로 생략된다.
- static variable
- global variable

memory layout
- 아래서부터,
- TEXT(program code)
- RODATA(constant)
- DATA(initialized data)
- BSS(uninitialized data)
- HEAP(BSS 이후로 커감)
- STACK(메모리 끝에서 부터 내려옴)
- 원하는 section에 넣으려면, linker를 이용한다.
- t32에서 section 확인하려면, sYmbol.List.SECtion

Inline assembly
- C보다 빠른 처리를 위함이고, __volatile__ 예약어를 사용해야 컴파일러가 맘대로 최적화 하지 않는다
- 일반적인 형태:
- __asm__ __volatile__("" : "" : "" : "")
- clobber는 개발자가 직접 register를 제어한다면 설정, compiler는 사용 못하도록 하며, 생략 가능하다.
- ex.
__asm__ __volatile__("add %0, %1, %2" : "=r" (result) : "r" (kind), "r" (embedded) : "r0", "r1")
r0, r1은 안쓴다는 의미임

Colling Convention
- ARM사에서 제시한 표준: PCS(procedure call standard), APCS(ARM procedure call standard), TPCS(Thumb ~), AAPCS(ARM Arch ~)
- r0, r1은 argument / result
- r2, r3는 argument / scratch register 등등..
- r13: stack pointer
- r14: link register

Call Assembly - C와 Assembly 상호 호출 방법
- Assembly에서 global keyword를 사용하여 선언하고 c에서 사용
- C symbol은 Assembly에서 .extern keyword 사용하여 호출
예시
startup.S
.extern main
BL main
.global MMU_DisableiCache
MMU_DisableiCache :
mrc p15,0,r0,c1,c0,0
bic r0, r0,#R1_I
mcr p15,0,r0,c1,c0,0
mov pc,lr

main.c
int main() {
MMU_DisableiCache();
return 0;
}

Call C++
- C와 Assembly처럼 심볼을 그대로 가져다 쓸 수 없음.
- compiler가 함수명을 가공하기 때문임.
- c++에서는 extern "C" { }를 이용해서 C의 symbol을 가져옴.

compile

C 파일을 compile 하면(main.c)
- 전처리기가 main.i를 생성
- compiler가 main.S를 생성
- assembler가 main.o를 생성
- 사람이 읽을 수 있는 언어를 hex code로 변경해 준다.
- objdump -S main.o <-- object의 정보를 확인할 수 있다.
- linker가 a.elf를 생성
- linker가 하는 역할은 object 파일을 메모리에 mapping을 하는 것
- input file들이 같은 메모리에 위치하여 서로 symbol을 사용할 수 있게 해준다.
- 각 object들의 .text(code), .data 들을 section별로 정리해서 elf로 저장한다.
- objcopy로 a.bin을 생성
- objcopy는 elf의 header, debug info 등을 날려버리고 binary만을 추출한다.
- 디버깅을 하려면 그래서 elf가 필요하다.
- gcc option인 --save-temps를 넣으면 위의 이미지들을 삭제하지 않는다.

booting mode

Booting mode는 어떤 메모리를 이용하는지에 따라 여러가지가 있다.
보통 Hardware pin으로 설정할 수 있도록 하며, 각 mode별로 memory mapping이 달라진다.
- NOR flash mode: NOR는 비싸고 부피가 큰 메모리로, 주소에 접근해서 실행 가능하다. 그래서 부트로더를 NOR flash에 넣고 부팅할 수 있다.
- NAND flash mode:
-NAND는 싸고 집적도가 높다. 하지만 주소가 없고 블락별로 data를 읽을 수 있도록 되어 있다. 즉 I/O port만 있어서 전자적 신호로 Page단위로 load하여 주소 접근이 가능한 다른 메모리에 복사하여 사용해야 한다.
- 일반적으로 MCU 내부에 IRAM에 복사해서 사용한다.
- 간단히 요약하자면, IROM이 NAND에서 IRAM으로 부트 코드를 복사한 다음에 PC를 IRAM으로 이동해 준다. IRAM은 DRAM을 초기화 하고 OS image를 얹은 다음 PC를 DRAM으로 이동해 주어 'stepping stone' 방식으로 부팅하게 된다.
- 일반적으로 boot code를 이동하는 IRAM 주소는 vendor datasheet에 있음.
- 그 외에 network booting mode / usb booting mode 등이 있다.

Linker script

일반적으로 복잡한 Memory map이 필요한 binary를 위해 사용 된다.
Linker가 필요한 정보
- 입력 section들이 어떻게 region으로 그룹화 되었는지 그룹화 정보
- Memory map에서 각 region이 어디에 위치한지 정보

Section name and macro
- .text: __TEXT_START__ / __TEXT_END__
- .bss: __BSS_START__ / __BSS_END__
- .data ..

Linker script를 사용하기 위한 옵션
- ld 옵션에 -T FILE 또는 --script FILE 으로 사용함.
- ex.
LDFLAGS = ~~~ -T linker.ld -Map $(OUTDIR)/$(BIN).map

Linker script 작성
- OUTPUT 형식
- OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm")
- OUTPUT_ARCH(arm)
- entry point
- ENTRY(_start)
- sections
- SECTIONS
{
section-command
section-command
..
}
- ex.
SECTIONS
{
        .text __TEXT_START__ :
        {
                __TEXT_START__ = .;
                *startup.o (.text);
                *main.o (.text);
                *(.text);
                . = ALIGN(4);
                __TEXT_END__ = .;
        }
}
작성 예시
OUTPUT_FORMAT("elf32-littlearm")
OUTPUT_ARCH(arm)

ENTRY(_start)

__TEXT_START__ = 0x20000000;

SECTIONS
{
        .text __TEXT_START__ :
        {
                __TEXT_START__ = .;
                *startup.o (.text);
                *main.o (.text);
                *(.text);
                . = ALIGN(4);
                __TEXT_END__ = .;
        }

        .rodata :
        {
                __RODATA_START__ = .;
                *startup.o (.rodata);
                *main.o (.rodata);
                *(.rodata);
                __RODATA_END__ = .;
                . = ALIGN(4);
        }
        .bss :
        {
                __BSS_START__ = .;
                *startup.o (.bss);
                *main.o (.bss);
                *(.bss);
                __BSS_END__ = .;
                . = ALIGN(4);
        }

        _end = .;
        __end = _end;
        PROVIDE(end = .);
}

2014년 10월 6일 월요일

arm. arm의 register

내 본업은 system engineer이다.

그간 정리해놓았던 것들을 blog로 옮겨보려고 한다. 차근차근...



ARM register 37개임거의범용 register로 구성되어 있다.
User 
System 
FIQ 
Supervisor 
Abort 
IRQ 
Undef 
R0 






R1 






R2 






R3 






R4 






R5 






R6 






R7 






R8 

R8_fiq 




R9 

R9_fiq 




R10 

R10_fiq 




R11 

R11_fiq 




R12 

R12_fiq 




R13(SP) 

R13_fiq 
R13_svc 
R13_abt 
R13_irq 
R13_und 
R14(LR) 

R14_fiq 
R14_svc 
R14_abt 
R14_irq 
R14_und 
R15(PC) 







CPSR 







SPSR_fiq 
SPSR_svc 
SPSR_abt 
SPSR_irq 
SPSR_und 

Current Program Status Register(CPSR) 
  • 31 ~ 28 (NZCV) 
    • N: Negative 
    • Z: Zero 
    • C: Carry 
    • V: oVerflow 
  • 27 ~ 8: unused 
  • 7: IRQ 
  • 6: FIQ enable(0)/disable(1), interrupt 걸리지 않도록 control 가능함. 
  • 5: Thumb mode / ARM mode 
  • 4 ~ 0: 현재의 mode 
Saved Program Status Register(SPSR) 
  • CPSR 복사해 넣는 특수 Register. 백업용임. 
  • SPSR 값을 CPSR 다시 집어 넣으면 이전 mode 곧바로 복귀 가능함. 

R14: Linked Register(LR) 
  • 어딘가로 jump 어디서 branch 왔는지 표시하는  

R13: Stack Pointer(SP) 
  • 현재 Stack 어디까지 쌓아두었는가. 

R15: Program counter(PC) 
  • 현재 어디를 수행하고 있는지를 나타냄. 

Context: Register set snapshot. , R0 ~ R1, CPSR snapshot 

Thumb / ARM mode 차이 
  • Thumb R0 ~ R7 사용 
  • ARM R0 ~ R12까지 사용 
FIQ banked register 많은 이유는 stack backup하는 시간 절약... 

Kernel 영역에서 행하는 것은 supervisor mode. 
User 영역에서 실행하는 것은 user mode. 
모델에서 0~3 4개의 모드가 있는데 kernel 0모드, user 3 모드, 1, 2 OS 제공해줌. 
user mode system call SWI(SW interrupt) 명령을 내리면 kernel 영역으로 진입함. 
unistd.h system call 정의 되어 있음