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

+ Recent posts