이 전에 올렸던 빌드했던 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)
|
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 |