ARM7 강좌 [5] : Exception (1)

 


● Exception


  우선 Exception이 무엇을 말하는지를 이제부터 정리해 보고자 합니다. 일반적으로 인터럽트와 유사한 개념으로 사용합니다. 어떻게 보면 인터럽트보다는 조금 큰 개념이랄 수도 있고, 정확한 정의에 대해서는 말씀을 드리지 못하겠군요. 개념을 ARM7에서의 Exception으로 한정해서 말씀드리겠습니다.


  구체적으로 ARM7에는 FIQ(Fast Interrupt reQuest)와 IRQ(Interrupt reQuest), Abort, Software Interrupt, Undefined Instruction Trap의 5가지 Exception이 있고, 각각 Exception이 발생하면 CPU는 대응하는 동작모드로 전환됩니다. 여기에 보통 동작상태인 User 동작모드가 추가되어 동작 모드는 총 6개가 있습니다.


 - IRQ

    : 일반적으로 I/O 장치로부터의 입력이 들어오면 반응을 하는 흔히 말하는 인터럽트 Exception입니다. IRQ의 종류로는 내부 타이머나 시리얼, 혹은 외부 IRQ입력 등이 될 수 있습니다.

 - FIQ

    : 개념적으로는 IRQ와 거의 유사한데, 다만 보다 빠른 처리를 할 수 있도록 제공되는 Exception입니다. IRQ의 소스는 대부분 FIQ로도 매핑 될 수 있습니다. 즉, 타이머 인터럽트를 IRQ로 처리하거나, 혹은 FIQ로 처리할 수도 있다는 의미입니다.

  - Abort

    : CPU가 메모리로부터 인스트럭션을 가져오거나, 혹은 인스트럭션을 동작시키면서 데이터를 가져오려고 할 경우, 해당 메모리를 억세스 할 수 없다면 Abort Exception이 발생합니다. 위에 제시한 두 가지 경우에 대응하여 Prefetch Abort와 Data Abort로 구분할 수 있습니다.

  - Software Interrupt

    : 프로그램에서 임의로 인터럽트를 호출하는 경우입니다. ARM Instruction SWI가 이에 해당합니다. 이 Exception이 발생하면 CPU동작모드가 Supervisor 모드로 바뀌도록 되어 있고, 보통 OS의 시스템 Call을 구현하기 위해 사용됩니다.

  - Undefined Instruction Trap

    : ARM7 에 정의되어 있지 않은 명령어를 만났을 경우 발생하는 Exception입니다. 이 기능은 코프로세서를 사용하는 경우와 관련되어 사용된다고 합니다.


  위에서 언급한 5종류의 Exception에 몇 가지를 더하여, 해당 처리를 위해 ARM7은 0번지에 벡터 테이블을 유지합니다. 즉, 해당 Exception이 발생하면 정해져 있는 벡터 번지로 실행을 옮깁니다. 해당 번지는 다음과  같습니다.


        Address         Exception               Mode on entry

        -----------------------------------------------------

        0x0000.0000     Reset                   Supervisor

        0x0000.0004     Undefined Instruction   Undefined

        0x0000.0008     Software Interrupt      Supervisor

        0x0000.000C     Abort(prefetch)         Abort

        0x0000.0010     Abort(data)             Abort

        0x0000.0014     --reserved--            --

        0x0000.0018     IRQ                     IRQ

        0x0000.001C     FIQ                     FIQ



  한가지 주의할 점은 8086의 경우엔 해당 벡터에 ADDRESS를 넣어 두면, 인터럽트 발생 시에 해당 주소를 가져다가 PC에 넣어주는 일이 발생하지만, ARM7의 경우엔 그냥 해당 벡터로 점프를 합니다. 예를 들어 IRQ가 발생했다면 다음순간의 pc(r15)값은 0000.0018 이 됩니다. 따라서 해당 벡터 번지에는 단순히 번지가 들어가는 것이 아니라 점프명령 같은 것이 들어갑니다.


● 동작모드


  위의 Exception과 관련되어 CPU의 동작모드 몇 가지가 나타나 있습니다. 전에도 몇 번 말했듯이 ARM7에는 6가지의 동작 모드가 있습니다. 해당 모드는 User Mode, FIQ Mode, IRQ Mode, Supervisor Mode, Abort Mode, Undefined Mode 등의 6가지입니다.


  - 동작모드와 범용 레지스터


    동작모드가 왜 있는 걸까요? ARM7은 각 동작모드에 따라서 몇 가지 기능을 제공합니다. 가장 큰 것으로 레지스터를 리-맵핑시키는 기능이 있습니다. 좀 다르게 설명하면, 각 모드마다 전용 레지스터가 따로 있다고도 표현할 수 있겠는데요, 차근차근 설명해 보도록 하겠습니다.


    CPU의 동작모드는 보통 때는 User 모드입니다. 이 때는 기본적으로 r0에서 r15까지를 사용하고 있겠죠. 그러다 가령 FIQ가 발생했다고 하면 동작 모드가 FIQ 모드로 바뀌게 됩니다. 이와 동시에 r8부터 r14까지의 7개의 레지스터는 FIQ 전용 레지스터로 리-맵핑됩니다. 즉, User모드의 레지스터와는 별개였던 레지스터 7개가 r8부터 r14까지의 위치에 배치되는 것이죠. 물론 기존의 User모드에서 사용하던 r8-r14까지의 레지스터와는 별개의 레지스터입니다.


    이렇게 하는 장점을 생각해 보겠습니다. 흔히 인터럽트 처리루틴을 작성할 경우 인터럽트 발생 시 가장먼저 하는 일이 사용 중이던 레지스터를 스택에 저장하고, 또 처리루틴이 종료될 때는 다시 복구시키는 일이었습니다. 그러나 ARM7에서 FIQ의 경우를 살피면, FIQ 처리 루틴의 코딩 시에는 r8부터 r14까지를 자유롭게 사용할 수 있고, 또 저장과 복구 과정을 생략해도 좋습니다. CPU모드가 바뀜에 따라 레지스터 자체가 별개의 다른 것으로 바뀌었기 때문입니다.


    해당 내용을 정리해 보도록 하겠습니다.


    -----------------------------------------------------

    User        FIQ     Super   Abort   IRQ     Undefined

    -----------------------------------------------------

    r0          .       .       .       .       .

    r1          .       .       .       .       .

    r2          .       .       .       .       .

    r3          .       .       .       .       .

    r4          .       .       .       .       .

    r5          .       .       .       .       .

    r6          .       .       .       .       .

    r7          .       .       .       .       .

    r8          r8_fiq  .       .       .       .

    r9          r9_fiq  .       .       .       .

    r10         r10_fiq .       .       .       .

    r11         r11_fiq .       .       .       .

    r12         r12_fiq .       .       .       .

    r13         r13_fiq r13_svc r13_abt r13_irq r13_und

    r14         r14_fiq r14_svc r14_abt r14_irq r14_und

    r15(PC)     .       .       .       .       .

    -----------------------------------------------------


    위의 그림이 각 모드에 따라 리-맵핑되는 레지스터들을 나타낸 그림입니다. FIQ모드에서는 7개, 그리고 나머지 모드에서는 2개씩의 레지스터가 리-맵핑됩니다.


    이 개념이 이해하기가 좀 어려울지도 모르겠군요. 시간을 가지고 차근차근 생각해 보시길 바랍니다.


    그러면 왜 다른 모드들에서는 r13과 r14를 따로 두었을까요? 그것은 그 레지스터들이 특별한 목적을 위해 사용되는 레지스터이기 때문입니다.


    레지스터차원에서 이번에는 IRQ가 발생한 경우를 가지고 설명해 보겠습니다.


    r14는 Link 레지스터로써 Call과 같은 인스트럭션이 발생할 경우 복귀 할 번지를 저장해 두는 레지스터입니다. 다음은 IRQ 발생 과정입니다.


        1) User모드에서 r0-r15를 사용하고 있다.

        2) IRQ 발생

        3) ARM CPU는 동작모드를 IRQ 모드로 바꾼다.

           ( 이때 r13과 r14는 IRQ 전용 레지스터로 대치된다.)

        4) 이 시점의 PC 값은 IRQ 처리 이후 복귀할 번지이다. 그 값을 r14 (이미 IRQ모드가 되었으므로 r14_irq) 에 넣는다.

        5) 만약에 r0부터 r12까지를 IRQ처리 루틴에서 사용하고자 한다면 해당 레지스터를 sp(r13, 역시 r13_irq)를 사용하여 스택에 넣는다.

           .....


    여기서 5)번 과정을 눈 여겨 볼 필요가 있을 듯 합니다. r13이 스택 포인터로 사용됨은 지난 강좌에서 말씀드렸었습니다. 그런데, 각 모드마다 r13을 따로 가지고 있으므로, 스택을 CPU 동작모드마다 따로 관리할 수 있게 되는 것입니다.


    그림이라도 그려서 설명을 드리면 좋을 듯 한데, 텍스트로만 설명하기가 쉬운 일이 아니군요.


    r14는 복귀번지가 들어가기 때문에 항상 디폴트로 사용되므로 여분의 레지스터가 필요하겠죠. 그런 이유로 r13과 r14를 각 모드마다 따로 둔 것입니다. 혹시 이해가 되시나요?


    FIQ모드는 그 이름에서도 나타나 있듯이 7개의 레지스터를 따로 두어서 레지스터 저장 복구 과정을 거의 생략할 수 있도록 한 것이죠.


    그리고 참고로 Exception에서 복귀할 경우엔 기본적으로는 해당 모드의 r14 번지의 내용을 r15번지로 넣는데, 각 모드마다 조금씩의 차이가 있습니다.


    맨 처음 ARM7 을 소개할 때 범용 레지스터가 31개라고  말씀드렸었는데, 지금 다시 계산을 해보면, User모드의 디폴트 16개 + FIQ 모드 7개 + 나머지 4개의 모드 *2 =8개 해서 16+7+2+2+2+2=31 개로 계산이 됩니다.


  - 동작 모드와 PSR(Status 레지스터)


    범용레지스터와 비슷하게 PSR 역시 동작모드마다 따로 관리가 됩니다. 해당 레지스터는 뒤에 모드 이름을 붙여서 SPSR_fiq, SPSR_svc, SPSR_abt, SPSR_irq, SPSR_und 와 같은 이름으로 부릅니다. 따라서 PSR 개수는 CPSR을 포함하여 총 6개가됩니다.


    SPSR은 CPSR값을 저장해 두는 역할을 합니다. 범용레지스터가 아예 맵핑이 바뀌는데 반해, 모드가 바뀔 경우, 예를 들어 IRQ가 발생했다면, 기존에 User모드에서 사용하는 CPSR값을 SPSR_irq에 저장을 합니다. 그리고 IRQ 모드에서는 CPSR과 SPSR_irq를 둘 다 볼 수 있습니다. 후에 IRQ가 끝나는 시점에서 SPSR_irq의 내용을 CPSR로 복구하면 원래의  CPSR값이 유지되는 것이죠.


    가만히 생각해보면 Exception이 발생했을 때 해당 처리 루틴에는 그 순간의 CPSR값을 그대로 가져오는 셈이지요. 다만 시작할 때 해당 값을 SPSR에 저장해 두었기 때문에 IRQ 처리루틴에서 수정이 된다고 해도, 복귀할 때 SPSR에서 CPSR값을 다시 가져오므로, 실행 중이던 환경은 그대로 유지가 되는 것입니다. 이렇듯 ARM7에서는 Exception처리에 있어 되도록 스택 사용을 최소화하려는 노력을 엿볼 수 있습니다.


 오늘 강좌 내용은 좀 어려웠던 것 같습니다. 다른 CPU에는 없는 개념(?)을 설명하느라 그런 것 같습니다. 더 이상 길어지기 전에 오늘은 이만 줄이려 합니다. 다음 강좌에서는 Exception부분에서 좀 더 알아야 할 몇 가지 사항을 간단히 언급하도록 하겠습니다.


+ Recent posts