ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Protected Regions

Initializing the MPU, Caches, and Write Buffer

Demonstration of an MPU System




#### 13. 메모리 보호 장치 (MPU, Memory Protection Units)


- protection : 원치 않는 액세스로부터 시스템 자원과 태스크들을 막아주는 것


- 시스템 자원 액세스를 제어하는 방법 : unprotected(비보호), protected(보호)

 unprotected systems

 소프트웨어에만 의존하여 시스템 자원을 보호

 protected systems

 하으뒈어와 소프트웨어 둘다에 의존하여 시스템 자원을 보호

* 프로세서의 기능과 제어 시스템의 요구사항에 맞춰 제어 방법을 선택


# Unprotected Embedded Systems

- 동작 중 메모리와 주변장치를 사용하돋록 강요하는 전용 하느웨어가 없음

- 시스템 자원을 액세스하기 위해 각 태스크들이 다른 모든 태스크와 동기를 맞춰야 함


# Protected Embedded Systems

- 시스템 자원의 액세스를 확인하고 제한하는 정용 하드웨어를 가짐

- 전용 하드웨어는 자원의 소유권을 가짐 (하드웨어 레벨에서 자원들을 모니터링하고 제어하는 특별한 권한)

- 소프트웨어 루틴보다 시스템을 더 잘 관리할 수 있음


# MPU : 능동적으로 시스템 자원을 보호하는 하드웨어

- 보호 시스템의 주요 보호대상 자원 : 메모리 시스템, 주변장치

- ARM의 주변장치는 일반적으로 메모리 매핑이 되어 있어 MPU는 두 자원을 보호하기 위해 동일한 방법을 제공(region을 이용)


# Region(영역) : 메모리 영역과 관련되어 있는 속성들의 집합

프로세서 코어는 몇몇의 CP15 레지스서 안에 속성값들을 저장(각 영역은 0-7의 번호로 규정)

- 메모리 경계는, 시작주소와 4KB와 4GB사이의 2의 지수로 설정되는 크기두 속성 값을 사용해 설정됨

=> 이 영역에 접근권한, 캐시쓰기정책(캐시 및 write buffer)와 같은 추가의 속성을 할당

* 접근권한 : RW, RO, no-access, 추가권한 : 현재 프로세서 모드(유저/특권 모드)

- 프로세서의 메인메모리 영역에 대한 접근 제어 : 

프로세서의 접근권한 및 현재 프로세서 모드를 비교하여 메모리 액세서를 허용하거나 abort 신호를 발생하여 메모리 액세스를 차단

- abort 신호 : exception 처리로 abort  handler로 분기하도록 한 후 처리를 위해 적절한 서비스 루틴으로 분기

* abort type : prefetch abort, data abort



### 13.1. Protected Regions


- ARM740T, ARM940T, ARM946E-S, ARM1026EJ-S


ARM940T

- 16개의 Protected Regions

- 명령어 메모리와 데이터 메모리를 제어하기 위한 분리된 영역을 가짐

- 코어는 명령어와 데이터 영역에ㅐ 대해 다른 영역 크기와 시작주소를 할당할 수 있음

- region 번호 : 0-7 사이의 번호로 할당되지만 명령어 영역과 데이터 영역의 pair로 이루어짐(8개씩)


ARM740T, ARM946E-S, ARM1026EJ-S

- 8개의 Protected Regions

- 명령어 영역과 데이터 영역이 통합

=> 데이터영역과 명령어영역은 크기와 시작주소를 설정하기 위해 똑같은 레지스터를 사용

메모리 접근 권한과 캐시 정책

ARM740T : 명령어와 데이터 액세스를 동일하게 적용 

ARM946E-S, ARM1026EJ-S : 명령어 액세스와 데이터 액세스를 독립적으로 설정 가능

- region 번호 : 코어가 폰노이만 아키텍처인지 하버디 아키텍처인지에 상관없이 독립적이며, 

각 영역은 0-7 사이의 번호로 할당


> region 제어를 위한 몇가지 규칙 <

1. 영역은 다른 영역에 overlap 될 수 있음

2. 영역은 영역에 할당되어 있는 특권에 독립적인 우선순위가 할당

3. 영역이 overlap 되면, 가장 높은 우선순위를 가진 영역 속성이 다른 영역에 대해 우선순위를 가짐.

* 우선순위는 overlap된 영역내의 주소에 대해서만 적용

4. 영역의 시작주소는 그 크기의 배수여야 함

5. 영역의 크기는 4KB-4GB 사이의 2의 지승의 값이여야 함(4KB, 8KB, 16KB, 32KB, 64KB, ...., 2GB, 4GB)

6. 정의된 영역 밖에 있는 메인메모리의 영역에 액세스를 시도하면, abort를 발생

- 명령어를 페치하고 있었다면 prefetch abort가 발생,

- 데이터를 위한 메모리 요청이었다면 data abort가 발생



## 13.1.1. Overlapping Regions

- Overlapping : 한 영역에 할당되어 있는 메모리 공간의 일부가 다른 메모리 영역에 할당되어있는 메모리 공간 안에 있는경우

* 접근 권한은 할당 할 때 overlap을 지원하지ㅏ 않는 영역보다 큰 유연성을 제공


ex. 가정 : 0x000000000 주소에서 시작하는 256KB의 메모리에서, user mode로 액세스가 불가능하도록 특권 시스템 영역에서 보호 중


특권영역의 코드와 데이터 스택이 벡터테이블 0x00000000에서 시작하는 32KB의 영역안에 맞춰져 있음

남은 공간은 user 공간으로 할당되어 있음

- overlap 영역과 함께 시스템은  256KB의 유저영역과 32KB의 특권 영영의 두 영역을 사용

- 특권영역 1은 속성이 유저역역 0보다 우선순위가 높음


## 13.1.2. Background Regions

- 큰 메모리 영역에 동일한 속성을 할당하는 데 사용되는 가장 낮은 우선순위의 영역

- 더 높은 우선순위를 가진 다른 영역들은,

 정의된 background 영역보다 적은 영역의 속성을 변경하기 위해서 background 영역에 overlapping 됨.

=> 더 높은 우선순위의 영역은 background 영역의 속성 일부를 변경하면 됨

- background 영역의 또 다른 부분은 다른 영역의 제어하에 활성화 됨

=> 원치 않는 액세스로 부터 몇몇 사용되지 않은 메모리 영역을 보호함


- 커다란 특권 영역을 정의하고 있을 때, background 영역이 오버랩되어 더 작은 비특권 영역을 정의할 수 있음

- 더 작은 비특권 영역은 다른 유저 공간을 나타내기 위해 background 영역의 다른 영역으로 이동 할 수 있음

이전에 관리되던 영역은 background 영역에 의해 보호됨

=> user 영역은 특권 배경의 다른 부분을 액세스 하지만, user 레벨 속성을 갖는 윈도우로 동작


> 3개의 간단한 태스크 보호 방법 < 

- 영역 3은 active task의 보호 속성을 정의

- background 영역 0은 non-active한 태스크로의 액세스를 통제

- task 1이 active 할 때, background 영역은 task 1로부터 task 2와 task 3을 보호

- task 2가 active 할 때, background 영역은 task 2로부터 task 1과 task 3을 보호

task 3이 active 할 때, background 영역은 task 3로부터 task 1과 task 2를 보호

=> 영역 0이 특권모드로써 더 높은 특권을 가지고는 있지만, 영역 3이 우선순위가 더욱 높기 때문



### 13.2. Initializing the MPU, Caches, and Write Buffer


MPU, Caches, Write Buffer의 초기화를 위해, 제어 시스템은 타겟이 동작하는 동안 필요로하는 보호 영역을 정의해야 함

- 최소한 보호 장치를 enable하기 전 적어도 하나의 데이터 영역과 하나의 명령어 영역을 정의해야 함

- 제어 시스템은 기본 CP15 레지스터인 c1, c2, c3, c4, c5, c6의 값을 설정하여 MPU를 설정


> MPU 동작 제어를 위한 CP15의 레지스터들

* c1 : 기본 컨트롤 레지스터


- c2, c3으로 영역의 cache와 write buffer의 속성이 설정

- c5 : 영역 접근 권한 제어

- c6 : 각 영역의 위치와 크기를 정의하며 8 or 16개의 보조 레지스터를 가짐


> MPU, caches, write-buffer의 초기화 수행 단계

1. CP15:c6를 사용하여 명령어 및 데이터 영역의 크기와 위치를 정의

2. CP15:c5를 사용하여 각 영역에 대한 접근 권한을 설정

3. 캐시를 위해서는 CP15:c2, write-buffer를 위해서는 CP15:c3를 사용하여 각 영역을 위한 cache, write-buffer의 속성을 지정

4. CP15:c1을 사용하여 캐시와 MPU를 enable.


# 13.2.1. Defining Region Size and Location

- CP15:c6:c0:0 - CP15:c6:c7:0 까지의 8개의 보조 레지스터 중 하나의 값을 설정하여 각 영역의 크기와 주소 범위를 정의

=> 보조 코프로세서 레지스터 숫자는 각 영역의 ID에 매핑

- 각 영역의 시작주소는 그 크기의 배수인 주소로 정렬

ex. 영역의 크기 128KB, 0x20000의 배수가 되는 어떤 주소에서 시작


> 8개의 보조 레지스터 CP15:c6:c0 - CP15:c6:c7의 포맷과 비트필드



- base address : 상위 [31-12], 비트필드의 배수여야 함.

- 비트필드 : [5-1]

- E 필드 : [0]. 영역을 enable/disable

=> 영역은 정의 된 후 E비트가 0으로 disable되어 있다가, 1로 설정되면 enable 됨

- 사용하지 않는 비트들은 0으로 설정되어야 함

- 영역의 크기는 "size = 2^(N+1)" 또는 영역크기 인코딩 값(표 13.4)을 참고        
????, N(크기)의 2배수 값이 base address가 되야한 다는 게 이해가 잘..안됨
- N : 크기 설정을 위해 지수 값 설정.(4KB~4GB를 의미하는 11~31의 정수값)


- ARM740T, ARM946E-S. ARM1026EJ-S는 8개의 영역을 갖고,

 CP15:c6:cX 안의 보조 레지스터에 값을 설정하여 한 영역의 크기와 위치를 결정


ex. 영역 3을 시작위치 : 0x300000, 크기 : 256KB 로 설정하기


MOV    r1, #0x300000           ;시작 위치 : 0x300000

ORR     r1, r1, #0x11<<1        ;크기 : 256KB

MCR    p15, 0, r1, r6, c3, 0    ; CP15 보조 레지스터 값 쓰기



- ARM940T는 8개의 데이터영역과 8개의 명령어 영역을 각각 가짐

명령어 영역과 데이터 영역을 선택하기 위해 추가로 "opcode2"를 가짐


ex. 데이터 영역 5, 명령어 영역 5의 크기와 위치 읽기(2개의 MRC명령어가 필요함)


MCR    p15, 0, r2, c6, c5, 0    ; r2 = base address / size, 데이터 영역 5   => r2에 데이터 영역 5의 크기와 시작주소 로드

MCR    p15, 0, r3, c6, c5, 1    ; r3 = base address / size, 명령어 영역 5   => r3에 명령어 영역 5의 크기와 시작주소 로드


> Example 13.1 <

: 영역의 시작주소, 크기, E bit를 설정하는 방법


* C 프로토 타입

void regionSet(unsigned region, unsigned address, unsigned sizeN, unsigned enable);

enable : 속성 변경 중에는 비활성화, 변경이 완료되면 활성화


- 명령어 영역과 데이터 영역을 위해 동일한 크기와 시작주소 정보를 설정하여 ARM940T 영역을 하나로 통합

=> ARM940T용과 다른 코어용의 두 부분으로 분리하여 매크로를 작성

=> SET_REGION 매크로 : 동일한 루틴으로 4개의 MPU 코어를 지원,

정의된 영역을 위해 CP15:c6 보조 레지스터에 값을 설정하여 시작주소, 크기, 활성상태를 설정 


- address, sizeN, enable의 영역 속성을 c6f라는 unsigned 정수에 통합

- SET_REGION매크로로 만들어진 8개의 regionSet 루틴 중 하나로 분기


# 13.2.2. Access Permission

- 접근권한 방식의 2가지 : 표준 설정 방식, 확장 설정 방식


> CP15 레지스터 6 접근권한 


ARM740T, ARM940T, ARM946E-S, ARM1026EJ-S : 표준 설정 방식 지원

- 최신의 ARM946E-S, ARM1026EJ-S : 확장 설정 방식도 지원하며 2가지 레벨의 권한도 지원

- 확장설정 비트 필드 AP : 12개의 추가 권한 값을 지원하며, 비트 중 2개만 현재 할당되어 사용

- 정의되지 않은 인코드 값의 사용은 예기치 못한 동작을 발생


> 레지스터에 접근권한 부여

: CP15:c5 안의 보조 레지스터에 값을 설정


- 표준 AP 설정 : CP15:c5:c0:0, CP15:c5:c0:1

- 확장 AP 설정 : CP15:c5:c0:2 or CP15:c5:c0:3


> AP 레지스터를 위한 레지스터의 권한 비트 할당과 CP15 레지스터 5 접근권한 레지스터 포맷


- 확장 권한을 지원하는 프로세서는 표준 권한을 위해 작성된 소프트웨어에서 실행이 가능

- AP 레지스터 마지막으로 쓰여진 것이 표준 AP 레지스터라면 코어는 확장 권한을 사용 ???????????????

=> 표준 AP 레지스터에 쓰이는 것은 확장 AP 영역 엔트리의 [2:3]비트가 0을 클리어 함을 의미. 

확장 AP 레지스터에도 업데이트 되기 때문 ??


# 표준 AP

- 레지스터 CP15:c5:c0:0, CP15:c5:c0:1 안의 두 비트를 사용


CP15:c5:c0:0 : 데이터를 위한 AP를 설정

CP15:c5:c0:1 : 명령어영역을 설정


- 명령어와 데이터메모리를 위한 표준 AP를 읽기 위해, 2개의 레지스터를 읽어야 하므로 두개의 MRC 명령어를 사용


MRC    p15, 0, r1, c5, c0, 0        ; 데이터 영역 표준 AP. r1에 데이터 영역 메모리를 위한 AP 정보를 저장

MRC    p15, 0, r2, c5, c0, 1        ; 명령어 영역 표준 AP. r2에 명령어 영역 메모리를 위한 AP 정보를 저장


# 확장 AP

레지스터 CP15:c5:c0:2, CP15:c5:c0:3 안의 4개의 비트 사용


하나의 레지스터 : 8개의 영역을 위한 명령어 정보를 저장

또 하나의 레지스터 : 데이터 정보를 저장


CP15:c5:c0:2 : 데이터 영역을 위한 AP 설정

CP15:c5:c0:3 : 명령어 영역을 위한 AP 설정


- 데이터 영역 및 명령어 영역 확장 AP를 얻기위해,  2개의 레지스터를 읽어야 하므로 두개의 MRC 명령어를 사용


MRC    p15, 0, r3, c5, c0, 2       ; 데이터 영역 표준 AP. r3에 데이터 영역 AP 저장

MRC    p15, 0, r4, c5, c0, 3       ; 명령어 영역 표준 AP. r4에 명령어 영역 AP 저장


> Example 13.2 & 13.3 <

: 접근 권한을 사용하는 예


표준 AP 비트를 설정하기 위한 2가지 표준 AP 루틴


void regionSetISAP(unsigned region, unsigned ap);

void regionSetDSAP(unsigned region, unsigned ap);


region : 영역 번호

ap : 영역에 의해 제어되는 명령어나 데이터 메모리를 위한 표준 AP


> Example 13.2 <

: 접근 권한을 사용하는 예(표준 AP)


void regionSetISAP(unsigned region, unsigned ap)

{

unsigned c5f, shift;


shift = 2 * region;                                    /*  영역의 큭를 권한 비트필드 안에 저장되어있는 비트 수의 배수로 정의  */

__asm{ MRC    p15, 0, c5f, c5, c0, 1 }    /*  표준 I AP 로드  */

c5f = c5f &~ (0x3 << shift);                    /*  이전 AP 비트 클리어  */

c5f = c5f | (ap << shift);                          /*  새로운 AP 비트 설정  */

__asm{ MCR   p15, 0, c5f, c5, c0, 1 }    /*  표준 I AP 저장  */

}


void regionSetISAP(unsigned region, unsigned ap)

{

unsigned c5f, shift;


shift = 2 * region;                                    /*  영역의 크기를 권한 비트필드 안에 저장되어있는 비트 수의 배수로 정의  */

__asm{ MRC    p15, 0, c5f, c5, c0, 0 }    /*  표준 D AP 로드  */

c5f = c5f &~ (0x3 << shift);                    /*  이전 AP 비트 클리어  */

c5f = c5f | (ap << shift);                          /*  새로운 AP 비트 설정  */

__asm{ MCR   p15, 0, c5f, c5, c0, 0 }    /*  표준 DI AP 저장  */

}


> Example 13.3 <

: 접근 권한을 사용하는 예(확장 AP)


void regionSetIEAP(unsigned region, unsigned ap);

void regionSetDEAP(unsigned region, unsigned ap);


region : 영역 번호

ap : 영역에 의해 제어되는 명령어나 데이터 메모리를 위한 표준 AP


void regionSetIEAP(unsigned region, unsigned ap)

{

unsigned c5f, shift;


shift = 4 * region;                                    /*  영역의 크기를 권한 비트필드 안에 저장되어있는 비트 수의 배수로 정의  */

__asm{ MRC    p15, 0, c5f, c5, c0, 3 }    /*  확장 D AP 로드  */

c5f = c5f &~ (0x3 << shift);                    /*  이전 AP 비트 클리어  */

c5f = c5f | (ap << shift);                          /*  새로운 AP 비트 설정  */

__asm{ MCR   p15, 0, c5f, c5, c0, 3 }    /*  확장 D AP 저장  */

}


void regionSetIEAP(unsigned region, unsigned ap)

{

unsigned c5f, shift;


shift = 4 * region;                                    /*  영역의 큭를 권한 비트필드 안에 저장되어있는 비트 수의 배수로 정의  */

__asm{ MRC    p15, 0, c5f, c5, c0, 2 }    /*  확장 I AP 로드  */

c5f = c5f &~ (0x3 << shift);                    /*  이전 AP 비트 클리어  */

c5f = c5f | (ap << shift);                          /*  새로운 AP 비트 설정  */

__asm{ MCR   p15, 0, c5f, c5, c0, 2 }    /*  확장 I AP 저장  */

}



## 13.2.3. Setting Region Cache and Write Buffer Attributes


- CP15:c2:c0:0, CP15:c2:c0:1 : D캐시와 I캐시의 영역 속성값을 저장

- CP15:c3:c0:0 : 영역 쓰기 버퍼 속성을 저장, 메모리 데이터 영역에 제공


> CP15:c2 캐시와 CP15:c3 write buffer 영역 레지스터


> CP15:c2 및 CP15:c3 레지스터를 위한 비트필드 할당


CP15:c2:c0:1 : 8개의 명령어 영역을 위한 캐시 설정 데이터를 포함

CP15:c2:c0:0 : 8개의 데이터 영역을 포함

=> 둘 다  동일한 비트필드 인코딩 사용


> cache-bit : 캐시가 영역 내에 주어진 주소를 위해 활성화할지의 여부를 결정

-  ARM740T, ARM940T는 cache-bit에 상관없이 항상 캐시가 사용되므로

캐시의 정책이 cached->noncached로 변경되는 영역에서는 캐시를 flush(무조건), clean(가능하면) 해주는 것이 좋음

=> MPU 시스템은 캐시 정책이 write-through 방식에서 noncached 방식으로 변경될 때 항상 캐시를 flush 해야 함

=> write-back 방식에서는 캐시 flush와 clean을 모두 해야 함

- ARM946E-S 에서 cache-bit이 0이면 캐시안에 물리적 정보는 캐시로 복원되지 않고 외부 메모리 액세스가 수행됨

=> 캐시가 비활성화 되어 있을 때는 캐시를 flush하는데 필요한 요구사항을 줄여주지만, 오래된 영역에 대한 clean은 계속 적용 됨


CP15:c3:c0:0 : 8개의 영역 write buffer는 각 영역에 대한 write buffer의 활성화/비활성화를 조작


> 데이터 영역 설정 시 영역의 정책 결정

: write buffer bit, cache bit

 write buffer bit

 - 영역을 쓰기 위한 write buffer 활성화/비활성화 설정

 - 영역의 cache write policy를 설정

 cache bit

 - write buffer bit의 용도를 설정


 cache bit

  write buffer bit

 

 0

 0

 write buffer 비활성화 

 0

 1

 write buffer 활성화 

 1   

 0  

 write-through policy 적용

 1 1

 write-back policy 적용

(* cache와 write buffer 모두 활성화되고 write buffer bit은 캐시의 쓰기 정책을 판단)



> Example 13.4 <

: cache와 write buffer 활성화/비활성화 방법


- 인라인 어셈블러 사용

- cache와 write-buffer의 제어를 하나의 루틴 호출로 통합

- 캐시 쓰기 정책을 결정하기 위해서는 데이터 cache와 write buffer bit들을 모두 참조

- 명령어 cache bit은 단독 사용

* cache와 write-buffer의 상태를 각 영역을 위한 하나의 값으로 통합하면 영역 정보를 RCB로 모으는 것을 보다 쉽게 처리

(RCB : Region Control Block, 13.3.3절)


void regionSetCB(unsigned region, unsigned CB);

(region : 영역의 번호, CB : cache 속성 + write buffer 속성)



## 13.2.4. Enabling Regions and the MPU


# 초기화 과정

- 영역의 시작주소, 크기, E bit를 설정

- 접근권한 설정

- cache와 write buffer 활성화/비활성화

- 활성 영역을 enable : regionSet 루틴을 이용

- MPU, caches, write buffer를 enable : CP15:c1:c0:0안의 비트값을 수정

(CP15:c1:c0 안에 있는 MPU, caches, write buffer의 위치는 ARM940T, ARM946E-S, ARM1026EJ-S과 동일)


> Enable-bit의 위치


> CP15 컨트롤 레지스터 1에서의 보호 장치 활성 비트

> Example 13.5 < 

: MPU, caches를 enable하기 위해 "changeControl" 루틴을 사용

(but, changeControl 루틴은 CP15:c1:c0:0 레지스터 안에 있는 값들을 변경할 수 있음)???? 


void controlSet(unsigned value, unsigend mask);

(value : 변경할 비트를 포함하는 unsigned 정수 값, 

mask : 변경하기를 원하는 비트들을 선택하는데 사용. 

"value"와는 상관없이 1은 컨트롤 레지스터 안에 있는 비트를 변경, 0은 비트값이 변경되지 않고 그래도 유지되도록 함)


ex. MPU와 I캐시를 enabling, D캐시는 disabling        ????

=> [12]bit : 1,    [2]bit : 0,    [0]bit : 1

value : 0x00001001

mask : 0x00001005




### 13.3. Demonstration of an MPU System

: 고정된 메모리 맵을 사용하여 간단한 보호 시스템을 초기화하고 제어하기

(간단한 보호 멀티태스킹 시스템 안에서 동작하는 3개의 태스크를 enable)



## 13.3.1. 시스템 요구사항

- MPU를 가지는 ARM 코어

- 0x0에서 시작하고 0x40000으로 끝나는 256KB의 물리 메모리

- 0x10000000 ~ 0x12000000의 몇 MB 이상을 차지하는 메모리 매핑 된 몇가지 주변장치


* 모든 메모리 매핑된 주변장치는 보호를 필요로하는 하나의 메모리 영역으로 가정


- 시스템 소프트웨어는 크기가 64KB 이내

exception을 지원하기 위한 vector table, exception handler, data cache를 포함

- 시스템 소프트웨어는 user mode로 액세스 할 수 없음. system level의 권한을 갖음

=> user mode는 이 영역 안에 있는 코드나 데이터를 액세스 하기 위해 시스템 콜을 사용해야 함

- 크기가 64KB 이내인 공유 소프트웨어가 존재. 전체 시스템에 의해 액세스가 가능

user task 사이에 메시지를 보내기 위해 공통으로 사용되는 라이브러리와 데이터공간을 포함

- 시스템에서 독립적인 기능을 제어하는 3개의 user task가 존재. 

32KB 이내의 크기를 가짐. 실행될때는 각각 다른 2개의 task에 의한 액세스로부터 보호되어야 함

- 소프트웨어는 할당되어 있는 영역 내에서 소프트웨어 컴포넌트를 놓아두도록 연결


> 소프트웨어 메모리 맵


* 마지막 열에보면 영역은 4개임을 알 수 있음


> 영역 레이아웃을 나타내는 메모리 맵



## 13.3.2. 메모리맵을 사용하여 영역 할당하기


- Region 1 : 전체 메모리 공간을 사용할 수 있는 background 영역. 특권영역(어떤 user mode access라도 불가능)

명령어 캐시는 enabled, 데이터캐시는 write-back 방식으로 동작(??? why?)

가장 낮은 우선순위를 가짐

<기능> 

1. 0x0 ~ 0x10000사이의 64KB 공간을 제한적으로 액세스 하도록 함

2. 배경역역으로 동작하여 non-active한 user task를 위한 보호영역으로 동작

3. 배경 영역으로서는 디폴트로 전체 메모리 공간이 시스템 레벨의 액세스로 할당 되게 함

=> active한 user task에 인해 다른 user task가 액세스 되는 것을 보호


- Region 2 : 공유 시스템 자원을 액세스 하는 것을 제어

0x10000을 시작주소로 64KB의 크기를 가짐

공유 메모리 영역에 직접 매핑

보호된 영역 1의 일부분의 꼭대기에 위치하며 영역1보다 높은 우선순위를 가짐

user system 레빌의 메모리 액세스를 허용


- Region 3 : active한 task의 메모리 영역과 속성을 제어

context switching이 발생하는 동안 처럼,
     한 task에서 다른 task로 제어권이 넘어가면 OS는 영역 3이 실행중인 task의 메모리 영역과 overlap되도록 재정의를 수행함

새로운 task에 대한 위치가 다시 정해질 때, 이전 task는 영역 1의 일부가 되고 실행하는 task는 새롭게 영역 3이 됨

실행중인 task는 이전 task에 액세스 할 수 없음. (영역 1의 속성으로 보호되기 때문)


- Region 4 : 메모리 매핑된 주변장치의 공간

목적 : 해당 영역을 캐시와 버퍼를 사용하지 않는 영역으로 설정하는 것

이유? 캐시에 의해 발생되는 잘못된 데이터 문제와 write buffer를 사용할 대 포함 될 수 있는 시간이나 순서적 문제 때문에 

입력이나 출력 컨트롤 레지스터가 영향을 받지 않게 하기 위함



## 13.3.3. MPU의 초기화


# Region data type : 초기화 과정을 조직화 하기위해 만들어진 구조체

시스템 동작중에 사용되는 영역의 속성값들을 포함

MPU를 사용할 때에는 필요하지 않고 단지 예시 소프트웨어를 지원하기 위해 만들어낸, 설계상 편의를 목적으로 함

RCB(Region Control Block)


- 초기화 소프트웨어는 MPU안에 영역을 설정하기 위해 RCB 안에 저장된 정보를 이용

- RCB안에 정의되어 있는 Region 구조체는 물리적 영역보다 많이 있을 수 있음

ex. 영역 3은 task를 위해서만 사용되지만, 각 user task를 위해 영역 3을 사용하는 Region 구조체에는 3개가 있어야 함


typedef struct {

unsigned int number;            // Region에 할당되어 있는 MPU 영역 번호

unsigned int type;                  // 사용되는 접근 권한의 유형 (STANDARD, EXTENDED)

unsigned int baseaddress;     // 영역의 시작주소

unsigned int size;                   // 영역의 크기

unsigned int IAP;                    // 접근권한

unsigned int DAP;                  // 접근권한

unsigned int CB;                     // cache, write buffer 설정

} region;


- RCB 안에 저장되어있는 6개의 Region


> RCB 안에 있는 엔트리들에 대해 만든 매크로


- 4개의 문자 조합을 이용해 데이터와 명령어 메모리로의 접근권한을 입력

처음 2개 : system access permission, 나머지 2개 : user access permission

(RW / RO/ NA(no access))


- cache와 write buffer의 정보 : 명령어 캐시와 데이터 캐시 정책을 위한 속성으로 매핑

처음 문자 : C or c. 영역을 위한 명령어 캐시 활성화/비활성화

다음 2개 문자 : 데이터캐시 정책과 write buffer의 컨트롤 결정. 

WT : write-through, WB : write-back, 

Cb : =WT, CB : =WB, cB : 캐시를 사용하지 않고 버퍼만 사용, cb : 캐시와 버퍼 모두 사용하지 않음



## 13.3.4. 영역 설정 및 초기화


- "configRegion" 루틴(초기화 다음 단계에 위치)  

: 영역을 설명하는 데이터를 가지고 CP15레지스터를 조작하기 위해 RCB에서 하나의 Region 구조체 엔트리를 얻어 옴


void configRegion(region *region);

(*region : 영역의 RCB를 가리키는 포인터 값으로 루틴내에서 Region의 멤버들을 초기화 하는 과정에서 데이터 입력값으로 사용)


> MPU, cache, write buffer 초기화


void configRegion(Region *region) 

 /* Step 1 - Define the size and location of the instruction */

 /* and data regions using CP15:c6 */

regionSet(region->number, region->baseaddress, region->size, R_DISABLE);


/* Step 2 - Set access permission for each region using CP15:c5 */

if (region->type == STANDARD) { 

regionSetISAP(region->number, region->IAP); 

regionSetDSAP(region->number, region->DAP); 

else if (region->type == EXTENDED) 

{

regionSetIEAP(region->number, region->IAP); 

regionSetDEAP(region->number, region->DAP); 


/* Step 3 - Set the cache and write buffer attributes */ 

/* for each region using CP15:c2 for cache */ 

/* and CP15:c3 for the write buffer. */ 

regionSetCB(region->number, region->CB); 


/* Step 4 - Enable the caches, write buffer and the MPU */ 

/* using CP15:c6 and CP15:c1 */ 

regionSet(region->number, region->baseaddress, region->size, region->enable); 

}



## 13.3.5. MPU 초기화


- 모든 영역을 설명하는 데이터를 저장하기 위해 RCB를 사용

- MPU를 초기화 하기위해 "initActiveRegions"라는 상위레벨의 루틴을 사용

: 시스템이 시작될 때 활성화된 각각의 영역을 위해 한번씩만 호출

- MPU를 enable 시킴으로써 마무리


void initActiveRegions();        // 입력 파라미터 없음


- 시스템이 시작할 때 활성화 되어있는 영역인 

kernelRegion, shareRegion, peripheralRegion, task1Region을 위해 configRegion을 한번 씩 호출


#define ENABLEMPU (0x1) 

#define ENABLEDCACHE (0x1 << 2) 

#define ENABLEICACHE (0x1 << 12) 

#define MASKMPU (0x1) 

#define MASKDCACHE (0x1 << 2) 

#define MASKICACHE (0x1 << 12) 


void initActiveRegions() 

unsigned value,mask; 

configRegion(&kernelRegion); 

configRegion(&sharedRegion); 

configRegion(&peripheralRegion);

 configRegion(&task1Region); 


value = ENABLEMPU | ENABLEDCACHE | ENABLEICACHE; 

mask = MASKMPU | MASKDCACHE | MASKICACHE; 

controlSet(value, mask); 

}


=> 여기까지 시스템 초기화 완료!!!, 제어 시스템은 첫번재 태스크를 실행!!



## 13.3.6. 보호된 문맥전환

- 제어 시스템은 시스템 초기화가 완료 된 후 첫번재 태스트를 실행하며, 다른 태스크를 실행하기 위해 context switching을 수행

- RCB는 현재 task의 영역 문맥 정보를 포함하므로 CP15 레지스터로부터 영역 데이터를 저장할 필요가 없음


- (그림 13.7)에서 task 2로 변환하기 위해 OS는 region 3을 task 2의 메모리 영역으로 변경

- 현재 task와 다음 task 사이의 context switching을 수행하는 코드를 실행하기 전, 

초기화의 일부로 이 함수를 수행하기 위해 configRegion 루틴을 다시 사용 

- configRegion으로의 입력 값 : taask2Region


STMFD sp!, {r0-r3,r12,lr} 

BL configRegion 

LDMFD sp!, {r0-r3,r12,pc}     ; return


- C 함수


configRegion(&task2Region);



## 13.3.7. mpuSLOS

: SLOS(11장) 메모리 보호 장치를 추가한 제어시스템


> 기존 SLOS와의 차이점

mpuSLOS는 MPU를 완전히 사용

- applications는 커널과 분리되어 컴파일 디고 빌드된 다음 하나의 바이너리 파일에 통합

각 application은 다른 메모리 영역에서 실행되도록 링크 됨

- 3개의 applications은 각각 Static Application Loader 라는 루틴에 의해 32KB 크기의 분리된 고정 영역으로 로딩

이 주소는 Application의 시작 주소임

각 영역은 32KB의 크기를 가지므로 스택 포인터는 32KB의 상위값으로 설정

- applications는 device driver call에의해서만 하드웨어를 액세스

만약 application이 직접 하드웨어를 액세스 하려고 한다면 data abort가 발생 

(SLOS와 다른점. device가 application으로부터 직접 액세스 될 때는 data abort가 발생하지 않음)

- applications으로 분기하는 것은 spsr을 설정한 다음, 

MOVS 명령어를 사용하여 pc가 task1을 가리키는 엔트리포인터의 값으로 변경하는 작업을 포함

- 스케줄러가 호출될 경우에는 언제나 활성화된 영역2는 새로 실행되는 application을 반영하도록 변경 됨




### 13.4. Summary


# 메모리 protection

1.  unprotected : 태스크 상호 작용을 위한 규칙들을 관리하기 위해 소프트웨어적인 제어 루틴을 사용

2. protected : 태스크 상호작용을 관리하기 위해 하드웨어와 소프트웨어를 둘 다 사용.

=> 보호 시스템에서 접근권한이 침범을 받은 경우 하드웨어적으로 abort를 발생하여 메모리 영역을 보호함

소프트웨어는 발생한 abort 루틴을 처리하고 메모리 기반의 자원을 제어하도록 반응


# Region : ARM MPU가 시스템 보호를 위한 기본적으로 사용하는 struct

메모리 영역과 관련되어 있는 속성값들의 모임을 말함

# Backgroud Region : 영역은 overlap 될 수 있으므로,

현재 실행중인 태스크에 의한 원치않은 액세스로 부터 non-active한 상태의 task의 메모리를 보호하기위해 사용


# MPU를 초기화하는 단계

1. CP15:c5을 사용하여 명령어 및 데이터 영역의 크기와 위치를 설정

2. CP15:c5를 사용하여 각 영역에 대한 접근 권한을 설정

3. 각 영역을 위한 cache와 write buffer 속성을 지정

CP15:c2 : 캐시 설정

CP15:c3 : write buffer 설정

4. CP15:c6를 사용하여 활성화 영역을 Enabled

5. CP15:c1을 사용하여 cache, write buffer, MPU를 enabled


# 간단한 멀티태스킹 환경에서의 보호에 대한 예제

1. 보호 시스템 정의

2. 초기화

3. 초기화 후 보호시스템을 실행하는 데 필요한 태스크 변환 및 태스크로 영역할당 받기


# mpuSLOS






'System > Embedded' 카테고리의 다른 글

[ARM] 캐시 2  (0) 2015.11.12
[ARM] 캐시  (0) 2015.11.12
[ARM] 펌웨어  (0) 2015.11.12
[ARM] 익셉션과 인터럽트 처리 2  (0) 2015.11.10
[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10

- Open Firmware

- FDT


- FPGA 참고



The Flattened Device Tree is...

The Flattened Device Tree (FDT) is a data structure. Nothing more.

It describes a machine hardware configuration. It is derived from the device tree format used by Open Firmware. The format is expressive and able to describe most board design aspects including:

  • the number and type of CPUs,
  • base addresses and size of RAM,
  • busses and bridges,
  • peripheral device connections, and
  • interrupt controllers and IRQ line connections.

Just like initrd images, an FDT image can either be statically linked into the kernel or passed to the kernel at boot time.


(출처. http://elinux.org/Device_Tree)


참고. http://blog.dasomoli.org/491


'System > Etc.' 카테고리의 다른 글

git  (0) 2016.01.17
[OS 기초] File System 2 - Disk Scheduling  (0) 2015.11.06
[OS 기초] File System  (0) 2015.11.05
[OS 기초] I/O Device와 Device Driver  (0) 2015.11.05
[OS 기초] Demand paging 3 - Trashing, Working Set  (0) 2015.11.04

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

The Memory Hierarchy and Cache Memory

Cache Architeture

Cache Policy

Coprocessor 15 and Caches

Flushing and Cleaning Cache Memory

Cache LockDown

Caches and Software Performance




### 12.4. Coprocessor 15 and Caches


# CP15 : 캐시를 가지는 ARM 코어를 설정하고 제어하기 위해 사용



- c7, c9 : cache setup 및 cache 동작을 제어

- 보조 CP15:c7 : write only, 캐시 clean, flush에 사용

- CP15:c9 : victim pointer base address를 정의. 캐시 안에서 lock 되어있는 코드 또는 데이터 라인수를 결정




### 12.5. Flushing and Cleaning Cache Memory


# Flushing ARM Cached Cores
: 캐시의 내용을 없앤다는 의미

- 저장되어있는 어떤 데이터를 0으로 클리어 하는 일
- 유효한 캐시 라인 안에 있는 유효비트를 0으로 클리어
- 메모리를 설정할 때 변경된 것을 지원하는 경우, 캐시의 모든 영역이나 일부 영역을 flush할 필요가 있음
- "invalidate"라고도 함
=> 캐시메모리와 메인메모리가 inconsistency하면  캐시의 내용을 모두 0으로 설정(reset?)
D캐시, I캐시를 선택적으로 flush할 수 있음

# Cleaning ARM Cached Cores
: 메인메모리의 D캐시 안에 있는 데이터를 복사한다는 의미

D 캐시의 일부가 writeback 방식을 사용하도록 설정된 경우 필요
write-through의 경우는 캐시와 메인메모리 모두 update하지만, write-back의 경우는 data를 캐시에만 update.
=> 속도는 더욱 빠르지만 inconsistency 할 수 있음
- 캐시로부터의 dirty cache line의 값을 강제로 메인메모리에 쓴 다음, 캐시라인의 dirty bit을 0으로 초기화
* dirty는 캐시에만 update되고 memory에는 update가 되지 않아 dirty 해졌다는 의미
- 캐시된 메모리와 메인메모리 사이의 일관성을 재정립 후, writeback을 사용해 D 캐시에 적용
=> 캐시의 내용을 메모리에도 update함 
(write-back 방식에서 dirty bit이 1이면  inconsistency를 의심하고, 캐시컨트롤러가 dirty cache line을 지우면 메인메모리에도 해당 데이터가 저장됨)

=> flush나 clean 동작은 메인메모리와 캐시메모리 사이의 데이터를 일치 시켜주기 위해 사용
- 시스템의 메모리 설정을 변경하면 캐시를 clean하거나 flush해야 함
   (접근권한, 캐시, 버퍼정책 변경, 가상주소 리매핑 등)

- 분할캐시안에서 self-modifying code를 실행하기 전에도 cleaning이나 flushing을 수행해야함. 
self-modifying code가 한 위치에서 다른 위치로 코드의 복사본을 포함하기 때문?? (캐시메모리에는 새로운 데이터가 업데이트 되지 않기때문?)
                      * self-modifying code : 기계어 레벨에서 실시간으로 자신 스스로의 코드를 변경할 수 있는 코드
프로세스 OS에서 제공하는 API를 사용하여 메모리상에서 
자신의 코드영역을 write할 수 있는 권한을 얻은 후, 메모리 포인터를 얻어와 코드를 수정할 수 있음
- 바이러스, 실행파일 자체를 압축한 후 실행 시 스스로 압축해제하는 프로그램 등..
(출처. https://kldp.org/node/124235)

> 2가지 상황으로 구분
1. self-modifying code가 D캐시 안에 저장되어 명령어처럼 메인메모리로부터 로드하는 것이 불가능한 경우
2.I캐시 안에 있는 기존 명령어들로 인해 메인메모리에 쓰여진 새로운 명령어가 수행되지 않는 경우

> write-back정책을 사용하며 self-modifying code가 메인메모리에 쓰여있다면,
1. 명령어를 한 블록의 데이터처럼 메인메모리의 어떤 위치에 저장
2. 다음번에 프로그램이 해당 메모리로 분기 후 해당 영역부터 명령어 스트림처럼 실행
3. 코드의 처음 부분을 데이터처럼 메인메모리에 쓰는 동안 대신 캐시메모리에 쓰일 수 있음
4. 유효캐시라인이, self-modifying code가 쓰여진 위치를 나태내는 캐시메모리에 존재한다면 ARM 캐시 안에서 발생함
5. 캐시라인은 메인메모리가 아닌 D캐시에 복사되어 있음
6. 프로그램이 self-modifying code로 분기할 때 저장되어있던 이전의 명령어를 실행할 것임(self-modifying code는 D캐시에 존재하므로)

=> 명령어가 메인메모리에 데이터러첨 저장되도록 하여 명령어 스트림처럼 읽도록 해주기위해 cleaning을 수행해야 함

- D캐시가 cleaning 되면 새로운 명령어는 메인메모리에 위치
- I캐시는 새로운 데이터(코드)가 쓰여있는 주소에 저장된 유효한 캐시라인을 가질 수 있음

> 복사된 코드의 주소에서 명령어를 fetch하면, 메인메모리의 새 코드가 아닌 I캐시에서 오래된 코드를 가져오게 됨
=> I 캐시를 flushing 해야 함


## 12.5.1. Flushing ARM Cached Cores

- write-back 방식을 사용한다면, flush를 하기 전 clean을 먼저 수행하여 flush 처리 결과 데이터가 손실되지 않도록 해야 함

# Cache Flush 명령어 (CP15:c7)
1. 캐시 전체를 flush
2. I 캐시만 flush
3. D 캐시만 flush

=> 프로세서 코어 레지스터 Rd는 3개의 MCR 명령어에 대해 0의 값을 가짐???


- flushICache    : I 캐시를 플러시
- flushDCache   : D 캐시를 플러시
- flushCache    : I와 D 캐시 둘다 플러시
=> 위의 루틴들은 입력 파라미터를 갖지 않고, 다음과 같은 prototype으로 C로부터 호출

void flushCache(void);
void flushDCache(void);
void flushICache(void);

 
> Example 12.2 < 
- 위의 명령어를 사용한 캐시 플러시 방법 설명
- 수정없이 사용하거나 시스템 요구 사항에 맞게 수정하여 사용이 가능
- 3가지 루틴을 만들어내는 매크로를 포함(부록A)


- 코어가 지원하는 명령어를 기반으로 그룹을 나누기

- 루틴을 만드는 작업을 돕기위해 "CACHEFLUSH" 매크로를 사용
- CP15:c7:Cm에 사용되는 코어 레지스터를 0으로 설정

RN    0                            ; CP13:c7에서 사용되는 레지스터
CACHEFLUSH    $op
- 필요한 캐시 동작의 유형과 코어의 지원 여부에 따라 특정 MCR 명령어를 삽입

IF    "$op" = "Icache"
MCR    p15,    0,    c7f,    c5,    0                ; I캐시 플러시
ENDIF
IF    "$op" = "Dcache"
MCR    p15,    0,    c7f,    c6,    0                ; D캐시 플러시
ENDIF
IF    "$op" = "IDcache"
IF    {CPU} = "ARM940T"    :LOR:    \
{CPU} = "ARM946E-S"
MCR    p15,    0,    c7f,    c5,    0        ; I캐시 플러시
MCR    p15,    0,    c7f,    c6,    0        ; D캐시 플러시

ELSE   
MCR    p15,    0,    c7f,    c7,    0        ; I&D 캐시 플러시
ENDIF
ENDIF

- 마지막에는 루틴을 만들기 위해 해당 매크로를 여러번 사용

IF    {CPU} = "ARM720T"                
EXPORT    flushCache
flushCache
CACHEFLUSH    IDcache
ELSE
EXPORT    flushCache
EXPORT    flushICache
EXPORT    flushDCache
flushCache
CACHEFLUSH    IDcache
flushCache
CACHEFLUSH    IDcache
flushCache
CACHEFLUSH    IDcache
ENDIF

ARM720T는 통합(unified) 캐시를 가지므로 flushCache 루틴만을 사용할 수 있음

> 좀더 간단한 루틴의 구현(ARM926EJ-S)

EXPORT    flushCache926
EXPORT    flushICache926
EXPORT    flushDCache926
c7f    RN    0        ; CP13:c7에서 사용되는 레지스터

flushCache926
MCR    p15,    0,    c7f,    c7,    c7,    0        ; 코프로세서15, c7f레지스터에게 c7 코프로세서레지스터, ...
MOV    pc, lr
flushCache926
MCR    p15,    0,    c7f,    c7,    c5,    0
MOV    pc, lr
flushCache926
MCR    p15,    0,    c7f,    c7,    c6,    0
MOV    pc, lr


> C로 인라인 함수로 작성하여 하나로 include하면 더욱 간결해 질 수 있음


__inline void flushCache926(void)

{

unsigned int c7format = 0;

__asm{MCR p15, 0, c7format, c7, c7, 0};

}


__inline void flushDCache926(void)

{

unsigned int c7format = 0;

__asm{MCR p15, 0, c7format, c7, c6, 0};

}


__inline void flushICache926(void)

{

unsigned int c7format = 0;

__asm{MCR p15, 0, c7format, c7, c5, 0};

}



## 12.5.2. Cleaning ARM Cached Cores

- 캐시 클린? 

캐시 컨트롤러에게 모든 D캐시라인을 메인 메모리에 쓰도록 명령하는 것


- 캐시라인의 dirty bit status bit들을 0으로 클리어

- write back 방식의 캐시에서만 적용이 가능

- 때때로 clean이라는용어대신 writeback이나 copyback 이라는 용어가 사용되기도 함

=> 캐시에 write back 방식을 적용한다는 의미는 캐시 clean이라는 의미와 동일 


* ARM이 아닌 곳에서는 flush라는 용어가 ARM에서 사용하는 clean이라는 의미로 사용되기도 함.



## 12.5.3. Cleaning the D-Cache

- 프로세서마다 D캐시를 clean하는 명령어가 다르므로 D 캐시를 clean하는 방법은 3가지로 나눌 수 있음


> D 캐시 clean 방법


- cleanDCache : 모든 D캐시를 clean

- cleanFlushDCache : 모든 D캐시를 clean하고 flush

- cleanFlushcache : I캐시와 D캐시를 모두 clean하고 flush


=> 위의 루틴들은 입력 파라미터를 갖지 않고, 다음과 같은 prototype으로 C로부터 호출

void cleanDCache(void);
void cleanFlushDCache(void);
void cleanFlushcache(void);

> 헤더 : cache.h
: 많은 ARM코어를 지원할 수 있있도록 공통 헤더파일이 만들어짐


- 헤더파일의 모든 값들은 2의 로그로 표현된 크기 또는 field locator임

- 만약 값이 locator라면, CP15 레지스터 안에 있는 비트필드의 최하위 비트들을 나타냄    

ex. 상수 17WAY : CP15:c7:c5 레지스터 안에 way 선택 필드의 최하위 비트를 가리킴

- 17WAY 값은 clean 명령어가 MCR 명령어를 사용하여 호출될 때, CP15:Cd:Cm 레지스터 쪽으로 이동되는

코어 레지스터(Rm)의 비트 조작을 지원하기 위해 이러한 형식으로 저장 ????


> 코어 아키텍처에 따라 헤더파일에 저장되는 6개의 상수

 CSIZE 

 캐시 크기에 밑이 2인 로그를 취한 값. 바이트.

 (즉, 캐시 크기는 (1<<CSIZE) 바이트 임) 

 CLINE

 캐시라인의 길이에 밑이 2인 로그를 취한 값. 바이트.

 (즉, 캐시라인의 길이는 (1 << CLINE) 바이트 임) 

 NWAY

 ways의 수를 의미하며 세트 연상과 동일 

 17SET

 CP15:c7 명령 레지스터 안에서 세트 인덱스가 왼쪽으로 시프트될 비트 수를 의미

 순차적으로 캐시를 액세스할 때 CP14:c7 레지스터의 세트 인덱스 부분을 증가시키거나 감소시키기 위해 사용 

 17WAY

 CP15:c7 명령 레지스터 안에서 way 인덱스가 왼쪽으로 시프트 될 비트 수를 의미

 순차적으로 캐시를 액세스 할 때 CP15:c7 레지스터의 way 인덱스 부분을 증가시키거나 감소시키기 위해 사용 

 19WAY

 CP15:c9 명령 레지스터 안에서 way 인덱스가 왼쪽으로 시프트 될 비스 수를 의미

 순차적으로 캐시를 액세스 할 때 CP15:c9 레지스터의 way 인덱스 부분을 증가시키거나 감소시키기 위해 사용 


> 코어 특정 데이터에서 계산되는 2가지 상수

 SWAY

 way의 크기에 밑이 2인 로그를 취한 값. 바이트. 

 (way의 크기는 (1 << SWAY) 바이트 임)

 NSET

 way 당 캐시 라인의 수를 의미. 세트 인덱스의 크기에 밑이 2인 로그를 ㅇ취한 값

 (세트의 값은 (1 << NSET) ) 



## 12.5.4. Cleaning The D-Cache Using Way and Set Index

- WAY 및 세트 인덱스 주소를 사용해서 D-캐시 클린하기 


- ARM 코어에서는 캐시안에 해당 위치를 찾기 위해 way와 set index를 사용하여 하나의 캐시라인을 clean하고 flush를 수행 함


> way를 이용하여 캐시라인을 클린하고 플러시하는 데 사용하는 MRC명령어 

- 명령어 캐시라인 플러시

- 데이터 캐시라인 플러시

- 데이터 캐시라인 클린 

- 데이터 캐시라인 클린 및 플러시

 



- 코어는 해당 way와 set index address를 가지고 각 캐시라인을 선택

- 코어 레지스터 Rd에 저장되어 있는 값은 동일한 프로세서에 대하여 모두 동일하지만,

레지스터 안에 있은 비트필드의 형식은 프로세스마다 다름

- 명령어를 실행하기 위해서는 코어 레지스터 Rd 안에 저장되는 값을 적절한 CP15:c7 레지스터 형식에 맞게 생성

- 일반적으로 "way를 선택하는 영역 + way 안에 세트를 선택하는 영역"의 두 필드를 포함

- 레지스터가 생서되면 Rd를 CP15:c7 레지스터로 이동하기 위해 적절한 MCR(coprocessor -> register)를 사용


> Example 12.3 <

- way와 set index를 사용한 캐시 flush, clean, flush and clean을 수행

- CACHECLEANBYWAY 매크로 사용

- CP15:c7 레지스터(c7f) 안에 프로세서 레지스터를 빌드하기 위해 cache.h를 include하여 상수를 사용


- 레지스터 c7f를 0으로 설정

: 선택된 동작을 실행하기 위해 MCR 명령어 안에 있는 Rd 입력값으로 사용

- 매크로는 각각의 쓰여진 캐시 라인에 대해 형식에 따라 레지스터 c7f를 증가시킴

: 내부  loop문에 있는 set index와 외부 loop문에 있는 way index가 증가

- 중첩 loop를 이용해 모든 way에 있는 모든 캐시 라인들이 클릭 됨


AREA cleancachebyway    ,    CODE    ,    READONLY        ; 영역의 시작

 ...

EXPORT    cleanDCache

EXPORT    cleanFlushDCache

EXPORT    cleanFlushCache

INCLUDE    cache.h


c7f   RN    0    


MACRO

CACHECLEANBYWAY    $op


MOV    c7f,    #0        ; c7f 생성

5

IF    "$op" = "Dclean"

    MCR    p15,    0,    c7f,    c7,    c10,    2

ENDIF

IF    "$op" = "Dcleanflush"

    MCR    p15,    0,    c7f,    c7,    c14,    2

ENDIF


ADD       c7f, c7f, #1<<17SET            ; set index 1 증가

TST        c7f, #1<<(NSET+17SET)        ; index의 overflow 테스트

%BT5

BIC        c7f,    c7f,    #1<<(NSET+17SEt)    ; index의 overflow 클리어

ADDS    c7f,    c7f,    #1<<17WAY                ; victim 포인터 1 증가

BCC    %BT5                    ; way overflow 테스트

MEND


cleanDCache

CACHECLEANBYWAY    Dclean

MOV    pc,    lr

cleanFlushDCache

CACHECLEANBYWAY    Dcleanflush

MOV    pc,    lr

cleanFlushCache

CACHECLEANBYWAY    Dcleanflush

MCR    p15, 0, r0, c7, c5, 0        ; I캐시 플러시

MOV    pc,    lr

...



## 12.5.5. Cleaning The D-Cache Using The Test-Clean Command

- 테스트 클린 명령어를 사용하여 D 캐시 클린하기


- 최근의 ARM 코어, ARM926EJ-S, ARM1026EJ-S는 

   CP15:c7레지스터의 clean 여부를 테스트하는 방법을 사용하여 캐시를 clean하는 명령어를 지원

=> "test-clean", 소프트웨어 루프에서 사용할 때 효율적으로 캐시를 클린할 수 있는 특별한 클린 명령어

ARM926EJ-S, ARM1026EJ-S는 way 및 세트 주소지정방식을 사용하여 clean할 수 있지만 "test-clean"을 이용한 방법이 보다 효율적임


ARM926EJ-S, ARM1026EJ-S 코어를 클린하기 위한 명령어

> Example 12.4 <

: ARM926EJ-S, ARM1026EJ-S 프로세서를 위한  cleanDCache, cleanFlushDCache, cleanFlushCache 루틴


- 첫번째의 dirty cache line을 찾아 내용을 메인메모리로 전송하여 클린 작업을 수행

- 만약 dirty cache가 더 존재한다면, Z 플래그가 0으로 설정

- Z 플래그를 테스트하고, 반복을 위해 이전 부분으로 다시 분기하여 D 캐시가 clean 될 때까지 반복하여 수행


* test-clean 명령어는 PC(r15)를 MCR 명령어의 입력 레지스터 Rd로 사용



## 12.5.6. Cleaning The D-Cache in Intel XScale SA-110 and Intel StrongARM Cores

- 인텔 XScale SA110과 인텔 StrongARM 코어에서의 D캐시 클린하기


cleanFlushcache(I캐시와 D캐시를 모두 clean하고 flush) 방법을 사용

XScale에는 프로세서에는 라인을 채우지 않고 D 캐시에마는 라인을 할당하는 명령어가 있음

-> 해당 명령어를 수행하면 유효비트가 1로 설정되고 Rd 레지스터 안에 제공된 캐시태그를 가지고 디텍토리 엔트리를 채움

명령어가 수행될 때는 메인메모리에서 데이터가 전송되지 않음

=> 캐시안에 있는 데이터는 프로세서에 의해 쓰여질 때 초기화 됨

* dirty 상태인 캐시라인을 없애는데 이점이있음


- StrongARM과 XScale 프로세서는 그 캐시를 clean 하기 위해 추가적익 기법이 필요

=> 캐시를 clean하기 위해 사용되지 않응 캐시된 메인메모리의 전용 영역을 필요로 함 => ????, 전용 영역을 어떻게 쓰는거지?

- StrongARM과 XScale 프로세서는 메모리의 고정된 블록을 읽어 clean을 수행

=> 프로세서들이 라운드로빈 정책을 사용하기 때문


- 코어에서 캐시 크기와 동일한, 캐시된 메인메모리 블록을 연속적으로 읽을 때,

- 읽혀진 값들은 현재 모든 캐시 라인에 있는 값을 없애고 메인메모리로 읽혀저 저장

- 읽혀진 데이터 열이 모두 쓰여지면 그 안에 유용하지 않은 정보들을 가지고 있음

=> 전용 메모리 블럭은 그 안에 유용하지 않은 정보들을 포함하므로

- 캐시는 유용한 캐시된 데이터를 잃어 버릴수 있는 것을 모르고 flush 될 수 있음


=> StrongARM과 XScale에서 이 방법을 이용하여 캐시 cleaning을 수행

: cleanDCache, cleanFlushDCache, cleanFlushCache, cleanMiniDCache(XScale에서 작은 D캐시 clean에 사용)


>  Example 12.5 <

- CPWAIT, CACHECLEANXSCALE 매크로


> CPWAIT : XScale에서 CP15 연산이 부작용 없이 수행되는지 보장하기 위해 사용되는 3개의 명령어 코드

CP15 명령어가 완료되고 파이프라인안에 명령어들이 모두 클리어 될 때까지의 충분한 프로세스 사이클을 보장하기 위해 수행 됨


MACRO

CPWAIT

MRC    p15,    0,    r12,    c2,    c0,    0    ; CP15를 읽음

MOV    r12,    r12

SUB    pc,    pc,    #4        ; 다음 명령으로 분기

MEND


> CACHECLEANXSCALE : cleanDCache, cleanFlushDCache, cleanFlushCache 루틴 생성


.....



## 12.5.7 Cleaning and Flushing Portions of a Cache

- 캐시의 일부분을 클린하고 플러시하기


- ARM core는 메인메모리 안에서 표현되고 있는 위치를 참조하여 하나의 캐시라인만을 clean 및 flush 할 수 있음


> MCR 명령어 메인메모리 안에서 위치를 참조하여 캐시라인을 clean 및  flush하는 명령어



> 메인 메모리 안에서 원래 값으로 참조된 캐시 라인을 clean 하고 flush할 때의 CP15:c7 레지스터 포맷


- 명령어를 사용할 때 코어 레지스터 Rd에 저장된 값은 같은 프로세서하에서는 네 명령어 모두 동일하지만,

CP15:c7 레지스터 안에 있는 비트값의 포맷은 프로세서들 마다 다름

- 코어가 MMU를 가진다면 수정된 가상 주소에 의해,

- 코어가 MPU를 가진다면 물리 주소에 의해,

- 캐시라인을 clean하고 flush하는 코어를 위핸 레지스터 포맷을 나타냄


- 메모리의 한 영역을 나타내는 캐시 안에 캐시라인을 클린하거나 플러시하는 6개의 루틴을 만들기 위해 4개의 명령어를 사용

 flushCacheRegion

 메인메모리의 한 영역을 나타내는 I 캐시로부터 캐시라인을 플러시

 flushDCacheRegion

 메인메모리의 한 영역을 나타내는 D 캐시로부터 캐시라인을 플러시 

 cleanDCacheRegion

 메인메모리의 한 영역을 나타내는 D 캐시로부터 캐시라인을 클린

 cleanFlushDcacheRegion 

 메인메모리의 한 영역을 나타내는 D 캐시로부터 캐시라인을 클린하고 플러시 

 flushCacheRegion

 I 캐시와 D 캐시 둘 다로부터 메인메모리의 한 영역을 나타내는 캐시라인을 플러시

 cleanFlushCacheRegion

 D 캐시를 클린하고 플러시 한 다음, I 캐시를 플러시 




- 함수의 파라미터 : 메인메모리에 있는 시작주소(adr). 바이트로 나타낸 영역의 크기(b)


* 캐시 영역의 함수의 사용은 작은 메모리 영역에 대해서는 매우 성공적임



### 12.6. Cache LockDown

: 프로그램이 시간에 민감한 코드와 데이터를 캐시 메모리에 캐시 쪽으로 로드하고 이것을 eviction에서 면제되었음을 표시하는 것을 의미

- LockDown안의 코드나 데이터는 캐시 메모리 안에 저장되어 빠른 시스템 반응을 제공
- 캐시 동작의 일부인 캐시라인 eviction 과정에서 발생할 수 있는 예기치 못한 실행 시간의 문제점을 피하게 함

> 캐시 안의 정보를 Lock 하는 이유? 캐시 miss를 피하기 위함
 lock down을 위해 사용되는 캐시 메모리는, 메인메모리의 다른 부분을 캐어할 수 없기 때문에 사용가능한 캐시의 크기는 줄어들게 됨

> ARM core는 Lockdown을 위해 캐시의 고정된 부분을 할당 (way)
ex. 4-way 세트 연상 캐시에서 캐시 크기의 1/4번째에 해당하는 장치에 코드나 데이터를 locking
* 캐시된 코어는 항상, 보통의 캐시 동작을 위해 최소한 하나의 way를 할당

> 캐시 안에서 locking 할 항목
- vector interrupt tables
- interrupt service routins
- 시스템에서 배타적으로 사용하는 critical algorithms
- 자주사용되는 external variables

* ARM 코어 안에서 락되어있는 데이터나 코드들은 교체에서 면제되지만,
cache flush를 하면 lock down 안에 저장된 정보는 없어지고, 해당 영역은 메모리로 사용할 수 없게 됨.
=> cache lockdown 루틴은 lock down 정보가 복원될 수 있도록 return 되어야 함


## 12.6.1. Locking code and Data in Cache
: 캐시 안에서 코드와 데이터를 락하는 과정을 설명

> 전형적인 C 호출 시퀀스 


int interrupt_state;                /* FIQ와 IRQ 비트들의 상태를 저장 */

int glovalData[16];

unsigned int *vectorable = (unsigned int *)0x0;

int VectorCodeSize = 212;    /* 바이트 단위의 벡터 테이블과 FIQ 핸들러 */


interrupt_state = disavle_interrupts();        

enableCache();        /* 13장 MPU/MMU참고 */

flushCache();        /* 예제 12.2. 참고 */


/* 전역 데이터 블럭 락 */

wayIndex = lockDCache(globalData, sizeof(globalData));

/* 벡터 테이블과 FIQ 핸들러 락*/

wayIndex = lockICache((vectortable. vectorCodeSize);)


enable_interrupts(interrupt_state);    /*  어떤 코드도 제공되지 않음  */


- 시작에서 인터럽트 disabled, 캐시  enabled

- lockDCache() : D 캐시 안에 데이터를 한 블럭 locking

- lockICache() : I 캐시 안에 데이터를 한 블럭 locking


* lock down 루틴은 캐시 설정이 안되어있는 메인메모리에 위치해야 함

캐시 안에서 락되어 있는 코드와 데이터는 캐시 설정된 메인메모리 안에 위치해야 함

캐시 안에 있는 코드와 데이터가 캐시 이외의 다른 장소에는 존재할 수 없음

=> 만약 캐시 내용이 알려져 있지 않다면 로딩 전에 캐시를 flush, writeback에 D캐시라면 clean.


- 코드와 데이터가 캐시 안에 로드되면 인러텁트를 다시 enable


> 아키텍처에 따라 캐시안에 코드를 locking하는 방법 3가지

1. way 어드레싱 기술을 사용

2. lock-bit을 사용

3. 특별한 할당 명령어와 메인메모리의 전용 블록을 읽어서 수행


> lockDCache, lockICache를 구현하기 위한 예제와 사용방법 및 관련 프로세서들



## 12.6.2. WAY index를 증가시켜 캐시 락하기

- ARM920T, ARM926EJ-S, ARM940R, ARM946E-S, ARM1022E, ARM1026EJ-S 에서는 lock down을 위해 way와 set index addressing을 사용


- 2개의 CP15:c9:c0 레지스터는 victim 카운터 리셋 레지스터들을 포함하여 하나는 D캐시를 하나는 I캐시를 제어

=> 데이터를 locking하는  way내에서 캐시 라인을 선택하는데 사용


- CP15:c7 레지스터에 쓰여진 값은 victime 리셋값을 설정 (victim 카운터가 코어에서 ways의 수 이상으로 증가했을 때 초기화 될 값)

=> 전원을 켤 때 0, 캐시의 일부가 락다운을 위해 사용되면 SW적으로 변경이 가능

- 캐시의 일부가 락다운을 위해 사용될 때, 정보를 일시 저장하기 위한 캐시라인의 수는 락다운된 캐시 라인의 수만큼 감소

     (사용가능한 캐시라인의 수 = 전체 캐시라인 수 - 락다운된 캐시라인의 수)

- 레지스터를 읽으면 현재의 victim 리셋값이 return


> 두 레지스터를 읽거나 쓰기 위한 MRC, MCR명령어


> 해당 way값을 참조하여 캐시 안에 데이터를 락할 때 사용되는 CP15:c9 레지스터 포맷

lockdown address를 read/write 할 때 MCR, MRC 명령어 안에서 사용되는 코어 레지스터 Rd의 포맷은 프로세서마다 다름



> 특별한 load 명령어의 사용

: 메인메모리의 cache-line-size의 블럭을 I캐시 안의 캐시라인에 복사




> Example 12.7. <

: 증가하는 way 어드레싱을 사용하여 lockdown을 지원하는 프로세서를 위한 lockDCache, lockICache 루틴

: 두 루틴에 대한 return value는 사용가능한 victim pointer base address.


- CACHELOCKBYWAY에서 사용되는 레지스터를 정의

(cache.h의 상수도 사용)

- 주소(adr)를 캐시라인에 정렬

- 코드를 포함하는 way의 수를 결정하기 위해 바이트로 된 코드 크기를 사용

- I캐시와 D캐시 현재 victim포인터를 CP15:c9:c0에서 읽어옴

- overflow나 로드한 데이터의 크기가 0인지를 체크


- ARM940T, ARM946E-S의 캐시안에 코드나 데이터를 락하기 위해선 

캐시라인 안에 메모리블록을 락하기 전에 락비트를 먼저 설정 해야함

=> 락비스 셋, 데이터를 CP13:c9:c0에 저장


- 중복 loop에 진입하여 외부 loop에서는 way를 선택, 내부 loop에서는 way안에 있는 캐시라인을 증가 



## 12.6.3. 락비트을 사용하여 캐시 락하기

* ARM926EJ-S, ARM1026EJ-S는 한 세트의 락비트를 사용


- 0-3 bit : 두 프로세서에 대해 4-way 세트 연상 캐시 안에서 각 way를 표시

=> 비트가 1이면, 해당 way는 locking되어있는 상태

=> I캐시라면 코드를 포함하고 있고, D캐시라면 데이터를 포함하고 있음

- locking이 된 way는 lock이 풀릴 때가지 캐시 라인의 복귀를 하지 않음

- locking 풀기 : L 비트 중 하나를 클리어하여 구현

=> way 각각에 대한 개별적인 lock 구현이 가능(각 way의 개별적인 locking은 시스템 코드를 보다 쉽게 lock/unlock 할 수 있게 됨)


ARM926EJ-S, ARM1026EJ-S의 CP15:c9 명령어


> Example 12.8 <

ARM926EJ-S, ARM1026EJ-S를 위한 lockDCache, lockICache는 동일한 입력 파라미터를 갖음

- but, 코드 크기가 way 크기의 최대값으로 제한이 되며 3번까지만 호출이 가능

- L bit (3)은 항상 캐시전용으로 프로세서 하드웨어의 제한 및 프로그램밍의 필요를 충족시키기 위한 프로시저 콜에 주어진 제한일 뿐

- 크기 파라미터가 1byte 이상이라면, locking된 way의 L비트를 return.

- lockdown을 위해 사용가능한 way가 없다면 8을 return.



## 12.6.4. 인텔 XScale SA-110에서 캐시라인 락하기


- CP15:c9의 캐시 lockdown 명령어를 사용

- CP15:c7 allocate D캐시라인 명령어 사용



- XScale은 프로세서는 라운드로빈 포인터를 가짐

=> 캐시안에 있는 각 설정값들을 캐시 안에 있는 추가의 캐시 라인이 locking 될 대마다, 순차적으로 증가

* 1 세트 안에서 32개의 캐시라인 중 28개까지 locking이 가능하며, 

그 이상의 캐시의 lock을 시도해도 캐시안에서는 할당된 것 처럼 보이지만 실제로는 locking이 되어있지 않음


> XScale에서 D캐시안의 데이터 locking하는 2가지 방법

1. 메인메모리의 위치를 D캐시 쪽으로 locking

2. 캐시의 일부를 데이터 RAM으로 재설정 하기 위해 allocate 캐시 라인 명령어를 사용

=> 할당된 캐시의 부분은 초기화 되어있지 않고, 유효 데이터를 포함하기 위해 프로세서 코어로부터 쓰기를 필요로 함


> Example 12.9 <

- CACHELOCKREGION 매크로에서 사용될 레지스터를 정의. cache.h의 상수들을 사용

- 주소(adr)을 캐시라인에 정렬, 코드를 포함할 캐시라인의 수를 결정

- 프로시저가 D캐시안에 데이터를 lock하고 있다면 다음의 몇몇 라인들이 write-buffer에 놓여 D캐시를 unlocking 함

(D 캐시 안에 데이터를 locking할 경우에는 D 캐시라인을 locking 하기 전 unlocking 명령어를 사용해야 함)

=> CP15:c9:c20 레지스터에 1로 비트를  설정

- 코드는 locking 중인 코드나 데이터를 가진 캐시를 채우기 위해 loop를 돌며 아래를 수행

- 프로시저가 I 캐시 안에서 코드를 locking 중이면, lock I 캐시 라인 명령어를 실행

- 외부 메모리로부터 데이터를 locking 중이면,  새로운 캐시라인을 D 캐시 쪽으로 clean/flush하며 load

- 데이터 RAM을 만들었다면 D캐시를 할당하고 28세트 이상 lock을 시도한 결과 발생할 수 있는 에러에 대해 보호할 write-buffer를 끄집어 내기

- STRD 명령어로 캐시라인을 0으로 초기화

- D 캐시데이터가 locking 중이라면 매크로는 캐시로드 CP15 레지스터에서 lock-bit을 0으로 클리어하여 종료




### 12.7. Caches and Software Performance

: 캐시 아키텍처를 이용하는 코드 작성 시 몇가지 규칙들


- 메모리 시스템 안에 있는 대부분의 region 들은 캐시 아키텍처를 최대한 활용 할 수 있도록 cache와 write-buffer를 모두 enable.

=> 평균 메모리 액세스 시간을 줄임


- 메모리 매핑된 주변장치들은 cache나 write-buffer를 사용하도록 설정된 경우 종종 오작동을 일으킴

=> 즉, 주변장치들은 메모리가 cache나 write-buffer를 사용하지 안도록 하는 것이 좋음

(캐시안에 저장되어 있는 오래된 데이터를 읽는 대신 매번 메모리 액세스를 통해 주변장치를 읽음)


- 캐시라인을 채우려면 메인메모리에서 새로운 데이터를 읽는 비용이 발생함

=> 자주 액세스하는 데이터는 메모리 안에 연속적으로 저장하는게 좋음. 보통의 루틴들이 액세스하는 데이터들을 메인메모리 안의 가가이에 함께 위치.

(locality를 통해 cache hit-rate를 높여줄 수 있음)


-  writing/processing/reading이 cache-line-size의 블록으로 이루어지도록 데이터를 구조화 하기

=> cache-line-size의 블럭은 메인메모리의 낮은 주소가 캐시라인의 시작주소와 일치시켜야 함


- 캐시를 사용시 linked-list는 프로그램의 성능을 저하시킬 수 있음

=> 리스트를 찾는 작업에서 많은 cache-miss가 발생할 수 있음







'System > Embedded' 카테고리의 다른 글

[ARM] MPU  (0) 2015.11.17
[ARM] 캐시  (0) 2015.11.12
[ARM] 펌웨어  (0) 2015.11.12
[ARM] 익셉션과 인터럽트 처리 2  (0) 2015.11.10
[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

The Memory Hierarchy and Cache Memory

Cache Architeture

Cache Policy

Coprocessor 15 and Caches

Flushing and Cleaning Cache Memory

Cache LockDown

Caches and Software Performance




#### 12. Cache


- 프로세서 코어와 주메모리 사이의 작고 빠른 메모리
- 가장 최근에 참조한 주메모리의 일부분을 저장

> write buffer : 프로세서 코어와 주메모리 사이의 매우 작은 FIFO 메모리
 main memory에 쓰는 것과 관련된 느린 쓰기 시간으로부터 플세서 코어와 캐시 메모리를 자유롭게 해줌

> 캐시의 주요 단점 : 프로그램의 실행 시간을 결정하기가 어려워 짐


## 12.1. The Memory Hierarchy and Cache Memory


- 레지스터 파일 : 코어에 위치하여 시스템에서 가장 빠른 메모리 액세스를 제공

- TCM / level-1 캐시 : 기본 레벨에 위차하는 메모리 컴포넌트로 on-chip 인터페이스를 통해 프로세서 코어에 연결

- main memory : SRAM/DRAM같은 휘발성 컴포넌트와 플래시 메모리 같은 비휘발성 컴포넌트가 있음


- 보조기억장치 : 디스크 드라이브나 이동 메모리 장치와 같은 크고 느린, 상대적으로 가격이 저럼한 대량 저장 장치

- 데이터 액세스 시간이 김

- 메인메모리에서 저장할 수 없는 매우 큰 프로그램에서 사용되지 않는 부분과 현재 실행되지 않는 프로그램을 저장

프로세서 코어에 읳 지원되는 데이터 형을 사용하여 프로세서 코어에 의해 직접 액세스 됨


* TCM와 SRAM은 동일한 기술이지만 아키텍처로서는 다른 레벨에 위치

(TCM은 CPU가까이에 위치한 SRAM)


- L1 캐시 : 가장 낮은 레벨에서부터 코드와 데이터를 임시로 저장하는 고속의 on-chip 메모리

- write buffer : 캐시로부터 메인메모리에 쓰기를 지원하는 매우 작은 FIFO 버퍼

- L2 캐시 : L1캐시와 더 느린 메모리 사이에 위치하며 보조캐시라고 함

- cache line : 캐시와 더 느린 메인메모리 사이에 전송하는 데이터의 작은 블럭




## 12.1.1. 캐시와 메모리 관리 장치
- 캐시코어는 코어와 MMU 사이에 위치하거나 MMI와 물리 메모리 사이에 위치하여 가상메모리를 지원 함

- 논리캐시 : logical cache, 프로세서와 MMU 사이에 위차하여 가상 주소 공간에 데이터를 저장하는 것
MMU를 거치지 않고서도 논리캐시에서 직접 데이터를 가져올 수 있음. 가상캐시.
- 물리캐시 : physical cache, MMU와 메인메모리 사이에 위치하여 물리주소를 사용해 메모리에 저장하는 것
MMU는 먼저 가상 주소를 물리 주소로 변경해야 함. 

* ARM7 ~ ARM9, StrongARM, XScale : 논리캐시 
ARM11 : 물리캐시


> 캐시에 의한 성능향상이 가능한 이유 <

: 예측 가능한 프로그램의 실행


"locality" (반복문 등에서)


- temporal locality : 시간적 지역성. 시간과 공간에서의 반복적인 국부적 참조성을 사용

- spatial locality : 공간적 지역성. 주소의 근접성에 의해 참조




### 12.2. Cache Arcitecture


# ARM은 캐시를 가진 코어에서 2가지의 버스 아키텍처를 지원

- 폰노이만 아키텍처 : 명령어와 데이터를 위해 사용하는 하나의 캐시를 통합캐시라고 하며, 통합 캐시는 명령어와 데이터값을 둘 다 포함하고 있음

- 하버드 아키턱처 : 전번적인 시스템 성능의 향상을 위해 분리된 명령어와 데이터 버스를 가짐.

=> 2개의 캐시를 필요로 함. 분할캐시 (명령어 캐시 I, 데이터 캐시 D)


# 캐시의 2가지 주요 요소

- cache memory : "cache line"이라는 장치 안에서 액세스되는 전용 메모리 열

- cache controller : 캐시 메모리의 일부분을 선택하기위해 메모리를 요청하는 동안 프로세서에 의해 이슈되는 주소의 다른 부분을 사용



## 12.2.1. Basic Architecture of a Cache Memory



# 3가지 구성요소

- Directory store : 메인메모리에서 캐시라인으로 복사된 주소를 구분하기 위해 사용. Directory Entry의 "cache-tag"

* cache-tag : 캐시라인이 메인메모리로부터 어디로 로드되는지 가리킼는 디렉토리 엔트리

- Data section : 메인메모리에서 읽어온 데이터를 저장하는 영역

캐시의 크기는 캐시가 메인메모리에서 저장할 수 있는 실제코드나 데이터로 정의되며,

캐시태그나 상태 비트들을 지원하는 데 필요한 캐시 메모리는 캐시 크기 안에 포함

- Status information : 상태 정보를 유지하기위한 상태비트

valid bit : 유효비트. 캐시 라인이 활성화되어 있음. 주 메모리에서 처음 읽어온 데이터를 포함하며 현재 프로세서 코어가 사용 가능하단 것을 의미

dirty bit : 더티비트. 캐시라인이 메인메모리 안에 저장되어 있는 값과 다른 데이터를 포함하고 있는지 아닌지를 결정



## 12.2.2. Basic Operation of a cache Controller


- 메인메모리에서 캐시메모리로 코드나 데이터를 자동으로 복사해주는 하드웨어

- read/write 동작을 위한 메모리 요청을 메모리 컨트롤러로 보내기 전에 가로챔

요청 주소이 구성 : tag + set index + data index


# 기본동작

1. 컨트롤러는 "set index"를 사용하여 요청된 코드나 데이터를 포함하고 있는 캐시 메모리의 캐시라인을 찾음

캐시라인은 캐시태그, 상태비트들을 포함하며, 캐시태그와 상태비트들은 실제 데이터를 평가하기위해 사용됨

2. 컨트롤러가 캐시라인의 활성화를 검사하기위해 유효비트를 확인하고 요청된 주소의 태그영역과 캐시 태그를 비교

성공 : hit, 실패 : miss

3-1. 캐시미스 : 메인메모리에서 모든 캐시 라인을 캐시메모리로 복사. "cache line fill" 캐시라인 채우기

요청된 코드나 데이터를 프로세서에게 전송

3-2. 캐시적중 : 캐시메모리에서 프로세서로 직접코드나 데이터를 공급하기위해 4단계로 이동

4.   캐시라인 안에 있는 실제 코드나 데이터를 선택하기 위해 주소요청의 데이터 인덱스 필드를 사용하여 프로세서에게 공급



## 12.2.3. The Relationship between Cache and Main Memory


# Direct mapped cache

- 가장 간단한 캐시 형태로 주어진 메인메모리 공간에 대해 캐시안에 하나의 공간만 할당

- 메인메모리 안에 각각 상주한 위치가 캐시 메모리 안에 하나의 위치로 바로 매핑

메인메모리의 크기가 훨씬 더 크기때문에 메인메모리에는 캐시메모리의 동일한 위치에 매핑되는 곳이 많음


*0x824로 끝나는 주소체계에 대한 관계


- set index : 0x824로 끝나는 주소를 가진 메모리 안에 모든 값들이 저장되어있는 캐시의 한 위치를 선택

- data index : 캐시라인에서 word, half-word, bytes를 선택

- tag : directory store에서 발견된 캐시 태그값과 비교한주소의 일부분을 저장


# Data Stream : 캐시라인을 채우는 동안 캐시 컨트롤러는 캐시에 복사하며 코어에 데이터를 로딩할 수 있음

캐시 컨트롤러가 캐시 라인 안에 남아있는 워드를 채우는 동안 프로세서가 계속 동작 할 수 있도록 함


# eviction : 캐시 미스가 발행하여 기존의 캐시 라인을 제거하는 과정

캐시 안에 로드외어야 하는 새로운 데이터를 위한 공간을 만들기 위해 캐시에서 메인메모리로 캐시 라인의 내용을 저장


Thrashing : 캐시 메모리 안에 동일한 위치에 대해 소프트웨어 병목 현상이 발생하는 것

Direct mapped cache에서는 메인 메모리로부터 값을 저장하기 위해 사용할 수 있는 공간이 하나이므로 가장 높은 thrashing을 야기할 수 있음

=> thrashing 때문에 캐시 라인을 반복해서 제거하고 로딩하는 작업이 일어남


* loading, eviction : 

프로그램의 일부를 캐시 라인안에 있는 동일한 캐시라인에 매핑되어있는 주소에서 메인메모리로 이동하는 결과를 야기


* cache thrashing : 실행하지 않는 루틴을 연속적으로 제거할 때 캐시미스가 반복되는 현상


=> trashing(스레싱)을 줄이기위해서 캐시는 way라는 더 작은 단위로 쪼개짐   .

하나의 메인메모리 주소에 대해 캐시 안에 여러개의 저장 위치를 제공할 수 있음(세트 연상 캐시, set associativity)



## 12.2.4. Set Associativity

- 목표 : 스레싱의 주기를 줄이기 위함

- way : 캐시메모리를 동일한 크기로 나눠놓은 단위

기존 256라인의 하나의 way  ->  64라인의 4개의 way로 사용

- Set index : 동인한 세트 인덱스를 가지는 4개의 캐시라인을 동일한 세트안에 있다고 말함

- Set Associativity : 세트 인덱스가 가리키는 캐시라인의 세트


- 메인메모리의 특정 위치로부터의 데이터나 코드 블록은 한 세트의 메모리인 어떤 캐시라인 안에 저장됨

- 세트안에 값을 저장하는 것은 동일한 코드나 데이터 블록을 한 세트안에 2개의 캐시 라인을 동시에 위치시키지 못하게 하기 위해 배타적으로 동작


# 4-way 세트연상


* 메인 메모리의 어떤 하나의 위치는 캐시안에 4개의 다른 위치에 매핑


- 태그에 대한 필드가 2비트 더 큼

- 세트 인덱스 비트 필드는 2비트 더 작음

=> 4배의 메인메모리 주소가 한세트의 4개의 캐시라인에 매핑됨을 의미


- 캐시에 매핑되는 메인메모리 영역의 크기는 4KB ->1KB  

- 캐시라인 데이터 블록을 동일한 세트에 네 번 더 매핑해야 함

=> 캐시라인이 1.4배 덜 제거될수 있음  ? ? ? ? ? ? ? ?




## 12.2.4.1. Increasing Set Associativity

- 캐시 컨트롤러의 Associativity가 증가할 때마다 thrashing의 발생은 줄어듬

- 메인메모리 위치가 어떤 캐시 라인에 매핑되도록 캐시의 set associativity를 최대화하여 설계하는 것이 좋을 수 있음

=> "Fully Associative Cache"

- set associativity를 증가하기 위해 하드웨어 설계자는 CAM을 포함할 수 있음


# CAM

- Content Addressable Memory

-  각각의 유효한 캐시 라인 안에 저장되어 있는 캐시태그와 입력 태그 주소를 비교하는 비교기세트를 사용

- RAM이 동작하는 반대 방법으로 동작(RAM은 주소값이 주어지면 데이터를 생성. CAM은 데이터값이 메모리 안에 존재할 때 주소를 생성)

=> CAM을 사용하면 더 많은 캐시태그를 동시에 비교할 수 있으므로 한 세트 안에 포함될 캐시라인의 수를 늘릴 수 있음

- ARM920T, ARM940T : 64-way


- 요청된 주소의 태그부분은 4개의 CAM입력으로 사용되어 64-way 안에 저장되어 있는 모든 캐시 태그와 입력 태그를 동시에 비교

- 일치하는 것이 있으면 메모리로부터 캐시데이터를 받고, 일치하는 것이 없다면 캐시 컨트롤러는 미스 신호를 전송


* 컨트롤러에서 set index bit을 사용해 4개의 CAM을 활성화,

index된 CAM은 캐시메모리안에 있는 캐시라인을 선택,

코어 주소의 데이터 인덱스부분으로 캐시라인안에서 요청된 워드 or 하프워드 or 바이트를 선택



## 12.2.5. Write Buffers

- 프로세서가 메인메모리에 쓸 데이터를 임시로 저장하고있는 작고 빠른 FIFO 메모리

- 작은 블록의 순차적인 데이터를 메인메모리에 write하는 시간을 줄임


eviction 동안에는 캐시의 성능 향상에도 도움을 줌

: 캐시 컨트롤러가 dirty cache line을 제거하면 메인메모리 대신 write buffer에 캐시라인이 쓰여짐

=> 즉, 새로운 캐시라인 데이터는 바로 사용이 가능하며 프로세서는 캐시메모리로 부터 계속해 동작이 가능 함


- write buffer에 쓰여진 데이터는 write buffer의 내용을 메인메모리로 저장 하기전에는 read가 불가능하며 cache line의 제거도 불가능

=> write buffer에 있는 동안은 읽기가 불가능 함

=> write buffer의 FIFO 깊이를 몇 개의 캐시라인정도의 매우 작은 크기로 줄여야 함


> 제한적인 FIFO 구조를 갖는 write buffer의 예외.

- ARM10 제품군은 "Coalescing(합성)" 방식을 지원

- write 연산을 하나의 캐시라인에 통합시킴.

- write buffer는 메인메모리 안의 동일한 데이터블록을 나타내는 경우 새로운 데이터를 기존의 캐시라인에 합성

- 합성방식 : write merging, write collapsing, write combining



## 12.2.6. Measuring Cache Efficiency

- 캐시의 효율성 측정


 hit rate : 주어진 시간 간격 동안 캐시 적중 수를 전체 메모리 요청 수로 나는 것


적중률 = (캐시 적중 수 / 메모리 요청 수) * 100


miss rate : 주어진 시간 간격 동안 전체 캐시 미스를 전체 메모리 요청수로 나눈 값의 백분률


미스률 = (캐시 미스 수 / 메모리 요청수) * 100


- hit time : 캐시에서 어떤 메모리 위치를 액세스 하는데 걸리는 시간

- miss penalty : 메인메모리에서 캐시로 캐시라인을 로드하는데 걸리는 시간




# 12.3. Cache Policy

: 캐시 동작을 결정하는데 사용되는 정책


- write policy : 프로세서가 쓰기 동작을 할 때 데이터를 어디에 저장할지를 결정

- replcement policy : 캐시 미스가 발생하였을 때 다음 라인을 채우는데 사용될 세트 안에 있는 캐시라인을 선택

- allocation policy : 캐시 컨트롤러가 캐시 라인을 언제 할당할 지 결정



## 12.3.1. Write Policy - Writeback or Writethrough

: 프로세서 코어가 메모리에 저장을 할 때, 캐시 컨트롤러가 사용하는 정책


# 12.3.1.1. 연속기입방식(writethrough)

- 저장을 할 때 캐시가 hit이라면 캐시와 메인메모리에 모두 값을 저장

- 캐시와 메인메모리가 항상 일관성을 유지

- 캐시컨트롤러는 캐시메모리에 값을 쓰기 위해 메인메모리에 쓰는 작업을 수행

=> 후기입방식보다 느림


# 12.3.1.2. 후기입방식(writeback)

- 유효한 캐시 데이터 메모리에만 저장하고 메인메모리에는 저장하지 않음

- 유효 캐시 라인과 메인메모리는 다른 데이터를 포함

=> 캐시라인은 가장 최근 데이터를 저장, 메인메모리에는 업데이트가 되지 않아 오래된 데이터를 포함


- 캐시 상태정보 블럭 안에 있는 하나 이상의 dirty bit을 사용

캐시 컨트롤러가 캐시 라인 안에 값을 저장하면 dirty bit을 1로 설정 한 후

코어가 캐시 라인을 액세스하려하면, dirty bit을 통해 캐시라인의 데이터와 메인메모리의 데이터가 다름을 알림

캐시 컨트롤러가 dirty한 캐시라인을 지우면, 내용이 자동으로 메인메모리에 저장 됨

=> 컨트롤러가 캐시라인에만 있는 최신 데이터를 메인메모리에 저장


* 서브루틴에 의해 임시지역변수를 반복적으로 사용할 때 우수한 성능을 보임

=> 임시지역변수는 주 메모리에 저장될 필요가 없기 때문

    (ex. 캐시된 스택이 오버플로우가 발생하는 지역 변수)



## 12.3.2. Cache Line Replacement Policy


- Cache Line : 캐시의 기본 컴포턴트로 Directory Store, Data Section, Status 로 구성


- 캐시미스가 발생하면 컨트롤러는 메인메모리로부터 새로운 정보를 저장하기 위해 캐시 메모리안에서 적절한 캐시라인을 찾아야 함

- victim : 교체를 위해 선택된 캐시라인. 

victim이 유효한 더티데이터를 포함한다면 컨트롤러는 새로운 데이터를 victim 캐시라인에 복사하기 전,

 캐시메모리로부터 메인메모리로 더티데이터를 씀

- eviction : victim 캐시라인을 선택하고 교체하는 과정

- replacement policy : victim을 선택하기 위해 캐시컨트롤러에 구현되어 있는 방법

설정된 정책은 캐시 컨트롤러가 캐시 메모리 안에 가능한 설정으로 부터 캐시라인을 선택하기 위해 사용하는 알고리즘을 정의함


=> set index는 가능한 캐시라인세트를 선택하고, replacement policy는 세트에서 교체할 특정 캐시라인을 선택


> replacement policy

 pseudorandom(의사랜덤방식)

 교체될 세트안에 있는 다음 캐시라인을 랜덤하게 선택

 - 선택 알고리즘을 무조건 증가하는 victim counter를 사용

 - 컨트롤러는 무작위로 증가값을 선택하고 이를 victime counter에 더하여 증가시킴

 - victim counter가 최대값이 되면 정이된 베이스 값으로 reset

 roud-robin(주기적교체)

 단순히 교체될 세트안에 있는 다음 캐시라인을 선택하는 것

 - 캐시 컨트롤러가 캐시라인을 할당 할 때마다 증가하는 victime counter를 사용

 - victime counter가 최대값이 되면 정의된 베이스 값으로 reset


 - 보다 광범위한 예측성을 가지며 이상적이나, 메모리 액세스 측면에서 작은 변경에도 성능면에 큰 변화를 가져옴

 LRU(Leat Reccently Used)

 - 캐시라인의 사용에 대해 조사하여 가장 오랜 시간 동안 사용되지 않은 캐시라인을 victime으로 선택

 (ARM 캐시코어는 LRU 방식은 지원하지 않지만 ARM반도체 파트너들으 캐시가 없는 ARM을 구매하여 그들이 만든 칩에 자신의 캐시를 추가하고 있어 LRU방식을 사용하는 ARM 기반의 제품도 나옴)  




## 12.3.3. Allocation Policy on a Cache Miss

- 캐시미스가 발생했을 때 ARM 캐시가 캐시라인을 할당하기 위한 정책

- 2가지 할당 방법 : read-allocated, read-write-allocate


# read-allocate 

: 메인 메모리에서 읽는 동안 캐시 라인을 할당 (데이터를 읽었을 때 메인메모리에서 캐시라인을 할당)

- victim 캐시 라인이 유효 데이터를 포함하고 있다면 캐시 라인이 새로운 데이터로 채워지기 전에 메인메모리로 쓰여짐

- 캐시라인이 유효데이터를 포함하고 있다면 메인메모리에 쓴 값은 캐시에 없데이트 되고, 

write policy로 write-through을 사용중이면 메인메모리에도 업데이트

- 만약 캐시라인이 이전에 메인메모리에서 읽은 것으로 할당되지 않으면, 메모리에 새로 쓴 데이터는 캐시메모리의 내용에 업데이트 되지 않음

- 데이터가 캐시안에 없다면 컨트롤러는 메인메모리에만 저장


# read-write-allocate

: 메모리를 읽고 쓰기 위해 캐시라인을 할당(데이터를 메인메모리로 슬 때 캐시라인을 할당)

- 캐시안에 없고 메인메모리에서 구현된 load-store 연산은 캐시라인을 할당

- 메모리를 읽을 때 컨트롤러는 read-allocate 정책을 사용


- write 할 때 컨트롤러도 캐시라인을 할당

- victim 캐시라인이 유효 데이터를 포함하면 

캐시 컨트롤러는 메인메모리로부터 새로운 데이터를 가지고 victim 캐시라인을 채우기 전 메인메모리에 먼저 저장

- 캐시라인이 유효하지 않다면 단순히 캐시라인만 채움

- 캐시라인이 메인메모리로부터 채워진 다음, 컨트롤러는 캐시라인 안의 데이터의 위치에 데이터를 저장

- write-through 방식을 사용한다면 캐시된 코어는 메인메모리에도 업데이트


> 각 코어에 의해 지원되는 policies



### 참고. ARM 커뮤니티에서 사용되는 많은 용어들 (요약정리)




'System > Embedded' 카테고리의 다른 글

[ARM] MPU  (0) 2015.11.17
[ARM] 캐시 2  (0) 2015.11.12
[ARM] 펌웨어  (0) 2015.11.12
[ARM] 익셉션과 인터럽트 처리 2  (0) 2015.11.10
[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Firmware and BootLoader

Example : SandStone




### 10.1. 펌웨어와 부트로더


# 펌웨어

: 하드웨어OS/Applications 레벨의 소프트웨어 사이의 인터페이스를 제공하는 하위 레벨 소프트웨어

- ROM에 상주, 시스템전원이 공급될 때 실행됨

- 시스템초기화가 된 후에도 실행 가능한 상태로 남아있을 수 있으며 기본적인 시스템 동작을 지원

- 주요 목적 : OS를 롣하여 부팅하기 위한 안정적인 mechanism을 제공


# 부트로더

: OS나 응용프로그램을 하드웨어 타겟 메모리로 다운로드 후 해당 소프트웨어로 pc 제어권을 넘기는 작업을 수행하는 작은 application

- OS나 응용프로그램이 시행되고 있는 시점에서 존재하며 일반적으로 펌웨어에 통합되어 있음


# 펌웨어 실행과정


1. Set up target platform

- 운영체제를 부팅하기 위한 환경을 준비

- 플랫폼이 초기화가 잘 되었는지 확인하는 작업도 수행

- 같은 실행 이미지를 다른 코어나 플랫폼 상에서 동작시키므로 정확한 코어의 종류와 플랫폼을 검색할 수 있어야 함

코어의 종류는 보통 코프로세서 0번째 레지스터에 저장되어 있음. (프로세서의 종류, 제조사 , ...)    

플랫폼의 종류는 특정 주변장치의 존재 여부를 체크, 칩에 저장된 정보를 읽기 등 다양한 방법이 존재

- 진단소프트웨어는 하드웨어의 불량 여부를 알아내는 유용한 방법을 제공하며 특성상 특정 하드웨어에 의존적임

- 디버깅 기능은 하드웨어 타겟에서 실행되는 디버깅 코드를 위한 소프트웨어를 제공해주는 모듈이나 모니터 형식으로 제공됨

RAM에 breakpoint 설정

메모리 상태를 표시하고 수정하기(peek and poke방식을 사용)

현재 프로세서의 레지스터 내용 표시

메모리를 ARM이나 Thumb 명령어로 역 어셈블하기


- CLI나 타겟 플랫폼에 연결되어있는 전용 호스트 디버거를 통해 명령어를 입력

펌웨어가 내부 디버그 회로에 접근할 수 없는 경우에는 RAM이미지만 SW Debug mechanism을 통해 디버깅 됨

- 호스트와 타겟간의 통신은 보통 시리얼이나 네트워크 연결을 통해 이루어짐


2. Abstract the Hardware 

- HAL(Hardware Abstract Layer) : 일련의 정의된 프로그래밍 인터페이스를 제공하여 하드웨어를 숨겨주는 소프트웨어 레이어

- 특정 하드웨어 주변장치와의 통신 역할을 담당하는 HAL Software를 "Device Driver"라 함함

Device Driver는 특정 주변 장치에서 데이터 read/write을 위한 API를 제공공


3. Load a bootable image

- 모든 OS/Applications은 RAM에 복사되어 실행되야하는 것은 아니며 ROM에서 직접 실행될 수 있음

-> 펌웨어 기능은 이미지를 저장하는데 사용될 미디어의 유형에 따라 달라짐

- FFS(Flashrom File System) : ARM 프로세서는 보통 플래시롬을 포함하며, FFS는 여러 실행 이미지를 저장하도록 함

- 로드하는 과정은 이미지 포맷을 고려해야 함

2진 이미지 : 헤더나 디버깅정보를 포함하지 않는 형식의 이미지

ELF(Excutable and Linking Format) : UNIX에서 개발되어 COFF라는 이미지 포맷을 대체하게 됨

-> 재배치 오브젝트 파일, 실행 가능한 오브젝트 파일, 공유오브젝트의 3가지 형식으로 구성

COFF : Common Object File Format의 약자로 ELF이전에 사용하던 포맷

- ELF 파일을 로드하는 과정에서 표준 ELF 헤더 정보를 분석하는 작업이 필요(이미지는 암축, 암호화될 수 있음)

* ELF 헤더 정보 : 실행주소, 유형, 크기 등


4. relinquish control 

- 펌웨어에서 OS/applications에게 플랫폼의 제어권을 넘기는 작업을 수행 (경우에 따라 펌웨어가 제어권을 유지하는 경우도 있음)

- OS가 제어권을 갖지 못한 경우 펌웨어의 일부인 MIL이나 HAL이 활성화되어,

SWI mechanism을 통해 특정 하드웨어 장치를 위한 표준 어플리케이션을 표시

* MIL : Machine Independent Layer


* 일반적으로 사용하되는 2가지의 firmware suite 

ARM Firmware Suite(AFS)

redhat ReadBoot



## 10.1.1. ARM Firmware Suite

- ARM에서 개발한 Firmware Package

- ARM기반의 임베디드 시스템을 위해 설계되어   XScale(Intel), StrongARM 프로세서를 포함한 많은 보드들과 프로세서들을 지원

- 두가지 중요 기술 : μHAL, Angel


μHAL

- 마이크로 HAL

- 서로 다른 통신장치들 사이에서의 동작이 가능하돌 해주는 하웨레벨 디바이스 드라이버를 제공

- 표준 API 제공

- port를 사용하는 경우라면 하드웨어에 영햐을 받는 부분들은 반드시 μHAL API로 구현현

- 표준 함수 프레임워크도 제공하며 상대적으로 포팅작업이 수월


μHAL의 중요한 기능들

 시스템 초기화

 타겟 플랫폼과 프로세서 코어를 셋업

 타겟 하드웨어의 복잡도에 따라 작업의 복잡도에 영향을 미침 

 폴링방식의 시리얼 드라이버  

 호스트와의 기본적인 통신을 위해 사용 

 LED 지원 

 간단한 사용자 피드백을 위한 LED 지원. application에게 동작상태를 표시

 타이머 지원

 추가적인 인터럽트를 설정할 수 있음

 context switching기능을 필요로하는 선점형 OS에서 필수적인 기능 

 인터럽트 컨트롤러 다양한 인터럽트 컨트롤러를 지원 


μHAL의 부트모니터는 CLI를 포함하고 있음


# Angel

- 호스트 디버거와 타겟 플랫폼 사이의 통신을 구현

* 호스트 디버거 상에서의 작업들

- 메모리의 값을 알아내 수정하거나 이미지를 다운로드하여 실행할 수 있음

- 브레이크포인트를 잡거나 프로세서 렐지스터 안의 내용을 표시하는데 사용할 수 있음

=> Angel 디버그 모니터는 SWI 벡터와 IRQ, FIQ벡터에 접근이 가능해야 함

- 프로그램이 호스트 파일 시스템을 열어 데이터 read/write이 가능하도록 API를 제공

* API는 SWI 명령어를 사용

- IRQ/FIQ 인터럽트는 호스트 디버거와의 통신 목적을 위해 사용



## 10.1.2. Red Hat, RedBoot

- redhat에서 개발된 펌웨어 툴

- 오픈소스 라이센스

- ARM, MIPS, SH등 다양한 CPU을 지원

- GDB(GNU Debugger)를 이용한 디버그 기능과 부트로더 기능을 제공

- RedBoot 소프트웨어는 HAL 기반


> RedBoot의 기능

- 통신 : 직렬통신 및 이더넷을 선택적으로 사용할 수 있음

직렬통신 : XModem protocol이 GDB와의 통신을 위해 사용

ethernet : TCP를 GDB와의 통신을 위해 사용


* RedBoot은 bootp, telnet, tftp와 같은 다양한 네트워크 표준 프로토콜을 지원


- 플래시 메모리 관리 장치

: 플래시롬 안에 이미지를 다운로드하거나 업데이트하고 지우는데 사용될 수 있는 파일시스템 루틴을 제공

이미지 압축/해제 기능도 제공


- 완전한 운영체제 지원

: 임베디드 리눅스, 레드햇 eCos, 기타 많은 유명한 운영체제들의 로딩과 부팅을 지원

임베디드 리눅스인 RedBoot은 부팅시 커널로바로 보내게 될 파라미터들을 정의하는 기능도 지원



### 10.2.  Example. SandStone


"SandStone이라는 시스템의 설계 예제"


- 타겟 플랫폼의 환경 설정

- 부팅이미지 로드

- OS에게 제어권 넘기기


- 타겟 : ARM7TDMI코어를 포함하는 ARM Evaluator-7T

- 목표

1. 간단한 플랫폼이 셋업되는 방법을 이해

2. 소프트웨어 페이로드가 메모리로 로드되어 부팅되는 과정을 이해

* payload는 applications 이미지 혹은 OS 이미지


* SandStone은 정적인 설계 과정으로 빌드 과정이 완료된 후에는 재설정이 불가능


- ARM 어셈블러로 완전히 작성되어 있음

- ARM Evaluator-7T에 대한 타겟 하드웨어를 초기화하고 소프트웨어의 일부를 부팅해주는 일련의 작업코드로 되어있음

- 웹에서 다운로드 가능


# SandStone의 기본적인 특징



## 10.2.1. Sandstone 디렉토리 구조

: 소스코드의 위치, 다른 빌드파일의 위치


 [Sand] 

 makefile

 

 

 readme.txt       ; ARM Evaluator-7T를 위한 바이너리 이미지를 빌드하는 방법에 대한 설명

 [buid]

 [src]

 sand.s 

 [obj]               ; 어셈블러에 의해 생성된 오브젝트 파일들

 

 [image]          ; 오브젝트 파일들이 링킹되어 만들어진 최종 이미지 파일들

                       ; 이미지는 Sandstone 코드와 페이로드 이미지를 포함

 

 [log]               ; 

 

 [payload]    ; 페이로드 이미지들 

 slos.bin 

 



## 10.2.2. Sandstone 코드 구조

- Sandstone 코드는 하나의 어셈블리 파일로 구성

- 파일 구조는 여러 단계로 분리할 수 있음. 각 단계는 각 실행과정을 의미함



* 플랫폼에 상관없이 시스템 레지스터 설정이나 메모리 리매핑과 같은 특정 부분은 반드시 펌웨어에 포함되어야 함


* Sandstone의 초기 목표 ? 

펌웨어가 동작중이며 플랫폼의 제어권을 가지고 있는가에 대해 알려줄수있는, 일종의 피드백 기능을 제공하는 타겟 플랫폼 환경 구축


# 10.2.2.1. 1단계 : Reset exception 처리


- 가장 먼저 Reset Exception이 실행

- 실행될 첫 명령어가 Reset 명령어 이므로 default vector table에 "Reset vector"만 있으면 됨

- 코드에서 reset vector 이외의 vector들은 모두 무한루프를 수행하는 더미 핸들러로 분기

(SandStone의 동작 중에는 어떠한 익셉션이나 인터럽트로 발생하지 않는다고 가정)

- Reset vector는 두번째 단계로 이동하기 위해 사용

- sandstone_start의 초기주소는 0x00000000

> 1단게 정리 < 

1. 더미 핸드러가 셋업

2. 하드웨어 초기화를 위한 코드로 제어가 넘어가게 됨


# 10.2.2.2. 2단계 : 하드웨어 초기화


- 하드웨어 초기화의 기본단게는 시스템 레지스터를 셋업하는 것 (시스템 레지스터는 하드웨어를 액세스하기 전 셋업되어있어야 함)


ARM Evaluator-7T의 "세븐 세그먼트 디스플레이 장치"

: 펌웨어의 활성화 여부를 가리키기 위한 피드백 장치로 사용

해당 장치의 셋업 전 시스템 레지스터의 Base Address를 정해진 주소에 위치해야함. (0x03ff0000로 설정)

=> 하드웨어 시스템 레지스터가 ROM/RAM에서 떨어진 위치에 올 수 있도록 하여 주변장치와 메모리를 분리시켜 줌 ????


- 모든 마이크로 컨트롤러 메모리맵 레지스터는 0x03ff0000에서의 오프셋에 위치



* r3 : 디폴트 시스템 레지스터 베이스 주소를 포함. 캐시와 같이 특정 속성과 새로운 디폴트 주소를 설정하는데 사용


- 상위 16-bit는 새로운 시스템 레지스터 베이스 주소 0x03ff를 포함하며, 하위 16-bit는 새로운속성값  0xffa0을 포함

0x03ff0000을 r3 주소로 메모리에 로드

0x03ffffa0을 r4 주소로 메모리에 로드

r4가 가리키는 메모리 주소의 내용을 r3이 가리키는 주소에 저장


- 시스템 베이스 주소를 셋업 후, 세그먼트 디스플레이 장치를 설정할 수 있음

* 세그먼트 디스플레이 하드웨어 : sandstone의 과정을 보여주기 위해 사용


> 2단게 정리 <

1. 0x03ff0000이라는 베이스 주소를 기준으로 시스템 레지스터들이 설정됨

2. 그 과정의 디스플레이를 위해 세그먼트 디스클레이 장치가 설정


# 10.2.2.3. 3단계 : 메모리 리맵핑


- SRAM을 초기화, 메모리 재배치를 수행


*플랫폼은 초기메모리 상태에서 시작


- 플랫폼에 전원공급 시, 플래시롬만 메모리맵에 할당.

2개의 SRAM bank 0,1은  초기화되지 않아 사용이 불가능 함



- 리매핑이 일어나기 전, sandstone_init2 루틴의 절대 주소를 계산

=> 새로운 리맵환경에서 다음 루틴으로 분기하기위해 사용


LDR    r14,    =sandstone_init2

LDR    r4,    =0x01800000


- 메모리 리매핑의 수행. 새로운 메모리맵 데이터는 memorymaptable_str이 가리키는 구조체를 r1에서 r12쪽으로 로드

- 레지스터를 이용하여 이 구조체는 시스템 컨트롤 레지스터부터의 메모리 컨트롤러 오프셋 0x3010에 쓰여짐


- 이 작업이 완료되면 새로운 메모리맵이 활성화 됨

- SRAM 뱅크가 현재 활성화 되어있음

- 플래시롬은 상위주소로 설정되어 있음


- 펌웨어의 다음 단계로 분기. ARM 파이프라인을 이용하여 구현

=> 새로운 메모리 환경이 활성화 되었더라도 다음 명령어는 이미 파이프라인안에 로드되어 있음.

=> r14의 내용(sandstone_init2의 주소)을 pc에 복사하여 다음 루틴을 호출할 수 있음

=> "MOV" 명령어로 구현


> 3단계 정리 <

1. 메모리 재배치 수행

2. pc는 다음 단계를 가리킴. 이 주소는 새로 리맵된 플래시롬에 위치


# 10.2.2.4. 4단계 : 통신 하드웨어 초기화

- 통신 초기화는 시리얼 포트를 설정하고 표준 배너를 출력하는 작업을 포함

- 배너 : 펌웨어가 완전히 기능적이고 메모리가 성공적으로 리매핑 되어있다는 것을 보여주기 위해 사용


- 시리얼포트 : 9600bps

- 패리티비트 없음

- 정지비트 하나

- 흐름제어 없음

* 시리얼 케이블이 보드에 연결되어 있다면, 호스트 터미널 역시 이러한 설정값으로 설정이 되어야 함


> 결과

- 위의 설정대로 시리얼 포트가 초기화 됨

- 시리얼 포트를 통해 보내진 Sandstone 배너


# 10.2.2.5. 5단계 : 부트로더 - 페이로드 복사 및 제어권 양도

- 페이로드를 복사하여 pc의 제어권을 복사된 페이로드로 넘김

- 블록 복사에 사용될 레지스터 r12, r13, r14 셋업

 * 부트로더 코드는 페이로드가 암호화나 압축이 불필요한 간단한 2진 이미지라고 가정


> 아래의 레지스터를 사용하여 페이로드가 SRAM으로 복사

- r13 : SRAM의 시작 주소. 0x00000000

- r12 : 페이로드 시작 

- r14 : 페이로드 끝


- pc가 복사된 페이로드의 엔트리 주소를 가리키게 하여 pc의 제어권을 페이로드에게 넘김

- 특정 페이로드에 대한 엔트리 포인트의 주소. 0x00000000

=>  페이로드는 시스템을 제어할 수 있는 상태가 됨


> 5단계 정리 <

1. 페이로드가 SRAM에 복사. 0x00000000

2. pc의 제어권이 페이로드로 넘어감. pc = 0x00000000

3. 시스템이 완전히 부팅되면 시리얼 포트를 통해 메시지 출력







'System > Embedded' 카테고리의 다른 글

[ARM] 캐시 2  (0) 2015.11.12
[ARM] 캐시  (0) 2015.11.12
[ARM] 익셉션과 인터럽트 처리 2  (0) 2015.11.10
[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10
[ARM] 최적화된 C프로그래밍  (0) 2015.11.10

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Exception Handling

Interrupts

Interrup Handiling Schemes





### 9.2. Interrupts


> 인터럽트의 두가지 종류

- 외부 주변장치에 의해 exceptions을 발생시키는 Interrupt : IRQ, FIQ

- 특정 명령어로 exceptions을 발생시키는 SWI 명령어



## 9.2.1. Assigning Interrupts

- 인터럽트 할당

- 인터럽트 컨트롤러 : ARM 인터럽트 요청 포트 중 하나에 여러 오부 인터럽트를 연결

복잡한 컨트롤러는 외부 인터럽트 소스가 IRQ나 FIQ exceptions을 발생시킬 수 있도록 프로그래밍 할 수 있음


> 인터럽트 할당에 대한 표준설계 방침

- 소프트웨어 인터럽트는 보통 특권을 가진 OS routin을 위해 예약

 ex. SWI 명령어는 user 모드에서 특권 모드로 변경하는데 사용될 수 있음 (11장)

- IRQ는 보통 범용 인터럽트를 위해 예약

ex. context switching을 위한 주기적인 타이머 인터럽트는 IRQ Exception으로 할당 가능

IRQ Exception은 FIQ Exception보다 낮은 우선순위와 높은 인터럽트 지연시간을 가짐

- FIQ는 보통 빠른 응답시간을 요하는 하나의 인터럽트 소스

ex. 메모리 블럭을 이동하기 위해 사용되는 DMA를 위하여 예약되어있음

* 임베디드 운영체제 설계 시, FIQ Exception은 특정 Application을 위해서만 사용하고,

그 외의 많은 범용 운영 시스템을 위해서는 IRQ를 사용 



## 9.2.2. Interrupt Latecy

- 인터럽트 지연시간 : 외부에서 인터럽트 요청 신호가 들어왔을 때부터 

인터럽트 서비스 루틴(ISR)의 첫 명령어를 수행할 때까지의 시간 간격을 의미


> 인터럽트 지연시간을 줄이기위한 소프트웨어 핸들어의 2가지 방법

1. 중첩을 허용한 인터럽트 핸들러

: 인터럽트 서비스 루틴을 실행하고 있을 때조차 다르 인터럽트가 발생할 수 있도록 허용


2. 우선순위 적용

: 인터럽트 컨트롤러는 인터럽트에 우선순위를 적용하여, 높은 우선순위를 가진 태스크만 중첩 인터럽트를 허용

* 우선순위가 더 높은 인터럽트가 발생하기 전까지 낮은 우선순위의 인터럽트가 처리되는데, 

우선순위가 낮은 인터럽트들은 평균적으로 좀 더 낮은 인터럽트 지연시간을 가지므로,

 시간에 민감항 인터럽트에 대해서는 속도를 향상시켜 시간의 지연을 줄임



## 9.2.3. IRQ and FIQ Exceptions

> IRQ와 FIQ exceptions은 cpsr의 특정 인터럽트 마스크 비트가 0인 경우에만 발생


> IRQ와 FIQ exceptions 발생 시 프로세서 하드웨어의 표준 과정

1. 인터럽트 발생 시 프로세서는 특정 인터럽트 모드로 변환, 인터럽트 발생을 알림

2. 이전 모드의 cpsr값을 새로운 인터럽트 모드의 spsr에 저장

3. 새로운 인터럽트 모드의 lr에 pc값을 저장

4. 인터럽트 disable. 같은 유형의 인터럽트 발생을 막기위한 작업

5. 프로세서는 벡테이블의 특정 엔트리로 분기


> 프로세서가 user모드일 때, IRQ exceptions이 발생한 경우

* cpsr의 IRQ와 FIQ익셉션 비트가 모두 활성화


- IRQ는 프로세서를 두번째 단계로 변환

IRQ 비트가 1이되어 다른 IRQ 익셉션이 발생하지 못하게 함.

FIQ 익셉션은 FIQ를 갖기 때문에 계속 활성화 상태를 유지.

- cpsr 프로세서 모드는 IRQ 모드로 변환. user 모드에서의 cpsr은 자동으로 spsr_irq로 복사

- 인터럽트가 발생하면 레지스터 r13_irq에 pc 값이 저장

- pc는 벡터 테이블의 IRQ 엔트리 +0x18로 설정

- 세번째 단계에서 소프트웨어 핸들러는 인터럽트의 소스를 서비스하기 위한 적절한 인터럽트 서비스 루틴이 호출

- 모든 작업이 완료되면 프로세서 모드는 첫번째 단계의 user 모드로 되돌아감


> FIQ exception의 예

- 세번째 단계의 소프트웨어 핸들러로 진입할 때 IRQ, FIQ 인터럽트 모두 비활성화


-FIQ 모드로 변환할 때, r8에서 r12를 저장할 필요 없음. IRQ모드에서 뱅크됨

- 해당 레지스터들은 bp(buffer pointer)나 counter와 같은 임시데이터를 저장하는데 사용

- 소스가 하나이고 우선순위가 가장높음

- 지연시간이 ㅈ가장 짧은 인터럽트를 서비스 하기에는 FIQ가 가장 이상적임


# 9.2.3.1. FIQ/IRQ exception의 활성화와 비활성화

- ARM 프로세서 코어는 프로세서가 특권모드에 있을때 cpsr을 수정하여 인터럽트를 활성화/비활성화 시킴


FIQ/IRQ 인터럽트를 활성화하는 ARM 명령어 3가지

 MRS

 cpsr의 내용을 레지스터 r1에 복사

 BIC

 IRQ와 FIQ 마스크 비트를 클리어 

 MSR

 r1의 내용을 cpsr로 다시 업데이트. 인터럽트 활성화

* _c는 업데이트 될 비트필드가 cpsr의 제어필드비트[7:0]이라는 것을 의미


 * 인터럽트 요청은 MSR명령어가 파이프라인의 실행단계를 완료했을 떄 활성화/비활성화 됨



* 데이터처리 명령어인 BIC나  ORR 명령어에 상수값으로 0xc0을 사용하면 두 인터럽트를 동시에 활성화/비활성화 할수 있음



## 9.2.4. 기보적인 인터럽트 스택 설계 및 구현

- Exception 핸들러는 상당한 스택을 사용

- 각 sp를 포함하는 레지스터를 가짐


>  Exception을 위한 스택 설계에 고려사항

1. OS의 요구사항 : 각각의 운영체제는 스택 설계시 어떤 요구사항을 가지고 있음

2. 타켓 하드웨어 : 타겟 하드웨어는 스택크기의 물리적인 제한과 메모리 안에서의 스택 위치를 제한


> 스택 설계시 필요한 2가지 결정사항

1. 위치 : 메모리 맵의 어느 위치에서 시작하는지

2. 스택사이크 : 핸들러의 유형/ 중첩/비중첩에 따라 크기가 결정


> 스택 오버플로우를 알아내는 소프트웨어 기술 : 메모리 깨짐 현상이 발생하기전 스택의 보정 작업을 진행

- 메모리 보호장치의 사용

- 각 루틴의 시작에서 스택 체크 함수를 호출


IRQ 모드 스택의 셋업

: 인터럽트 활성화 전, 셋업되어 있어야 함. 

간단한 임베디드 시스템에서는 펌웨어가 부트업 초기 단계에서 스택크기를 할당하기 때문에 최대 크기를 알수 있음


> 전형적인 두가지 메모리 레이아웃


> A

- 코드 세그먼트 아래에 인터럽트 스택이 저장되는 전통적인 레이아웃


> B 

- User 스택 위에 위치한 메모리의 맨 위쪽에 인터럽트 스택이 존재

- A에 비해 스택 오버플로우가 발생해도 벡터 테이블이 깨지지 않음

-> 오버플로우의 원인을 찾아 보정할 기회가 있음


> 스택의 셋업 방법

1. Supervisor mode Stack

- processor core는 supervisor mode에서 시작

- SVC Stack setup을 위해 r13_svc에 SVC_NewStack(스택값)이 가리키는 주소를 저장


LDR    r13,    SVC_NewStack        ; r13_svc

...

SVC_NewStack

DCD    SVC_Stack


2. IRQ mode Stack

- IRQ mode Stack을 setup하기 위해서는 processor mode를 IRQ mode로 변경

- mode변경을 하려면 r2에 cpsr 비트패턴을 저장 후 r2를 cpsr에 저장

-> r13_irq에 액세스가 가능해지며, IRQ_Stack값을 저장하면 됨


MOV     r2, #NoInt|IRQ32md

MSR      cpsr_c, r2

LDR        r13, IRQ_NewStack        ; r13_irq

...

IRQ_NewStack

DCD       IRQ_Stack

3.  User mode Stack

- User mode stack은 보통 가장 나중에 설정

- 프로세서는 user모드를 system 모드로 변경 후, user 모드의 스택을 설정할 수있음

=> system mode와 user mode는 같은 레지스터를 공유해서 사용하기 때문


MOV    r2,    #NoInt|Sys32md

MSR     cpsr_c, r2

LDR      r13, USER_NewStack    ; r13_usr

...

USR_NewStack

DCD    USR_Stack


각 모드별로 분리된 스택의 사용하나의 스택을 사용하여 처리하지 않고 각 모드별로 분리된 스택의 사용하면,

오류가 발생한 시스템의 나머지 부분과 분리하여 디버깅을 수행할 수 있음




### 9.3. Interrupt Handling Schemes

- 인터럽트 처리 방법


- 중첩을 허용하지 않는 인터럽트 처리 방법(Nonnested Interrupt Handler)

- 중첩을 허용한 인터럽트 처리 방법(Nested Interrupt Handler)

- 재진입 인터럽트 처리 방법(Reentrant Interrupt Handler)

- 우선순위를 적용한 간단한 인터럽트 처리 방법(Prioritized Simple Interrupt Handler)

- 우선순위를 적용한 표준 인터럽트 처리 방법(Prioritized Standard Interrupt Handler)

- 우선순위를 적용한 다이렉트 인터럽트 처리 방법(Prioritized Direct Interrupt Handler)

- 우선순위를 적용한 그룹 인터럽트 처리 방법(Prioritized Grouped Interrupt Handler)

- VIC PL190 기반의 인터럽트 서비스 루틴 

 : 벡터 인터럽트 컨트롤러(VIC, Vector Interrupt Controller)



## 9.3.1 중첩을 허용하지 않는 인터럽트 처리 방법

- Nonnested Interrupt Handler

- 한번에 하나의 인터럽트만 처리


1. Disable Interrupt 

- IRQ가 발생하면 ARM프로세서는 다른 인터럽트가 발생하지 못하게 함

- 적절한 IRQ 모드로 프로세서 모드가 변경

- 이전의  cpsr은 새로운 모드의 spsr_[인터럽트 모드]로 복사

- 프로세서는 pc를 벡터 테이블의 해당 엔트리의 값으로 변경하고 그 명령어를 실행

-> pc가 특정 인터럽트 핸들러를 가리키도록 함


2. Save Context

: 현재 프로세서 모드의  레지스터 값들을 저장


3. Interrupt Handler

: ISR을 실행


4. ISR, Interrupt Service Routin

: 해당 인터럽트 서비스 루틴을 실행한 후  인터럽트를 초기화


5. Restore Context

: ISR에서 인터럽트 핸들러로 되돌아간 후, 기존에 저장된 레지스터 값들을 복원


6. Enable Interrupt

: spsr_{인터럽트 모드}를 cpsr로 복사하여 pc는 인터럽트가 발생했던 시점의 다음 명령어를 가리킴


> Example 9.8 <


interrupt_handler    

SUB        r14,     r14    #4                    ; lr

STMFD   r13!,    {r0-r3,    r1,    r14}    ; 레지스터 백업

<ISR>

LDMFD    r13,    {r0-r3,    r2,    pc}^    ; 레지스터 복원 및 복귀


- r14(lr) : 파이프라인 때문에 IRQ 핸들러로의 엔트리에서는 lr의 복귀주소에서 4bytes 떨어진 지점. lr은 스택에 저장되었다가 복원되어 pc로 저장됨

- r0-r3, r12는 ATPCS 때문에 보존되어야 함. ATPCS 호환 서브륀이 핸들러내에서 호출될 수 있도록 함

- STMFD : 사용되는 레지스터 세트들을 스택에 저장(문맥저장). 인터럽트 지연을 줄이기위해 해당 레지스터들의 수를 최소화할 필요가 있음.

레지스터들은 r13_{인터럽트모드}인 SP가 가리키는 스택에 저장장

- 중첩을 허용하지 않는 인터럽트 핸들러 안에서는 spsr을 저장할 필요가 없음. 

이후에 호출되는 인터럽트에 의해 깨지는 일이 없기 때문

- LDMFD : 문맥을 복원한 다음, 인터럽트 핸들러에서 복귀.

- "^" : 명령어에 pc가 나오면 cpsr이 spsr로 복원. 만약 pc가 로드되지 않으면 user  뱅크 레지스터를 복원


<ISR>

LDR    r0,    =IRQStatus    ; 인터럽트 상태 레지스터의 주소

LDR    r0,    [r0]                ; 인터럽트 상태를 알아냄

TST    r0, #0x0080            ; 만약 카운터 타이머라면,

BNE    timer_isr                ;  timer_ISR로 분기

TST    r0,    #0x0001        ; 만약 버튼이 잘못 눌린 것이라면

BNE    button_isr            ; button_IST로 분기

LDMFD    sp!, {r0-r3,    r12,    r14}    ; 문맥복원

LDR    pc,    =debug_monitor    ;둘다아니면 debug_monitor로 분기


- 장점 : 구현 및 디버깅이 상대적으로 쉬움

- 단점 : 다중 우선순위를 가지는 복잡한 임베디드 시스템에서 사용할 수 없음



## 9.3.2. 중첩을 허용한 인터럽트 처리 방법

- Nested Interrupt Handler

- 현재 호출되어있는 핸들러내에서 또 다른 인터럽트가 발생할 수 있도록 해주는 것

- 인터럽트 핸들러가 현재의 인터럽트를 완전히 처리하기 전 인터럽트를 다시 활성화하여 다른 인터럽트를 받아들임

- 복잡도는 증가하지만 성능 향상에 도움

- 목적

1. 인터럽트에 재빨리 반응

2. 다양한 인터럽트들이 서비스되는 동안 규칙적으로 동시에 발생하는 코드의 실행을 지연시키지 않기 위함



- 핸들러를 종료할 때 ISR에 의해 업데이트 되는 플래그를 확인

- 플래그 : 다른 인터럽트를 처리해야 하는지의 여부

없다면 인터럽트 서비스 루틴은 완료. 인터럽트가 있다면 인터럽트를 활성화 시키고 문맥전환 등의 작업을 수행

- 인터럽트 활성화 : IRQ 모드에 SVC나 System 모드로 변환하는 작업을 포함

- IRQ모드에서는 r14_irq만 액세스가 가능, BL 명령어를 사용하여 인터럽트를 발생 했을 때, 

IRQ를 다시 활성화 시키면 레지스터의 새로운 값이 덮여쓰여 값이 깨질 수 있음 (9.3.3.)

- IRQ 스택에 데이터가 있는 동안 핸들러가 문맥전환을 수행할 수 없음

IRQ 스택을 비워야 함. IRQ 스택에 저장되어 있는 모든 레지스터들은 스택, 보통 SVC 스택으로 전송

"Stack Frame" 이라는 스택상에 할당된 메모리 블록으로 전송


> Example 9.9 <


- 스택프레임의 구조

FRAME_LR, FRMAE_PC : 해당 프레임의 맨 마지막에 나오는 레지스터여야 함.

나머지 레지스터들은 "LDMIA"명령어로 복귀할 것이므로 순서는 중요치 않음.

-  사용하는 Applications이나 OS에 따라 스택 프레임 쪽에 붙어야 하는 레지스터들

1. OS가 user 모드와 SVC 모드 둘다 지원해야 하는 경우, r13_user, r14_user를 저장

2. 시스템이 하드웨어 부동소수점을 사용할 때 부동소수점 레지스터들을 저장 

- 여러가지 정의들의 선언

1. 여러가지 cpsr/spsr 변화값을 특정 레이블에 매핑(I_Bit)

2. frame pointer offset으로 다양한 frame registers를 매핑

-> 인터럽트들을 다시 활성화 시키거나 레지스터들이 스택 프레임쪽으로 저장될 때 유용

- 인터럽트 서비스 루틴은 인터럽트 서비스를 수행

- 서비스 완료 후엔 제어권은 핸들러에게 넘어가며 핸들러는 read_RescheduleFlag라는 함수를 호출

또 다른 처리과정이 남아있는지 확인

- 추가 처리사항이 없으면 r0은 0이 아닌 값을 리턴, 추가 처리사항이 있으면 0을 리턴

- r0에서 리턴되는 플래그 값이 확인됨

- 0이 아니라면 문맥을 복원한 후 중단된 태스크에게 제어권이 넘어감.

- 0이라면 처리할 과정이 남아있음을 알려주기 위해, spsr값을 저장하고 해당 복사본은 r2에 저장

- spsr은 코드에서 나중에 나오는 핸들러에 의해 stack frame에 저장될 수 있음

- r13_irq의 값인 IRQ 스택 주소는 나중에 사용되기 위해 r0에 복사

- IRQ stack을 비우기위해 스택의 맨위 6*4 bytes를 더함

=> 아래 방향으로 스택이 자라고 ADD 명령어는 스택 설정을 위해 사용될 수 있기 때문


* 인터럽트 핸들러는 IRQ 스택안의 데이터가 복원될 때까지 인터럽트를 다시 활성화 시키지 않고 비활성화 상태를 유지

cpsr은 r1에 저장되며 프로세서 몯를 SVC 모드로 설정하여 변경

- 새로운 cpsr의 복사본은 나중에 사용하기 위해 r1에 유지

- 스택프레임의 크기만큼 스택을 확장시켜 스택프레임을 생성

r4-r11까지 스택프레임에 저장될 수 있으며, 

r0가 가리키는 IRQ 스택으로부터 남아있는 레지스터들을 복원 할 수 있을만큼 충분한 공간이 됨


- 스택프레임은 해당 표의 정보를 포함

- 프레임 안에 없는 레지스터들만 IRQ 핸들러로 진입할 때 저장


- 기존의 IRQ 레지스터에 상응하는 SVC 모드안에 있는 레지스터들

- 핸들러는IRQ스택에서 모든 데이터를 복원할 수 있고 인터럽트들을 다시 활성화해도 안전할 수 있게됨


- IRQ Exception이 다시 활성화 되고 핸들러는 모든 중요한 레지스터들을 저장

- 핸들러는 스택 프레임을 완료할 수 있음


- 인터럽트 서비스 루틴의 남은 부분이 처리됨

- 현재의 PCB안에 레지스터 r13의 현재 값을 저장 후,

 새로운 태스크의 PCB에 r13을 위한 새로운 값을 로드하면서 문맥전환이 수행됨


* 문맥전환이 발생하면 인터럽트가 발생했던 태스크/핸들러나 또 다른 태스크로 복귀가 가능해짐


- 장점 : 인터럽트 지연을 줄이기 위해 각 인터럽트의 서비스가 완료되기 전에 인터럽트를 활성화 시킬 수 있음

- 단점 : 인터럽트의 우선순위를 다루지 않아, 

낮은 우선순위의 인터럽트가 더 높은 우선순위를 갖는 인터럽트보다 우선적으로 처리될 수 있음



# 9.3.3. 재진입 인터럽트 처리 방법

- Reentrant Interrupt Handler


- 우선순위에 의해 걸러질 수 있는 다중 인터럽트 처리 방법

- 더 높은 우선순위를 가진 인터럽트가 더 낮은 인터럽트 지연을 갖도록 함


- 재 진입 인터럽트 핸들러 안에서 핸들러가 다시 활성화 됨


- "모드변환"의 필요

: 모드 변환이 없이 r14_irq에 복귀 주소를 저장하면, 인터럽트 서비스 루틴이 BL 서브 루틴 호출 명령어를 호출하였을 때,

다음 인터럽트의 복귀주소가 r14_irq에 중첩되어 데이터 깨짐이 발생하게 됨

- 모드는 system 혹은 SVC로 mode로 변경할 수 있음. (r14_svc 레지스터를 이용하면 중첩을 피할 수 있음)


- 처리 과정이 완료되고 인터럽트 소스가 비활성화되기 전에 인터럽트가 cpsr안에서 다시 활성화가 되면,

인터럽트는 즉시 다시 발생 => "race condition"이라는 무한 인터럽트 서비스 루틴 상태에 빠짐

=> 대부분의 인터럽트 컨트롤러는 "인터럽트 마스크 레지스터"를 사용하여,

 하나 이상의 인터럽트들을 마스크 하고 남은 인터럽트를 여전히 활성화 시킬 수 있음

- SVC 모드 안에서 서비스 되므로 인터럽트 스택을 사용하지 않고,

임시로 레지스터 몇개를 저장하기 위한 12 bytes 구조체를 사용하고자 이를 가리키는 r13을 사용됨


> Example 9.10 <


- 장점 : 다른 우선순위를 가진 인터럽트들을 처리할 수 있음

- 단점 : 코드가 더욱 복잡해짐



## 9.3.4. 우선순위를 적용한 간단한 인터럽트 처리 방

- Prioritized Simple Interrupt Handler


- 특정 인터럽트 소스에게 우선순위를 부여

- 우선순위

1. 하드웨어 우선순위 방식 

: 인터럽트 컨트롤러가 현재 서비스를 필요로하는 가장 높은 웃건순위 인터럽트가 무엇인지 알려주기 때문에 간단히 구현이 가능

시스템이 변경됮기 전 인터럽트와 관련 우선순위 테이블 설정이 필요하며 그만큼 초기화 코드가 길어짐

2. 소프트웨어 우선순위 방식

: 외부의 인터럽트 컨트롤러의 추가적인 도움을 필요로 함

마스크를 제어하고 인터럽트 상태 및 소스코드를 읽어내는 최소한의 함수들을 제공해야 함


- IRQRawStatus : 인터럽트 컨트롤러가 가지는 순수한 인터럽트의 상태를 저장하는 레지스터

    * 순수한 인터럽트 상태 : 컨트롤러에 의해 마스크되기 전의 인터럽트 신호의 상태

- IREnable 레지스터 : 어떤 인터럽트가 프로세서로부터 마스크 되어 있는지 결정함

 IRQEnableSet, IRQEnableClear를 통해서 set/clear 됨




> Example 9.11 <


- 장점 : 우선순위 레벨이 어떤 것인지 먼저 알아낸 다음, 더 낮은 우선순위의 인터럽트를 마스크 한 후 서비스 루틴히 호출 됨

=> 인터럽트 지연이 확정적

- 단점 : 낮은 우선순위의 서비스 루틴을 얻는 데 걸리는 시간이 가장 높은 우선순위의 루틴을 위한 것과 같음



# 9.3.5. 우선순위를 적용한 표준 인터럽트 처리 방법

- Prioritized Standard Interrupt Handler


- 우선순위 레벨이 정해진 후, 가장 높은 우선순위의 인터럽트를 찾았을 때, pc 값을 설정하여 즉시 분기

- 인터럽트 레벨을 결정 -> 즉시 더 낮은 우선순위 인터럽트 마스크를 처리하는 루틴으로 분기 

-> 적절한 ISR를 가리키는 분기 테이블에 의해 다시 분기



- 더 높은 우선순위를 가진 인터럽트를 먼저 처리. 우선순위 비교.

일치하는 인터럽트 소스를 찾으면 핸들러는 더 낮은 인터럽트들을 마스크 하기위한 루틴으로 분기

- r14 : 인터럽트 컨트롤러의 베이스를 가리키도록 할당. 동일하거나 더 낮은 순위의 인터럽트의 비활성화를 위함

- r10 : 인터럽트 컨트롤러 상태 레지스터를 로드.

r10은 새로운 마스크를 포함. r12와 AND연산 후, 결과를 IRQEnableClear 레지스터에 저장하여 낮은 우선순위 인터럽트를 0으로 clear

- 핸들러가 다시 사용될 수 있도록 하기위해 pc가 가리키는 현재 주소를 r11에 저장

* 가장 높은 우선순위 인터럽트를 포함


- 장점 : 높은 우선순위의 인터럽트는 외부 인터럽트 마스크를 1로 설정하기 위한 코드의 복사본 없이 더 긴급하게 처리됨

- 단점 : 핸들러는 두번의 분기가 필요. 분기가 필요할 때마다 파이프라인이 지워지기 때문에 시간면에서 손해



# 9.3.6. 우선순위를 적용한 다이렉트 인터럽트 처리 방법

- Prioritized Direct Interrupt Handler


- 처리되는 부분 중 일부가 핸들러에서 각각의 ISR로 이동

- 우선순위를 적용한 direct exception handler는 적절한 ISR로 직접 분기

- 각 ISR은 인터럽트를 다시 활성화 시키기 위해 cpsr을 수정해주기 전, 낮은 순위의 인터럽트의 비활성하는 일을 담당

=> ISR이 마스크 작업을 수행해 주므로 상대적으로 간단,  ISR이 같은 작업을 효율적으로 수정해야 하므로 작은 코드 복사본을 자짐


> 높은 우선순위를 가진 인터럽트를 보다 짧은 시간 안에 처리. 더 짧은 인터럽트 지연시간을 가짐

- 장점 : ISR로 이동하기 위해 한번만 부팅, 여러 사이클이 절약

- 단점 : 각 ISR은 낮은 우선순위의 이너럽트가 현재 ISR을 중ㅈ시키지 못하게 하기위해 외부 인터럽트를 1로 세트



# 9.3.7. 우선순위를 적용한 그룹 인터럽트 처리 방법

- Prioritized Grouped Interrupt Handler


- 그룹별로 한꺼번에 설계

- 여러 인터럽트들을 하나로 묶어서 서브 그룹을 형성 후 적절한 인터럽트 우선순위를 부여하여 구현

- 인터럽트 우선순위를 결정하기 위해 모든 인터럽트들을 검색할 필요가 없어지므로 핸들러의 복잡도 감소



- 장점 : 임베디드 시스템이 아주 많은 인터럽트를 처리하는 경우 유용함

우선 순위 레벨을 정하는 시간이 보다 짧아 반응 시간을 줄일 수 있음

- 단점 : 인터럽트를 어떻게 그룹화 할지를 결정하기 힘듬



# 9.3.8. VIC PL190 기반의 인터럽트 서비스 루틴 

- 벡터 인터럽트 컨트롤러(VIC, Vector Interrupt Controller)

- 벡터 인터러브 컨트롤러의 사용

:  IRQ Vector Entry의 수정이 필요


0x00000018    LDR    pc,    [pc,    #-0xff0]    ; IRQ pc = mem32[0xfffff030]

=> 0xfffff030 번지의 메모리에서 ISR 주소값을 읽어서 pc에 저장


- 하드웨어로부터 인터럽트 소스를 직접 알아낼 수 있음.

- 소프트웨어 인터럽트 핸들러는 거치지 않음

- 특정 ISR로 한번만 분기하면 되므로 인터럽트 지연도 줄어들게 됨


* VIC는 하드웨어 인터럽트 핸들러임. VIC가 활성화 되기 전 ISR주소 목록이 프로그래밍 되어있어야 함


'System > Embedded' 카테고리의 다른 글

[ARM] 캐시  (0) 2015.11.12
[ARM] 펌웨어  (0) 2015.11.12
[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10
[ARM] 최적화된 C프로그래밍  (0) 2015.11.10
[ARM] 32비트 ARM 명령어 2  (0) 2015.11.09

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Exception Handling

Interrupts

Interrup Handiling Schemes





> Exception Handler

: 외부 시스템에 의해 발생되는 에러와 인터럽트, 이벤트들을 처리


- Exception Hanling 

- Interrupt

- Interrupt Handling Scheme 




### 9.1. Exception Handling


# Exception 

- 명령어들의 순차적인 실행 과정을 중단해야하는 상태

Exception 상태 : ARM 코어가 레셋되었을 경우, 명령어를 읽거나 메모리를 액세스 하려다 실패한 경우, 정의되지 않은 명령어를 만났을 경우,

소프트웨어 인터럽트가 발생한 경우, 외부 인터럽트가 발생한 경우 등

- Exception handling : 익셉션을 처리하는 방법

- Exception Handler : 익셉션이 발생한경우 실행되는 소프트웨어 루틴



## 9.1.1. ARM 프로세서 익셉션과 모드


> Exception이 발생하면 코어는

- exception mode의 spsr에 cpsr값을 저장

- exception mode의 lr에 pc값을 저장

- cpsr의 mode bit를 변경하여, 해당 exception mode로 진입

- pc에 exception handler의 주소를 저장하여 해당 주소로 분기



* ARM 프로세서는 ARM 상태로 변환




## 9.1.2. Vertor Table

: exception이 발생하였을 때 ARM 코어가 분기하는 주소 테이블을 의미. 하나의 분기 명령어를 포함


# 분기명령어 형식

 B <address> 

 pc를 기준으로 한 상대 주소로 분기

 LDR   pc,   [pc,   #offset]

 메모리에서 핸들어이 주소를 로드하여 pc에 저장. (벡터 테이블 근처에 저장되어있는 32비트 절대 주소값)

 - 추가적인 메모리 액세스로 약간의 지연시간이발생하지만, 메모리 안에 있는 어떤 주소로도 분기가 가능

 LDR   pc,   [pc,   #-0xff0] 

 0xfffff303에서 특정인터럽트서비스 루틴 줏를 읽어 pc에 저장

 - 벡터 인터럽트 컨트롤러(VIC PL190)이 있을 때에만 사용이 가능

 MOV  pc,  #constant 

 상수값을 pc에 저장. 주소값으로 32비트 주소 공간 내에 있는 모든 값을 사용하는 것이 아니라, 8비트 상수값을 짝수 배만큼 오른쪽으로 로테이트하여 표현할 수 있는 주소값만 사용이 가능 


# Vector Table에서 사용할 수 있는 다른 유형의 명령어

: FIQ Handler는 주소오프셋 +0x1c에서 시작

=> FIQ Handler는 벡터 테이블의 가장 끝에 위치하여 FIQ 벡터 위치에서 바로 시작이 가능



# 전형적인 벡터 테이블

- Undefined Instruction 엔트리 : undefined 핸들러로 분기하는 명령어

- 나머지 다른 벡터들 : LDR 명령을 이용하는 간접 주소 분기 방법을 사용



## 9.1.3. Exception Priorities

Exception Priorities mechanism이 필요한 이유 : Exception은 동시에 발생할 수 있기 때문

- 어떤 exceptions은 cpsr의 I 또는 F 비트를 1로 설정하여 해당 인터럽트가 발생하지 못하게 함


 Reset Exception

 - 가장높은 우선순위

 - reset 핸들러는 메모리와 캐시를 셋업하고 시스템을 초기화 

 - 핸들러가  초기화 되기 전 인터럽트 신호가 활성화되면 예기치 못한 인터럽트가 발생하므로 FIQ, IRQ 인터럽트를 enable하기 전에 외부 인터럽트 솟들을 모두 초기화 해야 함

 - 각 프로세서 모드에 대한 스택 포인터들도 셋업 

 - 핸들러의 처음 명령어 몇 개가 실행되는 동안에는 exception과 interrupt가 발생하지 않는다고 가정

 - exception 신호가 발생하지 않도록 주의하여 핸들러를 구현해야 함

 Data Abort Exception

 - 메모리 컨트롤러나 MMU가 유효하지 않은 메모리 주소가 액세스 되었다는 것을 가리킬 때

 - 현재 코드가 access permision이 없이 메모리 read/write를 시도할 때

 - FIQ exception이 disable되지 않아 FIQ exception이 발생할 수 있음

    -> FIQ exception을 먼저 처리 후 Data Abort 핸들러로 제어권을 다시 넘김

 FIQ Exception

 - 외부 주변 장치가 FIQ 핀을 nFIQ로 설정하였을 경우에 발생 

 - 우선순위가 가장 높음

 - 코어는 FIQ 핸들러로 진입하자마자 IRQ와 FIQ 익셉션을 비활성화 시킴

    -> 어떤 외부 자원들은 프로세서에게 인터럽트를 발생시킬수 없음. 단, 소프트웨어적인 IRQ, FIQ 발생은 가능

 IRQ Exception

 - 외부의 주변 장치가 IRQ 핀을 nIRQ로 설정했을 때 발생

 - 두번째로 높은 우선순위

 - FIQ나 Data Abort Exception이 발생하지 않은 경우 IRQ 핸들러로의 진입이 가능

 - IRQ 핸들러로 진입할 때, IRQ Exception은 disable이 되어, 현재 인터럽트 처리가 끝날 때까지 비활성화를 유지

 Prefetch Abort Exception

 - 메모리로부터 명령어를 읽어오려고 시도하다가 실패한 경우 발생

 - 명령어가 파이프라인의 execute단계에 있고, 우선순위가 높은 다른 exceptions이 발생하지 않았을 때 발생

 - prefetch 핸들러로 진입하면, IRQ Exception은 비활성화, FIQ Exception은 변화없이 유지

 - FIQ Exception이 활성화 되어 발생된다면 Prefetch Exception의 처리 도중이라도 먼저 처리

 SWI Exception

 - 명령어가 실행되고 다른 높은 우선순위의 Exception이 발생하지 않은 경우 발생

 - 이 핸들러로 진입할 때, cpsr은 supervisor 모드로 변경 

 - SWI 호출을 중첩해 사용한다면, lr인 r14와 spsr은 중첩된 SWI로 분기하기 전 반드시 저장 됨

    (lr와 spsr의 손상 가능성을 피하기 위함)

 Undefined Instruction Exception

 - ARM이나Thumb 명령어가 아닌 파이프라인의 실행 단계에 이르렀고, 그 외 우선순위가 높은 다른 Exception이 발생하지 않은 경우에 발생

 - ARM 프로세서는 코프로세서에게 코프로세서 명령어에 이것이 있는지를 물어봄

 - 코프로세서는 파이프라인을 따르기 때문에 명령어의 존재는 "execute" 단계에서 발생

 - 만약 코프로세서가 이 명령어를 이해하지 못하면, Undefined Istruction Exception이 발생


* SWI와 Undefined Instruction Exception은 같은 레벨의 우선순위를 가지며 실행되는 명령어가 SWI 명령어 이면서 Undefined 명령어일 수 없음



## 9.1.4. Link Register Offsets

Exception이 발생하면  lr은 현재 pc값을 기준으로 특정 주소값으로 설정

- IRQ Exception이 발생하면, lr은 마지막으로 실행된 명령어에 8을 더한값을 가리킴

- lr은 Exception Handler를 처리한 후에 복귀를 위해 사용되므로 복귀할 주소는 다음명령어, 즉  lr-4.



# IRQ나 FIQ Exception Handler로 부터 복귀할 수 있는 다양한 방법들


> Example 9.2 : IRQ와 FIQ에서 리턴되는 전형적인 방법인 SUBS 명령어의 사용


hanler

...

SUBS    pc,    r14,    #4    ; pc = r14 -4


: pc값이 결과 레지스터인 경우 cpsr값이 spsr 레지스터로부터 자동 복원

> Example 9.3 : 핸들러의 시작 위치에서 링크 레지스터 r14로부터의 offset만큼 빼는 방법


handler

SUB    r14,    r14,    #4    ; r14 -= 4

...

<핸들러코드>

...

MOVS    pc,    r14        ; return


: 핸들러 루틴 수행을 완료한 후, 원래 코드로 돌아가기 위해 lr인 r14값을 pc에 저장하고, spsr을 cpsr로 복원


> Example 9.4 : lr을 저장하기 위해 인터럽트 스택을 사용하는 방법. 맨 처음 lr로 부터 offset을 뺀 후, interrupt stack에 값을 저장


handler

SUB    r14,    r14,    #4                    ; r14 -= 4

STMFD     r13!,    {r0-r3,    r14}        ; register value 백업

...

<핸들러코드>

...

LDMF    r13,    {r0-r3,    pc}^        ; return


: 원래 코드로 되돌아가기 위해 LDM 명령어가 사용. 명령어 뒤의 ^이라는 심볼은 spsr값을 cpsr로 저장하라는 의미




'System > Embedded' 카테고리의 다른 글

[ARM] 펌웨어  (0) 2015.11.12
[ARM] 익셉션과 인터럽트 처리 2  (0) 2015.11.10
[ARM] 최적화된 C프로그래밍  (0) 2015.11.10
[ARM] 32비트 ARM 명령어 2  (0) 2015.11.09
[ARM] 32비트 ARM 명령어  (0) 2015.11.09

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

C Looping Structures - summary

Register Allocation

Function Calls




### 5.3. 속도 향상을 고려한 C 루프문


> Summary <

효과적인 루프문의 코딩


1. 0으로 다운카운트하는 루프문 사용.

컴파일러는 최종값을 저장하기 위해 레지스터를 할당 할 필요가 없음

0과의 비교작업에 비용이 들지 않음


2. default로 unsigned loop count를 사용하고 반복조건은 i>0보다 i!=0을 사용

루프 오버헤드는 명령어 2개로 줄어듬


3. 루프가 적어도 한 번 이상 실행된다면 do-while 사용

컴파일러가 루프 카운터가 0인지 아닌지를 체크할 필요성을 없애줌


4. 루프 오버헤드를 줄여야 하는 중요한 루프문은 unrolling. (너무 많이 해서도 안됨)

루프 오버헤드가 전체의 일정 비율만큼 작아지면 언롤링은 코드 사이즈를 증가시킴

즉, 캐시 성능에 좋지 않음


5. 배열에서 element의 수는 4나 8의 배수가 되도록 정렬

배열의요소를 추가해야 할 지에 대한 고민하지 않고 루프를 2,4,8배로 쉽게 언롤릴 할 수 있음.




### 5.4. ATPCS에서의 레지스터 매핑


> 효육적인 함수 구현

- spilled(swapped out)되는 변수의 수를 최소화

- 가장 중요하면서 자주 액세스되는 변수들을 레지스터에 저장하도록 함



> 효율적인 레지슽 할당

1. 이론적으로 C 컴파일러는 14개의 변수를 레지스터에 저장할 수 있으나, 실제 몇몇 컴파일러들은 임시 스크래치 작업을 위해 r12같은 고정된 레지스터를 사용하고 있음.

또 복잡한 표현식 계산을 위해 임시 작업용 레지스터들을 필요로 함

=> 함수 내부 루틴에서 사용하는 지역 변수의 수를 12개로 제한하도록 하자.

2. 변수들이 내부 루프에서 사용되고 있는지를 확인하여 중요한 변수를 컴파일러에게 알리도록 하자.




### 5.5. 서브루틴 호출시 매개변수 처리


- APCS(ARM Procedure Call Standard) : 함수 인자와 리턴값을 ARM register로 보내는 방법에 대하여 정의

- ATPCS(ARM-Thumb Procedure Call Standard) : ARM과 Thumb interworking도 다루고 있음



- 처음 4개의 정수 인자는 ARM레지스터의 처음 4개인 r0,r1,r2,r3에 저장

- 다음의 정수 인자는 full descending stack에 저장

- 함수 리턴은 r0으로 보내짐

- long long, double과 같은 두 워드는 인접한 쌍의 연속된 레지스터로 보내지며, 리턴값은 r0, r1로 보내짐

- 구조체를 보낼 수도 있음


# PCS

1. four-register rule

: 4개 또는 그 이하의인자를 가지는 함수에서 모든 인자가 레지스터에 저장될 수 있어 효율적임

C에서 4개의 인자나, C++에서 3개이상의 외부인자가 필요할 경우 구조체가 더 효율적임

(구조체 포인터를 패싱하도록)


2. 작은 함수들은 같은 소스파일 안에 이들을 호출하는 함수 앞에 정의.

: 컴파일러는 함수 호출을 최적화 하거나 작은 함수들을 인라인 시킴, C 컴파일러는 호출될 함수를 위해 생성된 코드를 알 수 있게되어 호출할 함수 안에서 최적화가 가능


- caller는 callee가 중복되지 않는다는 것을 알 수 있어 레지스터들 보존이 필요 없음. 

caller는 ATPCS에서 중복 가능한 레지스터들을 저장할 필요가 없음

- callee가 매우 작다면 컴파일러는 호출하는 함수 안에 그 코드를 인라인 시켜 함수 호출 오버헤드를 완전히 없애줌


3. 크리티컬한 함수는 __inline 키워드로 Caller함수에 인라인 시킴. 

- 단순 정보를 주는 힌트일 뿐 컴파일러가 컴파일 할 때 사용하지는 않음.

- 큰 함수를 인라인 시키는 것은 성능향상에  별 도움이 안되며 코드 사이즈만 증가시키게 됨




'System > Embedded' 카테고리의 다른 글

[ARM] 익셉션과 인터럽트 처리 2  (0) 2015.11.10
[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10
[ARM] 32비트 ARM 명령어 2  (0) 2015.11.09
[ARM] 32비트 ARM 명령어  (0) 2015.11.09
[ARM] ARM 프로세서 개요 2  (0) 2015.11.09

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Data Processing instructions

Branch Instructions

Load-Store Instructions

Software Interrupt Instructions

Program Status Register Instructions

Loading Constants

ARMv5E Extensions

Conditional Execution





### 3.4. SWI 명령어

- Software Interrupt Instructions

- Software Interrupt Exception을 발생시키는 명령어

- Applications이 OS routin을 호출하기 위한 mechanism으로 사용


- "pc = vectors + 0x8" : 프로세서 모드를 "SVC"로 변경하여 OS routin이 특권모드로 호출될 수 있도록 함

- SWI_number : 특정 함수의 호출이나 특징을 나타냄




### 3.5. PSR 명령어

- Program Status Register Instruction

- ARM 명령어 세트는 PSR을 직접 제어하기 위해 MRS, MSR 명령어를 제공

MRS : cpsr이나 spsr의 내용을 레지스터로 전송

MSR : 레지스터의 내용을 cpsr이나 spsr으로 전송



* fields : control(c), extension(x), status(s), flag(f)의 어떤 조합으로 구성


- c fields : 인터럽트 마스크, Thumb 상태, 프로세서 모드를 제어어


> 예제 3.26에서 I 마스크를 클리어 하여 IRQ 인터럽트를 활성화 시키는 방법

1. MSR 명령어로 cpsr값을 r1로 복사

2. BIC 명령어로 r1의 7번째 비트를 0으로 클리어

3. r1을 cpsr로 복사하여 IRQ interrupt를 활성화

* cpsr의 다른 모든 설정값은 유지. control영역의 I 비트만 수정



## 3.5.1. 코프로세서 명령어

- Coprocessor Instruction

- 명령어세트를 확장하기 위해 사용

- 추가의 게산능력을 제공해주거나 캐시와 메모리 관리 장치를 포함한 메모리 서브시스템을 제어하기 위해 사용

- 데이터처리 명령어, 레지스터 전송 명령어, 메모리 전송 명령어

=> 코프로세서에 특화



- cp field : p0에서 p15 사이의 코프로세서의 수

- opcode field : 코프로세서 상에서 발생하게 될 동작

- Cn, Cm, Cd field : 코프로세서 안의 레지스터들을 의미미

- Coprocessor 15(CP15)는 메모리 관리장치, 쓰기버퍼제어, 캐시제어, 고유 레지스터 할당(identification registers) 같은 시스템 제어를 위해 할당됨



## 3.5.2. 코프로세서 15 명령어 표기법 

- Coprocessor 15 Instruction Syntax

- CP15 : 프로세서 코어를 설정하고 설정정보를 저장하기 위한 전용 레지스터 세트를 가짐

=> "시스템 컨트롤 코프로세서"

- MRC와 MCR 명령어로 CP15를 read/write

- Rd : 코어 결과 레지스터

- Cn : 기본 레지스터

- Cm : 보조 레지스터

- opcode2 : 보조레지스터를 수정하는 용도

- 확장 레지스터라는 보조 레지스터가사용되기도 함


"MCR    p15,                    0,                r1,                    c1,                                c0,    0"

"MCR 코프로세서 번호, 무조건 0, 레지스터번호, Coprocessor의 레지스터번호, c0, 무조건0"


=> 간략한 표기법

"CP15:cX:cY:Z"


"CP15    :                    cX    :                                            cY    :                                                            Z"

"코프로세서 번호, 기본레지스터(X:0-15사이의 값), 보조레지스터or확장레지스터(Y:0-15사이의 값), opcode2(0-6)"



참고.  http://dreamlog.tistory.com/221




### 3.6. Loading Constants

: 상수값 로드

: ARM 명령어 세트에서 32-bit 상수값을 레지스터에 저장하는 명령어는 없음

=> 명령어는 32-bit 이기때문에, ARM명령어가 32비트의 상수값을 포함할 수 없음


=> 2가지 의사명령어를 제공하여 ARM은 32-bit value를 레지스터에 저장할 수있게함 (플밍이 쉬워짐)



- LDR : 가능한 명령어들의 조합을 이용하여 32비트값을 레지스터에 저장

만약 상수값을 다른 명령어들의 조합으로 만들 수 없을 땐, 메모리를 직접 액세스하여 읽음

- ADR : 상대 주소를 레지스터에 저장. pc 상대 주소 표현법을 사용하여 해석

pc 상대 덧셈, 뺄셈을 이용하여 주어진 레이블의 주소를 레지스터 Rd에 저장


> 32비트 상수값 0xff00ffff를 레지스터 r0으로 읽는 LDR 명령어

-----------------------------------------

LDR    r0,    [pc,    #const_number-8-{pc}]

.

const_number

DCD    0xff00ffff

-----------------------------------------

- 상수값 로드를 위해 메모리에 액세스 (시간에 민감한 루틴에서는 큰 손실이 발생)


> MVN 명령어를 이용하여 상수 0xff00ffff를 레지스터 r0에 로드

-----------------------------------------

PRE        none...


 MVN    r0,    #0x00ff0000


POST    r0 = 0xff00ffff

-----------------------------------------


- 컴파일러와 어셈블리는 가능한 메모리에서 상수값을 로드하는 걸 피하기 위한 테크닉을 사용

- 한 레지스터안에서 상수값을 만들고, 배럴 시프터를 사용해 확장시키는데 필요한 최적의 명령수를 찾는 알고리즘을 가짐

-  LDR 명령어는 상수값을 만들기 위해 MVN, MOV 명령어를 삽입하거나,

 코드내에 literal pool에서 상수값을 읽기위해 pc 상대 주소를 갖는 LDR 명령어를 만들어 냄


# 의사 명령어의 변환


1. MOV 명령어를 만듬

2. pc상대 주소를 이용한 LDR 명령어를 생성




### 3.7. ARMv5E Extensions

- 새로운 많은 명령어들을 제공

- signed MAC 명령어 : 16비트 데이터를 가지고 처리가 가능한 곱셈계산 명령어로 16비트 데이터조작에 유연성과 효율성을 제공

* MAC : Multiply Accumulate

* 16비트 데이터 조작은 16비트 디지털 신호 처리와 같은 applications를 위해 중요한 요소


- 많은 ARMv5E 제품군에서는 MAC 명령어들이 한 사이클에 실행


> ARMv5E에서 제공하는 새로운 명령어



## 3.7.1. CLZ 명령어

- Count Leading Zeros Instruction

- 최상위 비트에서 처음으로 1이 나온 비트 사이에 0이 몇개나 있는가를 세는데 사용


> Example 3.30 <


PRE        r1 = 0b00000000000000000000000000010000    ; 0이 27번 나온 후 1이 나옴

CLZ    r0,    r1

POST    r0 = 27


* 정규화 루틴에서 유용함


## 3.7.2. 포화 산술 연산

- Saturated Arithmetic

- 보통의 ARM 산술 연산 명령에서는 32비트 정수값에 오버플로우가 발생하면, 제대로된 처리를 못함

ex. 0x7fffffff + 1 = -0x80000000


=> ARMv5E의 명령어를 사용하여 결과값을 포화시킴. 오버플로우가 발생하면 결과는 가장 큰 정수값으로 저장(0x7fffffff)

(오버플로우 체크를 위해 추가적인 코드가 필요없어짐)




## 3.7.3. ARMv5E 곱셈 명령어


- x,y : 32비트 레지스터에서의 첫번째 16비트를 사용할지, 두번째 16비트를 사용할지 선택

상위 16비트 : "T"

하위 16비트 : "B"

- 32bit의 결과를 생성하는 MAC연산에서 Q flag는 덧셈결과(signd 32bit value)에 오버플로우가 발생했는지의 여부를 나타냄

- 향상된 곱셈 명령어는 16비트값의 곱셈에서 나은 유연성을 제공



### 3.8. 조건부 실행

- 대부분의 ARM명령어는 조건부로 실행이 가능

- 주어진 조건이나 task 상황을 만족 할 때만 명령어가 실행되도록 설정해야 함

- 조건부 명령어의 사용은 성능 및 코드의 직접도를 향상

- AL(always) 조건문자 : 명령어 니모닉뒤에 붙어 항상 실행하라는 의미를 가짐


- 조건부의 실행은 분기 상황을 감소시켜 파이프라인이 깨지는 수를 줄여줌

=> 코드의 성능 향상

- 조건 필드와 상태 플래그에 영향을 받음

조건필드 : 명령어상에 위치

상태플래그 : cpsr


- 특정한 알고리즘을 수행하는 데 필요한 명령어의 수를 상당량 줄여줌



'System > Embedded' 카테고리의 다른 글

[ARM] 익셉션과 인터럽트 처리  (0) 2015.11.10
[ARM] 최적화된 C프로그래밍  (0) 2015.11.10
[ARM] 32비트 ARM 명령어  (0) 2015.11.09
[ARM] ARM 프로세서 개요 2  (0) 2015.11.09
[ARM] ARM 프로세서 개요  (0) 2015.11.09

ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Data Processing instructions

Branch Instructions

Load-Store Instructions

Software Interrupt Instructions

Program Status Register Instructions

Loading Constants

ARMv5E Extensions

Conditional Execution





> ARM은 버전에 따라 지원하는 명령어들이 다르지만 하위 호환성을 보장

> 어떤 명령어들은 기능 확장으로 mnemonic이 변경되기도 함

> ARM 명령어는 register안에 들어있는 데이터를 처리

> load-store 명령어만 가지고 메모리 액세스

> 2개 or 3개의 오퍼랜드를 가짐

> 명령어는 4가지로 분류

- Data Processing 명령어

- branch 명령어

- load-store 명령어

- Software interrupt 명령어




### 3.1. Data Processing Instructions


- 레지스터 안에서 데이터를 조작하는데 사용

- 데이터 이동 명령어, 산술 명령어, 논리 명령어, 비교 명령어, 곱셈 명령어

- 대부분의 데이터 처리 명령어에서 오퍼랜드 중 하나를 barrel shifter를 사용하여 처리가 가능

- "S" 접미사 : 명령어 처리 후 결과를 가지고 cpsr 플래그들을 업데이트

- 데이터 이동 연산과 논리연산 후 상태 플래그 업데이트

C : carry. barrel shifter의 결과, 시프트되어 나온 마지막 비트값으로 설정

N : nagative. 결과값의 31번째 비트 값으로 설정

Z : zero. 결과가 0인경우 설정


## 3.1.1. 데이터 이동 명령어

- N 값을 결과 레지스터인 Rd에 복사

* N : 레지스터 값 혹은 상수 값

- 초기값 설정이나 레지스터간에 데이터를 이동하기 위해 사용


## 3.1.2. Barrel Shifter

: 데이터 처리 명령어는 ALU에서 처리되는데, 데이터 처리의 효율을 목적으로 ALU로 입력되기 전 배럴 시프터에 의해 2진 단위의 좌우 시프트가 가능하게 함

-> 많은 데이터 처리 명령어의 강력함가 유연성이 증진


> N : 일반 상수값 or 레지스터값 or 데이터 처리 명령어에 의해 사용기 전 배럴 시프터로 미리 시프트 처리를 한 레지스터 Rm 


> barrel shifter를 사용할 수 없는 데이터 처리 명령어

: MUL(multiply), CLZ(Count Leadming Zero), QADD(signed saturated 32-bit add)



- 선처리나 시프트 작업은 한 명령어의 실행 사이클 내에서 처리

-> 상수 값을 레지스터로 로드하여 2의 거듭자리 만큼 곱하거나 나누기를 할 때 유용함


* LSL : C언어에서의 시프트 연산자 (<<)


# Barrel Shifter에서 사용할 수 있는 5가지 시프트연산

* carry flag : 1 



예제. 

* shift 해서 계산 후, 명령어에 S가 붙어있으므로 상태플래그 캐리비트 활성화



## 3.1.3. Arithmetic Instructions

산술명령어. 32-bit signed/unsigned 값의 덧셈/뺄셈을 구현하기 위해 사용


* ?????, 예제 3.6에서 상태플래그 중 Carry는 왜 1로 설정?



## 3.1.4. 산술 연산에서의 배럴 시프터 사용


* 산술 연산이나 논리 연산에서 두 번째 오퍼랜드의 시프트가 가능하는 점은 ARM 명령어 세트에서 매우 강력한 특징



## 3.1.5. Logical Instructions

> 논리 명령어는 2개의 소스 레지스터에 비트단위로 논리 연산을 수행



* ?????, BIC 예제 잘 이해 안됨. 물어보기.


## 3.1.6. Comparison Instructions

: 32-bit 값을 가진 레지스터를 비교하고 테스트 하기위해 사용


- cpsr 플래그 비트를 업데이트 하며, 다른 레지스터에는 영향을 미지치 않음

- 해당 비트들을 세트한 후, 조건부 실행을 사용해 프로그램의 흐름을 변겨하는데 사용

- 접미사 "S"가 필요하지 않음



* 비교 명령어는 비교되는 레지스터에는 영향을 끼치지 않음.

오직 cpsr의 상태 비트만 업데이트. 



## 3.1.7. Multiply Instructions

: 곱셈명령어. 두 레지스터 안의 내용을 곱하는 명령어


- 명령어에 따라 곱한 결과에 또 다른 값을 더하기도 함

- 62-bit 곱셈명령어(long multiply)는 64비트를 표현하는 2개의 레지스터를 이용하여 곱셈

결과는 하나의 결과레지스터에 저장 or 2개의 레지스터를 이용해 저장


- 곱셈 명령어를 실행하는 데 걸리는 사이클 수는 프로세서마다 다름

- 어떤 프로세서에서는 실행 사이클이 Rs 안에 저장되어 있는 값에 따라 다름


> 64-bit 곱셈 명령어(SMAL, SMULL, UMLAL, UMULL)를 수행하면 64-bit의 결과가 나옴

but, 결과는 32-bit 레지스터에 저장해야 하는데 크기때문에 문제가 됨

=> RdLo, RdHi라는 레이블이 붙은 2개에 레지스터에 각각 결과중 하위 32bits, 상위 32bits가 저장



### 3.2. Branch Instructions

: 실행의 흐름을 변경하거나 어떤 루틴을 호출하는 데 사용


- subroutines, if-then-else 구조, loops 사용이 가능하도록 해줌

- PC가 새로운 주소를 가리키도록 함으로써 실행의 흐름을 바꿈


# ARMv5E 명령어 세트의 4가지 분기 명령어

- label이 가리킼는 주소는 "signed PC - 상대 offset"의 형태로 명령어에 저장

- 분기 명령어의 약 32MB 이내의 주소여야 함

- T : cpsr의 Thumb bit

- BL : 링크값을 갖는 분기로써, LR로 복귀할 주소를 저장하며 서브루틴 호출을 수행하는데 사용

- BX : Thumb 코드로 분기하기 위해 레지스터 Rm에 있는 절대 주소값을 사용

BLX : BX와 유사하며, 최하위 비트값으로 cpsr의 T 비트를 업데이트. 추가로 LR를 복귀할 주소를 저장




### 3.3. Load-Store Instructions

: 메모리와 프로세서 레지스터 사이에서 데이터를 전송


- Single-Register Transfer

- Single-Register Load-Store Addressing Modes

- Multiple-Register Transfer



## 3.3.2. 단일 레지스터 전송 명령어

- Single-Register Transfer

- 메모리->레지스터, 레지스터->메모리, 하나의 데이터를 전송하는데 사용

- 지원되는 데이터형 : signed/unsigned words(32-bit), halfwords(16-bit), bytes(8-bit)

* addressing1, addressing2 : 표 3.5, 표 3.6  참고


- LDR과 STR 명령어는 load되거나 store되는 데이터형 크기와 똑같은 배열로 데이터를 load/store 할 수 있음

(ex. LDR은 0, 4, 8 등으로 메모리 주소상에 32비트 워드를 load할 수 있음)


*예제 3.15. 


; 레지스터 r1이 가리키는 메모리 주소의 내옹을 r0에 로드

LDR    r0,    [r1]        ; LDR    r0,    [r1, #0]


; 레지스터 r0이 가리키는 메모리 주소의 내용을 r1이 가리키는 메모리 주소에 저장

STR    r0,    [r1]        ; STR    r0,    [r1,    #0]


> #0 : offset, r1 : base address register



## 3.3.2. 단일-레지스터 로드-스토어 주소지정방식

- Single-Register Load-Store Addressing Modes

- ARM 명령어 세트는 메모리 주소의 계산을 위해 다양한 모드를 제공하며, 

이 모드는 preindex with writeback, preindex, postindex이라는 indexing methonds 중 하나를 포함하고 있음


* !는 명령어가 계산된 주소를 베이스 주소 레지스터에 저장한다는 의미


* 자동인덱스, 프리인덱스, 포스트인덱스로 변역되어 있음


# 자동인덱스

- auto index

- preindex with writeback

- 베이스 레지스터로부터의 주소에서, 주소 offset만큼을 계산한 후 새로운 주소값으로 베이스 레지스터를 업데이트

- 배열을 검색하는데 사용


# 프리인덱스

- 베이스 레지스터로부터의 주소에서, 주소 offset만큼을 계산한 후 주소 베이스 레지스터를 업데이트 하지는 않음

- 배열을 검색하는데 사용


# 포스트인덱스

- 주소가 사용된 후 주소 베이스 레지스터를 단지 업데이트만 수행 함

- 데이터 구조 안의 요소들을 액세스 하기위해 사용


> Example 3.16 <


pre    r0 = 0x00000000

  r1 = 0x00009000

  mem32[0x00009000] = 0x01010101

  mem32[0x00009004] = 0x02020202


LDR    r0,    [r1,    #4]!


Preindexing with writeback : 

POST[1]    r0 = 0x02020202

 r1 = 00009004


LDR    r0,    [r1,    #4]


Preindexing : 

POST[2]    r0= = 0x02020202

          r1 = 0x00009000


- LDR    r0,    [r1],    #4


Postindexing : 

POST[3]    r0 = 0x01010101

  r1 = 0x00009004


# 32-bit 워드 또는 unsigned 바이트의 load-store를 위해 사용할 수 있는 주소지정방식


- 특별한 load-store 명령어를 사용할 수 있는 주소지정방식은 명령어군에 따라 달라짐

- signed offset or register는 +/- : base address register Rn으로부터의 음/양수 offset

- immediate : base address register + offset(12-bit)을 통해 계산된 주소

- scaled : base address resister + barrel shift를 통해 계산된 주소


# 각각 다르 주소지정방식을 사용한 LDR 명령어의 예


# 단일 레지스터 로드-스토어 주소지정방식, 하프워드, signed 하프워드, signed 바이트, 더블워드

: 16-bit halfword나 signed byte data를 사용하여 load-store 명령어 상에서 사용할 수 있는 주소지정방식


* Table 3.6, Table 3.7의 동작들은 barrel shifter를 사용할 수 없음

- STRH는 signed/unsigned halfword를 둘 다 저장할 수 있어, STRSB나 STRSH가 존재하지 않음

* ????, STRH면 haftwork를 다루는건데, 왜 STRSB같은 바이트를 다루는 명령어를 여기서 이야기 할까

STRB에서 거론해야하는거 아닌가... ? 


# 다양한 STRH 명령어들



## 3.3.3. 다중-레지스터 전송 명령어

- Multiple-Register Transfer

- 명령어 하나로 메모리와 프로세서 사이에서 여러개의 레지스터를 전송

- 전송은 메모리를 가리키는 base address register에서 발생

- 인터럽트 발생 시, 다중-레지스터 전송명령어가 완료 될 때가지 인터럽트가 지연되므로 인터럽트 지연시간을 증대시킬 수 있음

* armcc와 같은 컴파일러는 전송할 레지스터의 최대수를 제어하여, 최대 인터럽트 지연시간을 제한


# 다중 로드-스토어 명령어를 위한 여러가지 주소지정 방식 (*N : 레지스터 리스트 안에 포함된 레지스터의 수)

: 메모리 배열의 앞쪽, 뒤쪽을 액세스가 가능하게 됨 (Stack의 push, pop)


> Example 3.17 <


"LDMIA    r0!,    {r1-r3}"

- "r0" : base register, Rn

- "!" : 명령어 실행 후 Rn에 update

- "-" : 레지스터 범위. 물론 열거해도 됨

- "{}" : 괄호안에 레지스터 각각을 열거해도 됨


- r0은 전 상태에서 메모리 주소 0x80010을 가리킴

- 메모리 주소 0x80010, 0x80014, 0x80018은 각각 1,2,3의 값을 가짐


> LDM명령이 수행된 후 각 r1, r2, r3는 1,2,3 값을 가지게 됨

- base register가 같는 주소는 업데이트 되어 0x8001c를 가짐


> "LDMIB는 Increment Before"

: base address register의 값을 미리 증가를 시킨 후 명령을 수행 ?


> DA, DB는 "decrement"

- 시작 주소를 감소시킨 후 그위의 메모리 영역에 저장

- 메모리 주소의 감소가 아니라 레지스터 리스트가 반대 순서로 액세스 함


# base address가 업데이트 될 때 사용되는 LDM-STM의 쌍

- 레지스터의 값을 임시로 저장하고 복구할 때 유용


> Example 3.18 <  


----------------------------------------

PRE         r0 = 0x00009000

r1 = 0x00000009

r2 = 0x00000008

r3 = 0x00000007


STMIB    r0!,    {r1-r3}


MOV    r1,    #1

MOV    r2,    #2

MOV    r3,    #3

----------------------------------------

- "IB" , "Rn!"이므로

r0의 주소값은 Rn + 4 * N = r0(0x00009000 + 4 * 3) = 0x0000900c 로 없데이트

- r1, r2, r3의 값인 9,8,7을 메모리에 저장

- MOV 명령어로 각각 1,2,3으로 업데이트

----------------------------------------

PRE2       r0 = 0x0000900c

r1 = 0x00000001

r2 = 0x00000002

r3 = 0x00000003


LDMDA    r0!,    {r1-r3}

----------------------------------------

- 레지스터 리스트가 반대 순서로 액세스하며 원래 값을 다시 읽음

- DA로 시작주소를 감소

r0 = Rn - 4 * 3 = 0x0000900c - 12 = 0x00009000

"!"이 있으므로 r0값을 업데이트

----------------------------------------

POST       r0 = 0x00009000

r1 = 0x00000009

r2 = 0x00000008

r3 = 0x00000007

----------------------------------------


> Example 3.19 <  

: 블록메모리 복사를 위한 다중-레지스터 전송 명령어의 사용. 32bytes 복사하기


; r9 - 소스데이터의 시작 위치

; r10 - 결과 데이터의 시작 위치

; r11 - 쓰의 끝 위치


Loop

; 소스의 위치부터 r0-r7(4bytes * 8 = 32bytes)를 로드하여 r9을 업데이트 (복사할 다음 블럭의 위치를 저장해야하므로 "!")

LDMIA    r9!,    {r0-r7}


; 결과데이터를 32bytes 저장하고 r10 업데이트 (결과를 저장할 다음 목적지 영역을 저장해야하므로 "!")

STMIA    r10!,    {r0-r7}


; 소스의 끝인지 체크

CMP    r9, r11     ; r9, r11을 비교

BNE    loop        ; Branch Not Equal. CMP 명령어가 상태 플래그를 not equal로 설정하면 분기 명령어 실행

   ; 업데이트 된 상태명령어가 not equal이 아니면 루틴이 끝남



# 3.3.3.1. Stack Operations


- 다중-레지스터 전송 명령어를 사용

- Pop : LDM, Push : STM

- ascending 스택 : 스택을 메모리 상위 주소 뱡향으로 증가

  descending stack :스택을 메모리 하위 주소 방향으로 증가

- full stack(F) : sp에 마지막으로 사용된 위치 저장(마지막 아이템)

  empty stack(E) : sp에 처음 사용되지 않은 위치를 저장(마지막 아이템 다음의 빈공간)


- stack 동작을 지원하기 위한 다양한 다중-레지스터 전송 주소지정방식이 있음

- ARM에서는 루틴의 호출과 레지스터들의 할당을 ATPCS에 정의하고 있음

* ATPCS : ARM-Thumb Procedure Call

- ATPCS에서 stack을 full descending stack으로 정의

- pop, push를 LDMFD, STMFD 명령어로 구현


> 스택 동작을 위한 어드레싱 방법


> STMFD 명령어 : full stack push 동작과 empty stack  push 동작



> Stack 처리를 위해 보존해야하는 3가지 인자

- Stack Base : 메모리 상에서 스택의 시작 위치

- Stack Pointer : 초기에는 스택 베이스를 가리키지만 데이터가 스택으로 들어가면 메모리 하단으로 이동하며 스택의 가장 맨 위를 계속 가리킴

- Stack Limit : 스택 포인터가 스택 리미트를 넘어가면 Stack Overflow가 발생


* Stack Overflow의 체크


SUB    sp,    sp, #size

CMP    sp,    r10

BLLO_stack_overflow        ; 조건


- ATPCS에서는 r10 레지스터를 "sl(stack limit)"으로 정의

- BLLO : 분기 명령어 "BL"  +  조건인자 "LO" 

=> sp가 r10보다 작으면 overflow 발생

=> sp가 stack base보다 작아질 때도 오버플로우 발생



## 3.3.4. Swap Instruction

: 메모리의 내용과 레지스터의 내용을 바꾸어주는 명령어


- 단일 수행 명령어

: 명령어의 처리가 끝날 떄까지 다른 명령어가 그 영역을 read/write 하지 못하도록 같은 버스를 사용해 어떤 영영을 read/write 함

=> "버스를 잡고있다."라고 표현


- OS안에서 세마포어나 뮤텍스 구현에 유용함

- 표기법에서 인자B(바이트 단위를 나타냄)를 사용할 수 있기때문에, 워드나 바이트 단위의 데이터 교환에 유용








'System > Embedded' 카테고리의 다른 글

[ARM] 최적화된 C프로그래밍  (0) 2015.11.10
[ARM] 32비트 ARM 명령어 2  (0) 2015.11.09
[ARM] ARM 프로세서 개요 2  (0) 2015.11.09
[ARM] ARM 프로세서 개요  (0) 2015.11.09
[ARM] ARM 임베디드 시스템  (0) 2015.11.08

+ Recent posts