[OS]/Embedded

[펌] ARM7의 기초

하늘을닮은호수M 2005. 6. 17. 09:32
반응형

Additional Resources: ARM7의 기초

Author: 김 효준
Source: HiTEL Digital Sig.
Date: 2001.3.18


ARM7의 기초

다들 아시다시피 ALTERA의 SOPC전략의 일환으로 나온 Excalibur 제품군에는 ARM9TDMI Core가 내장되어 있습니다... 따라서 ARM Processor에 대한 공부도 해야할 필요가 생기게 되었죠... 그러나 수많은 ARM관련 문서들은 모두 영어로 되어있어서 지식습득에 약간의 어려움이 있는 것도 사실인 것 같습니다... (나만 그런가...?)

그래서 여기에 한글로 된 ARM관련 글을 한편 소개합니다... 제목에서처럼 ARM9이 아닌 ARM7이지만, 어차피 기초적인 내용은 그게그거니깐 공부하시는데에는 별지장이 없을거란 생각입니다... (ARM7에다가 코어내부에 각각 8Kbyte의 데이터와 명령캐시를 덧붙이고, 동작주파수를 200MHz까지 올린게 ARM9이라는군요... 당근, ARM9은 ARM7과 상위호환성이 있습니다...)


목차


ARM7 강좌 [1] : 강좌 소개

* 강좌 소개

RISC칩 중에 가장 널리 사용되고 있는 것의 하나인 ARM7에 대하여 다루어 보려고 합니다. 부족한 점이 많겠지만, 이 강좌를 통해 ARM7을 공부하시는 분들에게 조금이나마 도움이 되었으면 하고, 아울러 저 역시 어떤 보람을 얻을 수 있었으면 합니다.

* 강좌에서 다루고자 하는 내용들

  • 개요
  • 구조
  • 레지스터
  • Exception(1)
  • Exception(2)
  • Instruction Set(1)
  • Instruction Set(2)
  • Instruction Set(3)
  • StrongARM

사실 ARM 아키텍춰의 경우엔 관련 문서가 아주 잘 만들어져 있습니다. http://www.arm.com 사이트에 들어가면 해당 문서를 PDF형태로 받아 볼 수 있고 내용도 아주 잘 정리되어 있습니다. 강좌를 통해 어느정도 기본을 익히고 해당 자료를 찾아 공부하신다면, 빠른 시일내에 ARM을 익힐 수 있으리라 생각됩니다.


ARM7 강좌 [2] : 개요

* ARM7의 특징

ARM의 가장 큰 특징은 전력을 조금만 소모한다는 것입니다. 일설에 의하면, ARM을 설계할 때, 요즘 CPU를 설계하는 방법처럼 VHDL등을 사용한 것이 아니라 일일이 회로를 그려가면서 했다는 설도 있습니다. 사실이야 어떻든, ARM의 저전력 설계기술은 널리 알려진 사실입니다. 이런 이유 때문에, ARM CPU는 상대적으로 전력이 중요하게 여겨지는 휴대용 단말기에 많이 채택되고 있습니다.

이제 이런 일반적인 내용 말고, 좀 구체적인 내용으로 들어가 보겠습니다. 다음은 ARM7의 특징들입니다.

  1. 32 Bit RISC 프로세서
    ARM7은 내부적으로 32Bit의 데이터 버스와 32Bit의 어드레스 버스를 제공합니다. 내부적이란 말은, ARM7이 Core 형태로 여러 종류의 칩에 탑제되기 때문인데, 어떤 칩은 어드레스 라인을 내부적으로는 32비트를 사용하고 외부적으로는 메모리 메니지먼트 유닛을 통해 24비트를 사용하기도 합니다. 그리고 잘 알려져 있듯이 ARM은 대표적인 RISC 코어입니다.
  2. Big / Little Endian 모드지원
    엔디안이라는 것은, 흔히 역워드 방식이라고 말하는 것과 관련이 있습니다. 인텔 계열의 CPU에서는 메모리에 여러바이트의 내용을 저장할 때, 하위 바이트가 먼저 오고 상위 바이트일수록 뒤에 오는데, 이런 형태를 Little Endian이라고 합니다. 또 모토롤라 계열의 CPU처럼 가장 상위 바이트가 먼저 오는 방식이 Big Endian입니다. 해당 설정은 ARM7이 구현된 칩에 따라 틀리지만, 보통 칩외부에서 핀입력을 받아 결정됩니다.
  3. High Performance RISC
    ARM7의 경우 3V를 사용하고 25MHz의 클럭을 사용할 때 약 17MIPS가 나온다고 합니다.
  4. Fast Interrupt Response
    인터럽트 처리를 빠르게 해 줄수 있는 FAST인터럽트 기능이 있습니다. 후에 레지스터 부분에서 자세히 다루겠지만, 레지스터 셋을 따로 제공해서, 인터럽트 처리루틴에서 레지스터를 저장, 복구하는 시간을 줄이는 방식입니다.
  5. Excellent high level language support
    인스트럭션 셋을 살피면, C나 다른 언어에서 일반적으로 사용하는 기능들을 바로 구현할 수 있는 명령어들이 제공됩니다. 예를 들어 변수의 앞, 뒤에 ++나 --를 붙이는 형식의 오퍼레이션을 제공합니다.
  6. Simple & Powerful Instruction Set
    ARM의 명령어는 그 종류가 적으면서도, 다양하게 적용시킬 수 있는 특징이 있습니다. 이후에 다루게 되면 여러분들도 느끼시겠지만, 배우기 쉽고, 또 강력한 기능을 제공합니다.

오늘은 ARM7의 전반적인 내용에 대해 간단히 언급했습니다. 사실 ARM을 개인적으로 사용하기는 쉽지 않은 일입니다. 왜냐하면, ARM을 사용한 칩을 구하기가 쉽지 않기 때문인데요, 최근 CPU인 만큼, 나오더라도 SMD타입으로 나오는 것이 많고 소량으로 구매하기가 쉽지가 않습니다. 하지만, 여러분들이 회사에서 일을 하게 되면, 사정이 달라 집니다. ARM은 비교적 저렴하면서도 가격에 비해 좋은 성능을 보이고, 게다가 저전력 소모라는 강력한 장점을 가지고 있기 때문입니다. 국내 반도체 업계에서도 LG, 삼성, 현대 등 모두 ARM 코어를 사용한 칩을 생산하고 있습니다. 그리고, ARM을 사용할 때의 장점중의 하나는, ARM사에서 개발환경과 도큐먼트 등을 제공한다는 것입니다.

내용은 되도록이면 쉽게 쓰려고 노력했는데, 어땠는지 모르겠군요. 그러면 다음 강좌에서 뵙겠습니다. 다음 강좌에서는 ARM7의 블럭구조에 대해서 살펴볼 생각입니다.


ARM7 강좌 [3] : ARM7의 구조

* ARM7의 구조

  1. 레지스터
    ARM7에는 31개의 32Bit 레지스터가 있습니다. 또, 동작모드에 따르는 - 여기서 동작모드는 Exception부분에서 자세히 다루겠습니다. - 6개의 Status 레지스터가 있습니다.
  2. ALU
    32Bit 연산이 가능한 ALU가 제공됩니다. 그런데 특이한 것은 ALU의 한쪽 입력은 Barrel Shifter라는것이 연결되어 있어서 ALU의 인수 하나는 레지스터에서 바로 들어오고, 다른 하나는 레지스터나 버스에서 Barrel Shifter라는 것을 거쳐 입력되도록 되어 있습니다. 이런 이유로 ARM7에서는 제 2오퍼랜드를 지정할 때, 해당 값을 쉬프트 시켜서 사용할 수 있습니다. 보통 다른 CPU에서는 쉬프트 명령이 따로 있었는데, ARM7에서는 따로 존재하는 것이 아니라 대부분의 명령에서 옵션으로 적용시킬 수 있습니다.
  3. Booth's 곱셈기
    곱셈 기능을 제공하는 32 비트 Booth's 곱셈기가 있습니다. 곱셈기는 32 비트 연산을 지원하며, 32비트의 두 입력을 받아서 곱하여, 결과가 32비트를 넘더라도 넘는 부분은 버리고 32비트만을 남깁니다.

이밖에 인스트럭션 디코더와 인크리멘터가 달린 어드레스 레지스터등이 있고, 내부적으로는 지난번에도 말했듯이 32비트의 어드레스 & 데이터 버스로 연결됩니다. 또, ARM7은 파이프 라인을 제공하고, 추가적으로 스트롱 ARM과 같은 칩에서는 캐쉬 기능과 MMU기능등도 제공합니다.

전에 말씀 드렸지만, ARM7은 CPU의 코어를 말합니다. 즉, 특정 칩을 일?는 것이 아니기 때문에, 흔히 CPU를 접할 때 다루게 되는 I/O제어나 타이머, 인터럽트 부분등은 강좌에서 소개되지 않습니다. 왜냐하면, 같은 ARM7 코어의 CPU라 하더라도 해당 부분들이 각기 다르기 때문입니다.

따라서 여러분들이 ARM7 코어를 사용한 칩을 공부하시고자 할 경우엔, 먼저 이 강좌의 내용인 ARM7 코어에 대한 부분을 공부하고, 다음에 해당 칩의 데이터시트를 보셔야 합니다. 참고로, 제가 공부했던 칩은 샤프에서 나온 LH77790이라는 칩과 StrongARM 코어를 사용한 인텔의 SA1100이라는 칩 두가지 입니다.

스트롱 암은 Digital사에서 ARM7 코어를 라이센스하여 확장한 형태인데, 기본적으로는 거의 유사하고 MMU같은 기능들이 추가가 된 코어입니다. 현재는 Intel이 Digital로부터 라이센스를 구입하여 생산하고 있고, SA110이나 SA1100, SA1111등의 칩으로 제작되어 판매되고 있습니다. 현재 생각으로는 강좌의 끝부분에 가서 StrongARM인 SA1100에 대해서도 조금 다루어 볼 생각입니다.

그럼 오늘 강좌는 이만 줄이도록 하겠구요, 다음 강좌에서는 ARM7의 레지스터에 대한 내용을 다루겠습니다.


ARM7 강좌 [4] : 레지스터

* ARM7의 레지스터

지난 강좌에서 ARM7에는 31개의 General Purpose 레지스터와 6개의 Status 레지스터가 있다고 말씀드렸습니다. 물론 모두 32비트 레지스터 입니다. 그런데 ARM7의 어셈블러에서 사용하는 범용 레지스터 키워드는 r0에서 r15까지 16개 밖에는 되지 않습니다. 즉, 다시 말해서 사용자가 한번에 사용할 수 있는 레지스터는 16개 입니다. 그중에 몇개는 프로그램 카운터(PC) 나 스택 포인터(SP) 등의 용도로 사용됩니다. 나머지 레지스터는 CPU 동작 모드(Exception)와 관련되어 r0-r15로 리맵핑 되어 사용되는데 이는 다시 설명 드리겠습니다.

  1. Special Purpose General Register
    윗부분에서 잠시 설명드렸지만, 유저가 프로그램 할때 레지스터 지정을 위해 사용할 수 있는 키워드는 r0에서 r15까지 입니다. 그중에 몇 가지는 특별한 목적을 위해 사용됩니다.

    - Program Counter (r15)
    : r15는 다른 CPU에서 PC와 같은 역할을 합니다. 다만 차이가 있다면 r15를 일반 다른 레지스터처럼 오퍼랜드로 사용할 수 있다는 점이고 (다른 CPU도 마찬가지 인가요?) ARM어셈블러에서는 pc라는 키워드와 r15를 동일하게 취급합니다.

    - Stack Pointer (r13)
    : ARM7에는 Stack을 위한 명령어가 따로 없습니다. 즉, Push나 Pop등의 명령어가 제공되지 않습니다. 그러나 sp라는 키워드를 사용하여 r13을 쓸 수 있는데, 묵시적으로 r13을 스택포인터로 사용할 수 있도록 정해 놓은 듯 합니다. 그러면 왜 하필이면 r0 나 r1이 아니고 r13을 sp라고 칭하느냐면, 역시 Exception과 관련된 부분이므로 잠시 후에(혹은 다음강좌에) 설명하도록 하지요. 그리고, Push 명령이나 Pop 명령이 없으므로, ARM7에서는 같은 기능을 일반 데이터 전송명령을 통해 해결 합니다. ARM7의 데이터 전송명령은 Auto Increment 기능이 있어서 하나의 인스트럭션으로 Push나 Pop과 동일한 기능을 수행 할 수 있습니다.

    - Link Register (r14)
    : r14는 링크 레지스터라고 부릅니다. 이 레지스터는 8086등에서는 보지 못했던 기능의 레지스터 입니다. 8086등의 프로세서는 서브루틴을 호출할 경우 CALL을 사용하면 다음에 수행될 프로그램 카운터를 스택에 넣고, 호출될 번지를 프로그램 카운터에 넣는 동작을 하는데, ARM7에서는 CALL과 RET와 같은 명령이 없습니다. 대신 Branch with Link 라는 명령(BL) 이 있는데, 해당 명령을 수행하면, CALL과 비슷하게 다음에 수행될 pc(r15)값을 스택이 아니라 lr(r14)에 넣고 분기번지를 pc(r15)에 넣어 분기합니다. 즉, 스택을 사용하지 않는 것이지요. 복귀할 때는 RET대신 mov pc,lr 이라는 데이터 전송명령으로 복귀합니다.
    이런 방식은 나름대로 장단점이 있습니다. 우선 단점을 말하자면, 어떤 서브루틴이 콜 되었을 때, 서브루틴에서는 복귀번지가 r14에 들어있는 상태가 됩니다. 문제는 해당 서브루틴에서 다시한번 다른 서브루틴을 콜 한다면, 원래 r14에 보관되어 있던 복귀 번지값이 덮어씌워지는 결과가 생깁니다. 이런 경우엔, 수동으로 sp(r13)를 이용하여 스택에 r14 값을 보관해 두어야 합니다. 즉, Call하기 전에 r14를 스택에 보관해 두고, 리턴해서 복구하는 과정을 거치는 셈이지요.
    그러면 구태여 왜 이런 방법을 사용할까요. 이미 눈치채신 분들도 계시겠지만, 그렇게 Call을 연속적으로 하는 경우가 아닌, 한번만 Call하는 경우라면, 스택을 사용하지 않고 레지스터를 사용함으로써, 그속도에서 이익을 얻게 되는 것이죠.
    개인적으로는 기능이야 어떻든, Call과 Ret마저 없어서 코드를 읽기가 상당이 좋지 않다는 생각입니다. 코드를 보고 어디서 어디까지가 한 프로시져 인지 쉽게 분간이 안갈 경우가 많거든요.
  1. ARM7 Status Register
    이제 스테이터스 레지스터를 살펴보려고 합니다. ARM7에는 32비트의 스테이터스 레지스터가 6개 있습니다. 그러나 6개 모두를 한꺼번에 사용하지는 못하고, 또 그럴 필요도 없죠. 일단은 하나의 32비트 Status 레지스터만 생각하면 됩니다.
    스테이터스 레지스터는 PSR이라고 부릅니다. 그리고 일반적으로 CPSR이라고 하여 Current Processor Status Register로 부릅니다.
    PSR은 크게 Flag Bits부분과 Control Bits부분으로 나뉩니다.

    - Flag Bits
    : 어떤 인스트럭션의 결과등을 나타내는 부분으로 4 비트가 있습니다. 다른 CPU의 그것과 유사한데, 각각 N, Z, C, V의 4가지 입니다.
    1) Negative / Less Than Flag : N으로 표기되는 이 플래그는 연산의 결과가 마이너스인 경우에 세트됩니다.
    2)Zero Flag : Z으로 표기되는 이 플래그는 연산의 결과가 0이 되었을 경우에 세트됩니다.
    3)Carry / Borrow / Extend Flag : C로 표기되는 이 플래그는 자리올림이나 내림이 발생한 경우, 그리고 Shift 연산 등에서 사용됩니다.
    4) Overflow Flag : V로 표기되는 이 플래그는 연산의 결과가 오버플로우 되었을경우 사용됩니다.
    이상의 Flag Bit들은 다른 칩의 상태 레지스터와 다르지 않습니다. 따라서 이해를 하는데에도 별 무리가 없으리라 생각되며, 더 자세히 알고 싶으시다면, 각 명령어와 관련된 문서를 참조하시길 바랍니다.

    - Control Bits
    : 컨트롤 비트들은 인터럽트를 제어하는 비트와 계속해서 언급되기만하고 실체를 드러내지 않고 있는 Exception과 관련된 CPU 동작모드를 설정하거나 확인할 수 있는 기능을 가진 Bit가 있습니다.
    1) IRQ / FIQ Disable Bit : ARM7의 인터럽트중에서 IRQ와 FIQ를 금지시킬 수 있는 플래그입니다. 인터럽트의 종류는 이밖에도 몇가지가 더 있는데, 그중에서 IRQ, FIQ는 PSR을 통해 금지시키거나 가능하도록 설정할 수 있습니다.
    2) Mode Bits : M0에서 M4까지의 모드 비트는 CPU의 6개 동작 상태를 나타냅니다. 즉, 간단히만 말하자면 ARM7은 6개의 동작 모드를 가지는데, 이를테면 유저모드와 인터럽트 모드등 입니다. 역시 자세한 내용은 다음 강좌를 통해 말씀드리겠습니다.

이제 스테이터스 레지스터를 한번 그려보겠습니다.

N

Z

C

V

...

I

F

...

M4

M3

M2

M1

M0

31

30

29

28

...

7

6

...

4

3

2

1

0

오늘은 ARM7의 레지스터에 대해 기본적인 내용을 알아보았습니다. 하지만 많은 부분에서 ARM7의 동작 모드와 관련되는 부분이 나왔지요. 때문에 다음 시간으로 미룬 부분들이 많군요.

다음 강좌에서는 ARM7 Exception에 대해서 다루려고 합니다. 오늘은 이만 줄이겠습니다.


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 인스트럭션 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)를 사용하여 스택에 넣는다.
    6. .....

    여기서 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(스테이터스 레지스터)

    범용레지스터와 비슷하게 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부분에서 좀 더 알아야 할 몇가지 사항을 간단히 언급하도록 하겠습니다.


ARM7 강좌 [6] : Exception(2)

* Exception Overview

Exception을 처음부터 완전히 이해할 필요가 있다고는 생각하지 않습니다. 대부분의 것들이 그러하듯 우선은 이런게뭐다 라고 파악만 하고 있다면, 정작 필요할 경우에 다시 자세히 살펴보아도 좋을 것입니다.

그런 취지에서 Exception을 바라본다면, ARM7에는 여섯가지의 CPU 동작모드를 지원한다는 사실과, 차후 MMU 같은것에도 그 동작모드가 관여한다는 것, 그리고 각 모드마다 스택을 따로 설정해 준다는 것... 등.

꼭 알아야 할 것이 있다면 IRQ나 FIQ정도가 되겠지요. 흔히 말하는 인터럽트 관련 부분이 바로 이 부분입니다. 실제 코딩을 할 경우에는 CPU에서 정해져 있는 IRQ, FIQ관련 레지스터를 설정하고, 해당 벡터 번지를 바꾸는 정도... 그리고 초기화시에 해당하는 스택 공간을 따로 잡아주는 정도만 신경을 쓰면됩니다.

원래는 각 Exception 모드마다 종료방법이 각기 다름을 설명드리려고 했는데, (구체적으로는 FIQ는 서비스 종료시에 SUBS pc,r14_fiq,#4라는 명령을 사용해야 합니다. 혹은 소프트웨어 인터럽트의 경우엔 MOVS pc,r14_svc를 사용합니다.) 굳이 그런 내용까지 다룰 필요는 없을듯 하다는 생각입니다. 필요한 경우 도큐먼트를 참조하면 자세히 나와 있거든요... 참고로 그렇게 각각 경우에 따라 복귀 방법이 다른 이유는 추측컨데 파이프라인 기능과 관련이 있는것 같습니다.

그러면, 다시 앞으로 돌아가서, Exception과 CPU동작모드의 연관성에 대해 마저 얘기해 보려 합니다. CPU동작 모드는 일반적으로는 Exception에 의하여 바뀌게 되고, User모드가 아니라면 프로그램에 의해 강제로 바꿀수도 있습니다.

전에 살펴보았던 CPSR(스테이터스 레지스터)과 관련되어, CPSR의 모드비트 부분에 현재 CPU의 동작모드를 나타내는 비트들이 있는데, 해당 비트를 변경시킴으로서 프로그램에서 동작모드를 전환할 수 있습니다.

OS를 포팅할 경우 동작모드와 관련하여 메모리 보호기능등도 구현될 수 있습니다.

마지막으로 몇가지 사항만 말씀드리고 오늘 강좌를 마치려 합니다.

우선, Exception간의 우선순위 문제인데, 기본적으로 ARM7은 IRQ, FIQ를 지원하지 않습니다. 해당 인터럽트가 발생하면 CPU는 CPSR의 인터럽트 금지 플래그를 설정합니다. 만약 도중에 인터럽트를 받고 싶다면 사용자가 해당 플래그를 클리어 해주어야 합니다. 또, 동시에 여러개의 Exception이 발생한 경우라면 그 우선순위는 다음과 같습니다.

  1. Reset (가장 높은 우선순위)
  2. Data abort
  3. FIQ
  4. IRQ
  5. Prefetch abort
  6. Undefined Instruction, Software Interrupt

Exception 부분을 설명하다 보니 강좌가 자칫 너무 딱딱해지는 감이 있어서 그냥 이쯤에서 마무리를 지으려 합니다.

다음 강좌부터는 좀더 구체적인 ARM7의 인스트럭션에 대해 다루겠습니다. 명령어가 몇개 되지 않기 때문에 3회 정도로 다룰 생각이구요.

그럼 다음 강좌에 뵙겠습니다.


ARM7 강좌 [7] : Instruction Set(1)

* ARM7 인스터럭션의 특징

AMR7은 32 Bit 코어입니다. 특징적인 것은 모든 명령어가 32Bit 하나의 Word로 구성된다는 것입니다. 8086의 경우엔 명령어에 따라 1바이트 명령부터 5바이트까지 있나요? 그런데, ARM7은 모든 명령어를 한 워드로 처리 합니다. 일단은 명령어의 개수가 몇 안되고, 주소는 상대주소 방식을 사용하며, 심지어는 Immediate 상수값도 32비트 값은 그대로 넣을 수 없습니다.

무슨 얘기냐면, 만약 r0에 32비트 상수를 넣고 싶다면, 몇몇 예외를 제외 하고는 메모리에 미리 넣어두고 해당 메모리를 상대 주소로 참조해서 얻어와야 한다는 뜻입니다.

역시 장단점이 있겠지만, 장점으로는 모든 명령어를 같은 사이즈로 처리함에 따라 파이프라인 구현이 용이하다는 점이 있습니다. 그리고 명령어 해석기를 설계할 경우 예외 처리부분이 없으므로, 쉽고 고속으로 처리할 수 있겠지요.

단점은 앞에서도 간단히 언급했지만 코딩시에 몇몇 제한이 따른다는 점입니다. 상대 주소 지정 방식은, 이때 사용하는 옵셋이 24+2=26비트이므로, 상대주소라고는 하지만 거의 불편이 없고, 다만 Immediate 오퍼랜드를 지정할 경우에 좀 번거롭다는 점이 있습니다. 그러나 8비트의 해상도를 가지는 오퍼랜드라면 한 워드 내에서 처리 가능합니다. 자세한 내용은 다시 말씀드리겠습니다.

ARM 인스터럭션의 다른 특징으로는 모든 명렁어를 조건적으로 실행시킬 수 있다는 것입니다. 저의 경우엔 이부분에서 대단히 감명을 받았는데요. 예를 들어, 8086에서는 jz, jc와 같은 점프 명령을 사용합니다. 그 의미는 제로 플래그가 설정되어 있으면 점프를 해라, 혹은 캐리 플래그가 설정되어 있으면 점프를 해라.. 라는 의미임을 아실 것입니다. 그냥 무조건 점프의 경우엔 jmp를 쓰지요.

ARM의 경우엔 그런 플래그의 사용이 점프명령에 국한되지 않고, 예외없이 모든 명령어에 사용할 수 있습니다.

Ex) BEQ jmp_1 ; Branch if Z flag set to jmp_1
MOVEQ r0, r1 ; r0 := r1 if Z flag set

위의 경우를 보시면 jmp에 해당하는 B명령 뿐만 아니라 MOV와 같은 데이터 전송명령에도 플래그 옵션을 사용했음을 볼 수 있습니다. 이런기능의 장점은 잠깐만 생각해 보아도 알 수 있습니다. 예를 들어 C연산자 중에 ? 연산자를 컴파일 한다고 생각해 봅시다.

Ex) a=(b==c) ? d:e;
(편이상 변수를 레지스터로 바꾸어 생각하겠습니다.)

CMPS r2, r3
MOVEQ r1, r4
MOVNE r1, r5

아직 명령어를 안 다루었으므로 대충 의미는 추측해 보십시요. 8086이랑 대충은 비슷하니까 어려운 일은 아닐 것입니다. r2와 r3를 비교해서, 그결과가 같다면 r1에 r4를 넣고, 그렇지 않다면 r1에 r5를 넣는 코드입니다. 만약 같은일을 8086등에서 하려면 점프 명령이 한개 이상은 들어가야 하겠죠.

다시한번 강조하지만 ARM7에서는 이와같은 조건 옵션을 모든(!) 명령어에 사용 가능합니다. 실제로 모든 OP코드의 상위 4비트는 이런 조건옵션을 나타내는데 사용되는 비트입니다. 각조건에 사용되는 접미사 목록입니다.

0

EQ

Z Set

equal

1

NE

Z Clear

not equal

2

CS

C set

unsigned higher or same

3

CC

C Clear

unsigned lower

4

MI

N Set

negative

5

PL

M Clear

positive or zero

6

VS

V Set

overflow

7

VC

V Clear

no overflow

8

HI

C Set and Z Clear

unsigned higher

9

LS

C Clear or Z Set

unsigned lower or same

10

GE

N Set and V Set or
N Clear and V Clear

greater or equal

11

LT

N Set and V Clear or
N Clear and V Set

less than

12

GT

Z Clear and
ether N Set and V Set or
N Clear and V Clear

greater than

13

LE

Z Set or
N Set and V Clear or
N Clear and V Set

less than or equal

14

AL

Always

15

NV

Never

뒷부분에 가서는 무지 복잡하죠? 저두 치면서 이걸 꼭 쳐야 할 필요가 있을까 생각을 했습니다만, 이왕 시작한거.. 하면서 다 쳤군요. 자세하고 정확한 내용은 다른 문서를 참조하시길 바라고 여기서는 그냥 이런것들이 있구나하는 정도만 알아두십시요.

다음으로 말씀드릴 명령어의 특징은 대부분의 명령어에 S라는 접미사를 사용하여 플래그 레지스터에 영향을 줄지 여부를 결정할 수 있다는 것입니다. 특히 연산명령을 수행할 때 'S'를 붙이면 해당 결과에 따라서 플래그 값들이 변하게 되고, 붙이지 않으면 영향을 미치지 않도록 할 수 있습니다. 8086에서는 연산의 결과에 따라 항상 플래그 값이 영향을 받죠? 여기서는 받지않게 할수도 있다는 점을 말씀드렸습니다. 자세한 내용은 명령어의 세부사항을 참조해야 겠지요.

명령어의 특징을 말씀드리는 중인데, 앞으로도 오퍼랜드 사용등에 대해 말씀드릴것이 많이 남아있네요... 그런데 분량은 점점 늘어나고...

ARM은 지금 말씀드리는 규칙들이 거의 예외없이 적용됩니다. 명령어가 간단한 대신 옵션이 많거든요... 그리고 지금 그 옵션 두가지를 말씀드렸구요.

오늘은 그냥 끝내기는 서운하니까 점프 명령 하나만 말씀드리고 마치겠습니다.

* Branch and Branch with link (B, BL)

Cond

101

L

..........

Cond 부분은 위에서 말씀드린 조건 옵션이구요, 101은 B 명령코드입니다. L 부분은 1일 경우 BL이 되는 것이고 0이면 B 명령입니다. B는 JMP라고 생각하시면 되고, BL은 CALL로 생각하시면됩니다. 다만 BL의 경우엔 PC값을 스택에 넣는것이 아니라 r14(lr)에 넣는다는 것이 차이가 있습니다. 나머지 하위 24비트가 Offset으로 사용되는데, ARM7은 모든 명령어들이 Word단위이므로 총 +/- 32메가 바이트 영역을 커버합니다.

좀 어수선한 느낌입니다만.. 오늘은 이만.


ARM7 강좌 [8] : Instruction Set(2)

이번 강좌에서는 데이터 프로세싱 명령에 대해 다루려 합니다. 해당 명령은 ARM7의 50%정도에 해당하는 명령입니다. 실제 개수는 16개이고, 연산명령과 비교명령, 비트연산명령, 데이터 전송명령 등이 포함됩니다.

* Data Processing Instruction

데이터 전송명령의 형식은 다음과 같습니다.

Cond

00

I

OpCode

S

Rn

Rd

Operand 2

4

2

1

4

1

4

4

12

전에 언급했듯이 모든 인스트럭션의 크기는 32 Bit 입니다. 위의 그림에서 숫자는 해당 필드의 비트 수를 의미합니다.

  1. [31:28] 4 Bit
    해당 명령의 조건 실행 플래그 입니다. 지난 강좌에서 언급했었던 부분인데, 모든 명령어에 포함되므로 데이터 프로세싱 명령에도 포함 됩니다. 혹시나 해서 다시 말씀드리는데, 해당 플래그를 통해 명령을 현재 플래그 래지스터(CPSR)의 상태에 따라 실행 여부를 결정하는데 사용되는 플래그 입니다.
  2. [25] 1 Bit
    Operland 2로 지정되어 있는 부분이 Immediate Operand 인지 아닌지 여부를 나타내는 비트입니다. Immediate Operand라 함은, 예를들어 8086에서 MOV AX,01234h라고 했을 경우, 1234h를 가리키는 말입니다. 자세한 내용은 Operland2를 설명하면서 자세히 다루겠습니다.
  3. [24:21] 4 Bit
    데이터 프로세싱 명령중 어떤 명령인지를 나타내는 필드입니다. 해당 필드와 명령어는 다음과 같습니다.

    Instruction

    Description

    0000

    AND

    Rd := Op1 AND Op2

    0001

    EOR

    Rd := Op1 XOR Op2

    0010

    SUB

    Rd := Op1 - Op2

    0011

    RSB

    Rd := Op2 - Op1

    0100

    ADD

    Rd := Op1 + Op2

    0101

    ADC

    Rd := Op1 + Op2 + C

    0110

    SBC

    Rd := Op1 - Op2 + C - 1

    0111

    RSC

    Rd := Op2 - Op1 + C - 1

    1000

    TST

    Op1 AND Op2 -> CPSR

    1001

    TEQ

    Op1 XOR Op2 -> CPSR

    1010

    CMP

    Op1 - Op2 -> CPSR

    1011

    CMN

    Op1 + Op2 -> CPSR

    1100

    ORR

    Rd := Op1 OR Op2

    1101

    MOV

    Rd := Op2

    1110

    BIC

    Rd := Op1 AND (NOT Op2)

    1111

    MVN

    Rd := NOT Op2

    명령어들의 간단한 설명만으로도 어느정도는 이해할 수 있으리라 생각합니다. 각각의 명령에 대해서는 자세히 다루지 않도록 하겠습니다. 나중에 예제를 보시고 이해할 정도라면 족하다고 생각합니다.

  1. [20] 1 Bit
    S비트가 1인 경우는 데이터 프로세싱 명령의 결과가 CPSR(플래그레지스터)에 영향을 미칩니다. 즉, 0인 경우에는 CPSR은 변하지 않습니다.
  2. [19:16] 4 Bit
    ARM 데이터 프로세싱 명령은 그 결과와 첫번째 오퍼랜드는 항상 레지스터로 지정해야 합니다. Rn은 첫번째 오퍼랜드를 가리키는 것으로 위에서 Op1으로 표기한 것에 해당합니다. ARM에서 한번에 볼 수 있는 범용 레지스터는 sp, lr, pc등을 포함해서 r0에서 r15까지라고 말씀 드렸습니다. 즉, 4Bit를 통해 레지스터를 나타내게 됩니다. 해당 필드는 명령에 따라 사용되지 않기도 합니다. MOV나 MVN등이 이에 해당합니다.
  3. [15:12] 4 Bit
    오퍼레이션의 결과가 저장될 레지스터를 의미합니다. 역시 레지스터를 가리키므로 4비트를 사용하고 모든 명령에서 디폴트로 사용되는 필드입니다. 말씀드렸듯이 ARM의 데이터 프로세싱 명령의 결과는 항상 레지스터로 들어갑니다.
  4. Operand 2 [11:0] 12 Bit
    드디어 오늘의 가장 험난한 산인 Operand 2필드까지 왔군요... 설명 드려야 할 부분이 좀 많거든요. 어떻게 설명을 드려야 할지, 막막하긴 하지만, 용기를 가지고 차근차근 설명해 보도록 하겠습니다.

* 데이터 프로세싱 명령의 Operand 2 필드

오퍼랜드 2의 의미 자체는 별게 아닙니다. 아마도 다들 아시겠지만, ALU에 연산명령을 내릴 경우 한쪽입력은 오퍼랜드 1, 다른쪽은 오퍼랜드 2 그리고 결과가 나오고... 앞서 설명한 내용에 의거하면 다음과 같이 표현할 수 있겠죠?

Op1(Rn) Operand 2
--------------------
/ /
/ /
/ /
//
//
/
ALU/
/
-------------
Rd

예를 들어 AND r1,r2,r3 라는 명령에서 Rd = r1 이고 Rn=r2 입니다. 그리고 r3가 Operand2에 해당하는 부분이 되겠죠. 의미는 r1 = r2 & r3 라는 의미입니다.

여기서 설명하고자 하는 ARM7의 데이터 프로세싱 명령의 Operand2의 타입은 두가지 종류가 있습니다. 필드의 내용에 따라 구분이 되는데, Immediate Operand 혹은 레지스터 Operand입니다.

  1. Register Operand

    필드가 0일 경우엔 Op2가 레지스터임을 의미합니다. 레지스터를 나타내기 위해서는 아시다시피 4비트만 있으면 되죠? 그런데 Op2를 위한 비트 수는 총 12비트입니다. 남는 군요...

    구체적으로 레지스터 오퍼랜드2의 형식을 보여드리겠습니다.

    Shift [11:4]

    Rm [3:0]

    위의 그림을 보시면 Rm이라고 레지스터를 지정하는 4비트가 포함되어 있습니다. 그러면 Shift 부분은 뭘까요?

    강좌의 앞부분에서 언급했었나요? 무슨 쉬프터가 ALU의 한쪽에 달려있다고... 결론적으로는 레지스터를 ALU로 그냥 집어넣는 것이 아니라 Shifter를 통해서 넣어주도록 되어 있습니다. 따라서... Shift라는 필드는 (8비트죠?) 그런 역할을 해주는 것이겠군요. 그럼 왜 8비트일까요? 여기서 다시한번 Shift를 구체적으로 살펴보겠습니다.

    우선 그전에 Shift 필드에는 Shift 회수를 지정하는 방법이 두가지가 있는데, 첫째는 Shift 필드안에 그 회수를 포함시키는 방법이고, 두번째는 Shift 회수가 들어있는 레지스터를 Shift 필드 안에서 지정하는 방법입니다. 경우의 수가 자꾸 늘어가는군요... 해당 타입 두가지는 Shift 필드의 최 하위 비트인 4번 비트에 따라 결정됩니다. 다시 그림을 그려보겠습니다.

    count

    type

    0

    .....

    Rs

    0

    type

    1

    5

    2

    1

    .....

    4

    1

    2

    1

    최 하위 비트의 값이 0이냐 1이냐에 따라 두가지로 구분이 되었습니다. 왼쪽의 경우가 직접 Shift 회수를 지정하는 방법으로, count 부분이 총 5비트로서 그 값을 의미합니다. ARM7의 레지스터는 32비트이므로 5비트는 있어야 Shift 회수를 표현할 수 있겠지요.

    그러면 Type은 뭘까요? 2비트인 Type의 의미는 다음과 같습니다.

    • 00 : logical left (LSL)
    • 01 : logical right (LSR)
    • 10 : arithmetic right (ASR)
    • 11 : rotate right (ROR)

    그렇군요... 단순히 Shift라고 말씀드렸지만 실제로는 좌, 우, 부호의 여부와 Rotate까지 형식을 지정할 수가 있습니다. (무지 복잡해지는 듯한 느낌입니다..^^)

    오른쪽에 레지스터를 지정하는 방식은, 1비트가 남아서 0으로 처리한 것만 제외하고는 별 문제가 없겠죠?

    원래는 여기서 쉬프트 방식 4가지에 대한 설명이 뒤를 이어야 겠지만. 이러다보면 한도 끝도 없을 것 같네요...

    반응형