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

+ Recent posts