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

+ Recent posts