Subwayway Embedded DEV

메모리 설계, 익셉션 핸들러

개발 환경

  • Window 10, WSL1(Ubuntu 18.04)
  • QEMU(realview-pb-a8)
  • Graphic : VcXsrv Windows X Server

메모리는 크게 3가지 영역으로 나뉨.

  • text 영역 : 사용자 코드
  • data 영역 : 초기화된 전역, 정적 변수 저장 / 동작 모드별 스택, 태스크 스택
  • bss 영역 : 초기화 되지 않은 전역, 정적 변수 저장
  • stack, heap 영역 : 태스크, 동적할당

text 영역 메모리 설계

  • RTOS 펌웨어를 수십 kb로 가정하고 지정
할당
크기
주소
text 1MB 0X00000000~0X000FFFFF

동작모드 스택을 메모리에 설계

  • USR모드와 SYS모드는 메모리공간과 레지스터 공유
할당
크기
주소
USR,SYS 2MB 0X00100000~0X002FFFFF
SVC 1MB 0X00300000~0X003FFFFF
IRQ 1MB 0X00400000~0X004FFFFF
FIQ 1MB 0X00500000~0X005FFFFF
ABT 1MB 0X00600000~0X006FFFFF
UND 1MB 0X00700000~0X007FFFFF

테스크 스택, 전역변수, 동적 할당 영역 설계

  • RTOS의 동작 테스크를 1MB씩 64개 할당
할당
크기
주소
테스크 스택 64MB 0X00800000~0X047FFFFF
전역 변수 1MB 0X04800000~0X048FFFFF
동적 할당 55MB 0X04900000~0X07FFFFFF
1+2+1+1+1+1+1+64+1+55=128MB

Entry.S

익셉션 벡터 테이블

#include "ARMv7AR.h"	;동작모드 전환 비트 헤더파일
#include "MemoryMap.h"	;메모리맵 주소 헤더파일

.text
    .code 32

    .global vector_start
    .global vector_end

    vector_start:
      LDR PC, reset_handler_addr
      LDR PC, undef_handler_addr
      LDR PC, svc_handler_addr
      LDR PC, pfrch_abt_handler_addr
      LDR PC, data_abt_handler_addr
      B .
      LDR PC, irq_handler_addr
      LDR PC, fiq_handler_addr

      reset_handler_addr: .word reset_handler
      undef_handler_addr: .word reset_handler
      svc_handler_addr: .word reset_handler
      pfrch_abt_handler_addr: .word reset_handler
      data_abt_handler_addr: .word reset_handler
      irq_handler_addr: .word reset_handler
      fiq_handler_addr: .word reset_handler
    vector_end:

    reset_handler:
      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_SVC	;SVC 동작모드 비트
      MSR cpsr, r1	;cpsr에 동작모드 비트를 설정
      LDR sp, =SVC_STACK_TOP	;스택포인터에 SVC스택 탑 메모리 주소 설정

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_IRQ
      MSR cpsr, r1
      LDR sp, =IRQ_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_FIQ
      MSR cpsr, r1
      LDR sp, =FIQ_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_ABT
      MSR cpsr, r1
      LDR sp, =ABT_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_UND
      MSR cpsr, r1
      LDR sp, =UND_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_SYS
      MSR cpsr, r1
      LDR sp, =USRSYS_STACK_TOP

      BL main
    dummy_handler:
      B .
.end

스택은 메모리를 반대방향으로 사용하기 때문에 스택의 꼭대기 주소를 스택포인터로 지정한다.


ARMv7AR.h

동작모드 전환 비트 헤더파일

#define ARM_MODE_BIT_USR  0X10
#define ARM_MODE_BIT_FIQ  0X11
#define ARM_MODE_BIT_IRQ  0X12
#define ARM_MODE_BIT_SVC  0X13
#define ARM_MODE_BIT_ABT  0X17
#define ARM_MODE_BIT_UND  0X1B
#define ARM_MODE_BIT_SYS  0X1F
#define ARM_MODE_BIT_MON  0X16

MemoryMap.h

메모리맵 주소, 사이즈, 스택 탑 위치 헤더파일

#define INST_ADDR_STATRT 0
#define USRSYS_STACK_START 0X00100000
#define SVC_STACK_START 0X00300000
#define IRQ_STACK_START 0X00400000
#define FIQ_STACK_START 0X00500000
#define ABT_STACK_START 0X00600000
#define UND_STACK_START 0X00700000
#define TASK_STACK_START 0X00800000
#define GLOBAL_STACK_START 0X04800000
#define DALLOC_STACK_START 0X04900000

#define INST_MEM_SIZE (USRSYS_STACK_START - INST_ADDR_STATRT)
#define USRSYS_STACK_SIZE (SVC_STACK_START - USRSYS_STACK_START)
#define SVC_STACK_SIZE (IRQ_STACK_START - SVC_STACK_START)
#define IRQ_STACK_SIZE (FIQ_STACK_START - IRQ_STACK_START)
#define FIQ_STACK_SIZE (ABT_STACK_START - FIQ_STACK_START)
#define ABT_STACK_SIZE (UND_STACK_START - ABT_STACK_START)
#define UND_STACK_SIZE (TASK_STACK_START - UND_STACK_START)
#define TASK_STACK_SIZE (GLOBAL_STACK_START - TASK_STACK_START)
#define DALLOC_MEM_SIZE (55*1024*1024)

//스택 사이의 패딩(padding)을 위한 4바이트 비우기
#define USRSYS_STACK_TOP (USRSYS_STACK_START + USRSYS_STACK_SIZE - 4)
#define SVC_STACK_TOP (SVC_STACK_START + SVC_STACK_SIZE - 4)
#define IRQ_STACK_TOP (IRQ_STACK_START + IRQ_STACK_SIZE - 4)
#define FIQ_STACK_TOP (FIQ_STACK_START + FIQ_STACK_SIZE - 4)
#define ABT_STACK_TOP (ABT_STACK_START + ABT_STACK_SIZE - 4)
#define UND_STACK_TOP (UND_STACK_START + UND_STACK_SIZE - 4)

Makefile

ARCH = armv7-a
MCPU = cortex-a8

CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
OC = arm-none-eabi-objcopy

LINKER_SCRIPT = ./milktea.ld
MAP_FILE = build/milktea.map

ASM_SRCS = $(wildcard boot/*.S)
ASM_OBJS = $(patsubst boot/%.S, build/%.os, $(ASM_SRCS))

C_SRCS = $(wildcard boot/*.c)
C_OBJS = $(patsubst boot/%.c, build/%.o, $(C_SRCS))


INC_DIRS = include

milktea = build/milktea.axf
milktea_bin = build/milktea.bin

.PHONY: all clean run debug gdb

all: $(milktea)

clean:
	@rm -fr build

run: $(milktea)
	qemu-system-arm -M realview-pb-a8 -kernel $(milktea) -nographic

debug: $(milktea)
	qemu-system-arm -M realview-pb-a8 -kernel $(milktea) -S -gdb tcp::1234,ipv4

gdb:
	arm-none-eabi-gdb

$(milktea): $(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)
	$(LD) -n -T $(LINKER_SCRIPT) -o $(milktea) $(ASM_OBJS) $(C_OBJS) -Map=$(MAP_FILE)
	$(OC) -O binary $(milktea) $(milktea_bin)

build/%.os: $(ASM_SRCS)
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<

build/%.o: $(C_SRCS)
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<