본문 바로가기

임베디드 개발/ARM

ARM DS-5 예제 초기화 코드 분석 (startup_Cortex-A9_GCC) 방법

이 전에 올렸던 빌드했던 startup_Cortex-A9_GCC 예제 코드를 분석하는 방법에 관한 글이다. 

(솔직히 예제없이 매뉴얼만 보고 처음부터 코드 작성하기는 어려운 부분이다. 적절한 예제를 잘 구해 이용해야 한다.)

 

파일 구성은 다음과 같다.

  • startup.S - ARM 초기화
  • sorts.c - 소팅 예제
  • gcc.ld - 함수 배치할 메모리 정보기술(상용 ARM compiler와 차이가 있을 수 있으니 참고)
  • makefile - 컴파일, 링크용

먼저, startup.S 파일의 큰 흐름부터 살펴보자.

// Entry point for the Reset handler

프로그램의 시작 부분

 

// Exception Vector Table

익셉션 상황이 발생했을 때, 여기로 온다.

(리셋이나 인터럽트 발생 때, 어떻게 처리를 시켜야할까? CPU 설계자의 입장에서 생각해보자

 

// Exception Handlers

각 익셉션에 따라 수행할 코드

 

// Reset Handler

처음 부팅이나 Reset 신호가 인가될 때 수행된다.

 

// Initialize Supervisor Mode Stack

스택 위치 설정

(임베디드 시스템은 OS위의 프로그램과 달리 사용할 스택의 위치를 직접 지정해주어야 한다)

 

// Invalidate Data and Instruction TLBs and branch predictor

invalidate는 왜 해줄까? 

cache나 branch predictor에 쓰레기 데이터가 동작에 영향을 줄 수 있기 때문이다.

(대량생산 때 적은 빈도로 불량이 발생했는데, cache line 일부를 비우지 않아 발생한 문제로

몇 개월 허비한 적이 있다)

 

// Set Vector Base Address Register (VBAR) to point to this application's vector table

익셉션 vector table이 있는 주소를 설정한다.

 

// Cache Invalidation code for Cortex-A9

// MMU Configuration

// PAGE TABLE generation

// Enable MMU and branch to _start

 

MMU 관련 설정을 하고 

sorts.c의 main 본격적인 함수로 진입된다. 

 

스택 초기화 전에는 어셈블러를 이용해야 한다. 

 

처음에 스스로 공부했던 방법은

1. 일단 구글 검색이다.

예를 들어, 시작 부분의 


    .global Vectors  

 

여기서 .global 이 뭐지? 궁금해진다. 

 

"arm .global" 로 서치하면

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204ik/Babeagih.html

 

ARM Information Center

 

infocenter.arm.com

"EXPORT 지시어는 별도의 객체 및 라이브러리 파일에 있는 기호 참조를 확인하기 위해 링커에서 사용할 수 있는 기호를 선언합니다. GLOBAL EXPORT의 동의어입니다." 라고 나온다. 

 

ARM 매뉴얼은 대체로 심플하다. 이해가 부족해서 조금 더 강호의 의인의 글을 찾아보니... 

다른 파일에서 참조하려고 선언한 것 같은데, 어떤 파일에서 쓰는거지? 일단 C 파일에는 없다. 

 

2. 직접 실험 한다. 

주석 처리를 한 번 해본다. 컴파일은 잘 되었는데, 엇! 실행 시작이 바뀌었다. 

원래는 하기와 같았다. 

gcc.ld 파일에 보니, 쓰는데가 있다. 

ENTRY(Vectors) 


SECTIONS 
{ 

    .vectors 0x80000000: 
    { 
        __code_start = .; 
        KEEP(*(VECTORS)) 
    }

ENTRY 문법은 또 뭐지? 검색한다.

http://infocenter.arm.com/help/topic/com.arm.doc.dui0204ik/Chdibhie.html

 

RealView Compilation Tools 어셈블러 설명서: 7.8.5. ENTRY

ENTRY 지시어는 프로그램에 대한 진입점을 선언합니다. 프로그램에 대한 ENTRY 포인트를 최소한 하나 이상 지정해야 합니다. ENTRY가 없으면 링크 타임에 경고가 생성됩니다. 단일 소스 파일에서 둘 이상의 ENTRY 지시어를 사용하면 안 됩니다. 모든 소스 파일에 ENTRY 지시어가 있을 필요는 없습니다. 단일 소스 파일에 둘 이상의 ENTRY가 있으면 어셈블리 타임에 오류 메시지가 생성됩니다.

infocenter.arm.com

"ENTRY 지시어는 프로그램에 대한 진입점을 선언합니다." 라고 나온다. 

링커에게 프로그램의 시작점을 알려주기 위해 사용했다는 것을 알 수 있다.

 

이제 그 밑의 "리셋 핸들러" 부분도 분석해보자 

Reset_Handler: 
//---------------------------------------------------------------- 
// Disable caches, MMU and branch prediction in case they were left enabled from an earlier run 
// This does not need to be done from a cold reset 
//---------------------------------------------------------------- 
    MRC     p15, 0, r0, c1, c0, 0       // Read System Control Register 
    BIC     r0, r0, #(0x1 << 12)        // Clear I bit 12 to disable I Cache 
    BIC     r0, r0, #(0x1 <<  2)        // Clear C bit  2 to disable D Cache 
    BIC     r0, r0, #0x1                  // Clear M bit  0 to disable MMU 
    BIC     r0, r0, #(0x1 << 11)        // Clear Z bit 11 to disable branch prediction 
    MCR     p15, 0, r0, c1, c0, 0       // Write System Control Register 
    ISB

"MRC"에 대해 찾아본다. 

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204ik/Ciheeidj.html

 

ARM Information Center

 

infocenter.arm.com

MRC     p15, 0, r0, c1, c0, 0       // Read System Control Register 

"ARM 코프로세서 1번의 레지스터 값을 r0로 가져오겠다"는 것을 알 수 있다.

 

코프로세서 관련 내용은 ARM CPU 매뉴얼을 참조한다(구글에 "Cortex-A9"로 검색하면 바로 보임).

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0388e/DDI0388E_cortex_a9_r2p0_trm.pdf

 

Table 4-7에 c1 에 대한 내용이 있다.

클릭해보면, CRm이 c0이고 Op2가 0이면, 주석대로 "System Control Register" 이다. 

 

그 다음 어셈블리어를 보자. 

BIC     r0, r0, #(0x1 << 12)        // Clear I bit 12 to disable I Cache

12번째 bit을 clear 하려는 것을 알 수 있고

 

Table4-7의 "System Control Register"를 클릭하면, 각 bit의 내용이 나온다. 12번째 bit이 instruction cache를 enable/disable 하는 것으로, 주석 내용과 일치한다. 

 

 

ARM register 창에서 Z bit 을 강제로 enable 상태로 바꾸고, 코드가 동작하는지 확인해보자 

 

소스 창에서 왼쪽 라인에 더블클릭하면 브레이크 포인트가 잡힌다. 

브레이크 포인트를 잡고 F8을 누르고 F5로 하기 ISB 명령어까지 가보면

하기 레지스터 값이 Disabled 로 변경됨을 확인할 수 있다. 

 

 

위 리셋 핸들러 부분의 동작은 cache/MMU/branch prediction 기능을 disable 하는 것이다.

이후 관련 설정을 마치고 enable 한다.

예를 들어, 생활 속에서도 공기청정기 필터를 갈 때, 보통 끄고 필터를 교체한 뒤, 켜는 것처럼

동작 중에 설정에 영향을 받지 않게 하려는 것이다.

 

주석에도 써있지만, cold reset (완전히 power가 나갔다 들어온 경우)이 아닌 경우의 재시작 경우를 고려한 것이다.

//---------------------------------------------------------------- 
// Disable caches, MMU and branch prediction in case they were left enabled from an earlier run 
// This does not need to be done from a cold reset 

 

참고로, "branch prediction"은 성능을 효과적으로 높여준다.

이 기능이 처음 나왔을 때, 위 가이드처럼 처리하지 않았다가 몇 개월을 허비한 적이 있다. 

 

RVDS나 DS-5 설치하면 예제가 있으니

이를 적절히 실전에 활용하고

위와 같은 방식으로 하나씩 알아나가면 된다. 

 

초기화 때 추가로 실무서 사용할만한 것은 

  • MPU 설정
  • TCM ECC 기능 켜기
  • ZI 영역 초기화
  • RW 영역 재배치
  • 익셉션 핸들러의 활용

등이 떠오른다.

시간이 날 때 다루어보도록 하겠다. 

 

 

 

 

 

'임베디드 개발 > ARM' 카테고리의 다른 글

ARM Register 동작이해  (0) 2020.04.13
ARM Stack 동작 이해  (0) 2020.04.12
ARM Exception 디버깅 실습  (0) 2020.04.10
ARM DS-5 Community Edition 설치 및 실행  (0) 2020.04.09