ARM System Developer's Guide  

- Designing and Optimizaing System Software




#####

Data Processing instructions

Branch Instructions

Load-Store Instructions

Software Interrupt Instructions

Program Status Register Instructions

Loading Constants

ARMv5E Extensions

Conditional Execution





> ARM은 버전에 따라 지원하는 명령어들이 다르지만 하위 호환성을 보장

> 어떤 명령어들은 기능 확장으로 mnemonic이 변경되기도 함

> ARM 명령어는 register안에 들어있는 데이터를 처리

> load-store 명령어만 가지고 메모리 액세스

> 2개 or 3개의 오퍼랜드를 가짐

> 명령어는 4가지로 분류

- Data Processing 명령어

- branch 명령어

- load-store 명령어

- Software interrupt 명령어




### 3.1. Data Processing Instructions


- 레지스터 안에서 데이터를 조작하는데 사용

- 데이터 이동 명령어, 산술 명령어, 논리 명령어, 비교 명령어, 곱셈 명령어

- 대부분의 데이터 처리 명령어에서 오퍼랜드 중 하나를 barrel shifter를 사용하여 처리가 가능

- "S" 접미사 : 명령어 처리 후 결과를 가지고 cpsr 플래그들을 업데이트

- 데이터 이동 연산과 논리연산 후 상태 플래그 업데이트

C : carry. barrel shifter의 결과, 시프트되어 나온 마지막 비트값으로 설정

N : nagative. 결과값의 31번째 비트 값으로 설정

Z : zero. 결과가 0인경우 설정


## 3.1.1. 데이터 이동 명령어

- N 값을 결과 레지스터인 Rd에 복사

* N : 레지스터 값 혹은 상수 값

- 초기값 설정이나 레지스터간에 데이터를 이동하기 위해 사용


## 3.1.2. Barrel Shifter

: 데이터 처리 명령어는 ALU에서 처리되는데, 데이터 처리의 효율을 목적으로 ALU로 입력되기 전 배럴 시프터에 의해 2진 단위의 좌우 시프트가 가능하게 함

-> 많은 데이터 처리 명령어의 강력함가 유연성이 증진


> N : 일반 상수값 or 레지스터값 or 데이터 처리 명령어에 의해 사용기 전 배럴 시프터로 미리 시프트 처리를 한 레지스터 Rm 


> barrel shifter를 사용할 수 없는 데이터 처리 명령어

: MUL(multiply), CLZ(Count Leadming Zero), QADD(signed saturated 32-bit add)



- 선처리나 시프트 작업은 한 명령어의 실행 사이클 내에서 처리

-> 상수 값을 레지스터로 로드하여 2의 거듭자리 만큼 곱하거나 나누기를 할 때 유용함


* LSL : C언어에서의 시프트 연산자 (<<)


# Barrel Shifter에서 사용할 수 있는 5가지 시프트연산

* carry flag : 1 



예제. 

* shift 해서 계산 후, 명령어에 S가 붙어있으므로 상태플래그 캐리비트 활성화



## 3.1.3. Arithmetic Instructions

산술명령어. 32-bit signed/unsigned 값의 덧셈/뺄셈을 구현하기 위해 사용


* ?????, 예제 3.6에서 상태플래그 중 Carry는 왜 1로 설정?



## 3.1.4. 산술 연산에서의 배럴 시프터 사용


* 산술 연산이나 논리 연산에서 두 번째 오퍼랜드의 시프트가 가능하는 점은 ARM 명령어 세트에서 매우 강력한 특징



## 3.1.5. Logical Instructions

> 논리 명령어는 2개의 소스 레지스터에 비트단위로 논리 연산을 수행



* ?????, BIC 예제 잘 이해 안됨. 물어보기.


## 3.1.6. Comparison Instructions

: 32-bit 값을 가진 레지스터를 비교하고 테스트 하기위해 사용


- cpsr 플래그 비트를 업데이트 하며, 다른 레지스터에는 영향을 미지치 않음

- 해당 비트들을 세트한 후, 조건부 실행을 사용해 프로그램의 흐름을 변겨하는데 사용

- 접미사 "S"가 필요하지 않음



* 비교 명령어는 비교되는 레지스터에는 영향을 끼치지 않음.

오직 cpsr의 상태 비트만 업데이트. 



## 3.1.7. Multiply Instructions

: 곱셈명령어. 두 레지스터 안의 내용을 곱하는 명령어


- 명령어에 따라 곱한 결과에 또 다른 값을 더하기도 함

- 62-bit 곱셈명령어(long multiply)는 64비트를 표현하는 2개의 레지스터를 이용하여 곱셈

결과는 하나의 결과레지스터에 저장 or 2개의 레지스터를 이용해 저장


- 곱셈 명령어를 실행하는 데 걸리는 사이클 수는 프로세서마다 다름

- 어떤 프로세서에서는 실행 사이클이 Rs 안에 저장되어 있는 값에 따라 다름


> 64-bit 곱셈 명령어(SMAL, SMULL, UMLAL, UMULL)를 수행하면 64-bit의 결과가 나옴

but, 결과는 32-bit 레지스터에 저장해야 하는데 크기때문에 문제가 됨

=> RdLo, RdHi라는 레이블이 붙은 2개에 레지스터에 각각 결과중 하위 32bits, 상위 32bits가 저장



### 3.2. Branch Instructions

: 실행의 흐름을 변경하거나 어떤 루틴을 호출하는 데 사용


- subroutines, if-then-else 구조, loops 사용이 가능하도록 해줌

- PC가 새로운 주소를 가리키도록 함으로써 실행의 흐름을 바꿈


# ARMv5E 명령어 세트의 4가지 분기 명령어

- label이 가리킼는 주소는 "signed PC - 상대 offset"의 형태로 명령어에 저장

- 분기 명령어의 약 32MB 이내의 주소여야 함

- T : cpsr의 Thumb bit

- BL : 링크값을 갖는 분기로써, LR로 복귀할 주소를 저장하며 서브루틴 호출을 수행하는데 사용

- BX : Thumb 코드로 분기하기 위해 레지스터 Rm에 있는 절대 주소값을 사용

BLX : BX와 유사하며, 최하위 비트값으로 cpsr의 T 비트를 업데이트. 추가로 LR를 복귀할 주소를 저장




### 3.3. Load-Store Instructions

: 메모리와 프로세서 레지스터 사이에서 데이터를 전송


- Single-Register Transfer

- Single-Register Load-Store Addressing Modes

- Multiple-Register Transfer



## 3.3.2. 단일 레지스터 전송 명령어

- Single-Register Transfer

- 메모리->레지스터, 레지스터->메모리, 하나의 데이터를 전송하는데 사용

- 지원되는 데이터형 : signed/unsigned words(32-bit), halfwords(16-bit), bytes(8-bit)

* addressing1, addressing2 : 표 3.5, 표 3.6  참고


- LDR과 STR 명령어는 load되거나 store되는 데이터형 크기와 똑같은 배열로 데이터를 load/store 할 수 있음

(ex. LDR은 0, 4, 8 등으로 메모리 주소상에 32비트 워드를 load할 수 있음)


*예제 3.15. 


; 레지스터 r1이 가리키는 메모리 주소의 내옹을 r0에 로드

LDR    r0,    [r1]        ; LDR    r0,    [r1, #0]


; 레지스터 r0이 가리키는 메모리 주소의 내용을 r1이 가리키는 메모리 주소에 저장

STR    r0,    [r1]        ; STR    r0,    [r1,    #0]


> #0 : offset, r1 : base address register



## 3.3.2. 단일-레지스터 로드-스토어 주소지정방식

- Single-Register Load-Store Addressing Modes

- ARM 명령어 세트는 메모리 주소의 계산을 위해 다양한 모드를 제공하며, 

이 모드는 preindex with writeback, preindex, postindex이라는 indexing methonds 중 하나를 포함하고 있음


* !는 명령어가 계산된 주소를 베이스 주소 레지스터에 저장한다는 의미


* 자동인덱스, 프리인덱스, 포스트인덱스로 변역되어 있음


# 자동인덱스

- auto index

- preindex with writeback

- 베이스 레지스터로부터의 주소에서, 주소 offset만큼을 계산한 후 새로운 주소값으로 베이스 레지스터를 업데이트

- 배열을 검색하는데 사용


# 프리인덱스

- 베이스 레지스터로부터의 주소에서, 주소 offset만큼을 계산한 후 주소 베이스 레지스터를 업데이트 하지는 않음

- 배열을 검색하는데 사용


# 포스트인덱스

- 주소가 사용된 후 주소 베이스 레지스터를 단지 업데이트만 수행 함

- 데이터 구조 안의 요소들을 액세스 하기위해 사용


> Example 3.16 <


pre    r0 = 0x00000000

  r1 = 0x00009000

  mem32[0x00009000] = 0x01010101

  mem32[0x00009004] = 0x02020202


LDR    r0,    [r1,    #4]!


Preindexing with writeback : 

POST[1]    r0 = 0x02020202

 r1 = 00009004


LDR    r0,    [r1,    #4]


Preindexing : 

POST[2]    r0= = 0x02020202

          r1 = 0x00009000


- LDR    r0,    [r1],    #4


Postindexing : 

POST[3]    r0 = 0x01010101

  r1 = 0x00009004


# 32-bit 워드 또는 unsigned 바이트의 load-store를 위해 사용할 수 있는 주소지정방식


- 특별한 load-store 명령어를 사용할 수 있는 주소지정방식은 명령어군에 따라 달라짐

- signed offset or register는 +/- : base address register Rn으로부터의 음/양수 offset

- immediate : base address register + offset(12-bit)을 통해 계산된 주소

- scaled : base address resister + barrel shift를 통해 계산된 주소


# 각각 다르 주소지정방식을 사용한 LDR 명령어의 예


# 단일 레지스터 로드-스토어 주소지정방식, 하프워드, signed 하프워드, signed 바이트, 더블워드

: 16-bit halfword나 signed byte data를 사용하여 load-store 명령어 상에서 사용할 수 있는 주소지정방식


* Table 3.6, Table 3.7의 동작들은 barrel shifter를 사용할 수 없음

- STRH는 signed/unsigned halfword를 둘 다 저장할 수 있어, STRSB나 STRSH가 존재하지 않음

* ????, STRH면 haftwork를 다루는건데, 왜 STRSB같은 바이트를 다루는 명령어를 여기서 이야기 할까

STRB에서 거론해야하는거 아닌가... ? 


# 다양한 STRH 명령어들



## 3.3.3. 다중-레지스터 전송 명령어

- Multiple-Register Transfer

- 명령어 하나로 메모리와 프로세서 사이에서 여러개의 레지스터를 전송

- 전송은 메모리를 가리키는 base address register에서 발생

- 인터럽트 발생 시, 다중-레지스터 전송명령어가 완료 될 때가지 인터럽트가 지연되므로 인터럽트 지연시간을 증대시킬 수 있음

* armcc와 같은 컴파일러는 전송할 레지스터의 최대수를 제어하여, 최대 인터럽트 지연시간을 제한


# 다중 로드-스토어 명령어를 위한 여러가지 주소지정 방식 (*N : 레지스터 리스트 안에 포함된 레지스터의 수)

: 메모리 배열의 앞쪽, 뒤쪽을 액세스가 가능하게 됨 (Stack의 push, pop)


> Example 3.17 <


"LDMIA    r0!,    {r1-r3}"

- "r0" : base register, Rn

- "!" : 명령어 실행 후 Rn에 update

- "-" : 레지스터 범위. 물론 열거해도 됨

- "{}" : 괄호안에 레지스터 각각을 열거해도 됨


- r0은 전 상태에서 메모리 주소 0x80010을 가리킴

- 메모리 주소 0x80010, 0x80014, 0x80018은 각각 1,2,3의 값을 가짐


> LDM명령이 수행된 후 각 r1, r2, r3는 1,2,3 값을 가지게 됨

- base register가 같는 주소는 업데이트 되어 0x8001c를 가짐


> "LDMIB는 Increment Before"

: base address register의 값을 미리 증가를 시킨 후 명령을 수행 ?


> DA, DB는 "decrement"

- 시작 주소를 감소시킨 후 그위의 메모리 영역에 저장

- 메모리 주소의 감소가 아니라 레지스터 리스트가 반대 순서로 액세스 함


# base address가 업데이트 될 때 사용되는 LDM-STM의 쌍

- 레지스터의 값을 임시로 저장하고 복구할 때 유용


> Example 3.18 <  


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

PRE         r0 = 0x00009000

r1 = 0x00000009

r2 = 0x00000008

r3 = 0x00000007


STMIB    r0!,    {r1-r3}


MOV    r1,    #1

MOV    r2,    #2

MOV    r3,    #3

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

- "IB" , "Rn!"이므로

r0의 주소값은 Rn + 4 * N = r0(0x00009000 + 4 * 3) = 0x0000900c 로 없데이트

- r1, r2, r3의 값인 9,8,7을 메모리에 저장

- MOV 명령어로 각각 1,2,3으로 업데이트

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

PRE2       r0 = 0x0000900c

r1 = 0x00000001

r2 = 0x00000002

r3 = 0x00000003


LDMDA    r0!,    {r1-r3}

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

- 레지스터 리스트가 반대 순서로 액세스하며 원래 값을 다시 읽음

- DA로 시작주소를 감소

r0 = Rn - 4 * 3 = 0x0000900c - 12 = 0x00009000

"!"이 있으므로 r0값을 업데이트

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

POST       r0 = 0x00009000

r1 = 0x00000009

r2 = 0x00000008

r3 = 0x00000007

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


> Example 3.19 <  

: 블록메모리 복사를 위한 다중-레지스터 전송 명령어의 사용. 32bytes 복사하기


; r9 - 소스데이터의 시작 위치

; r10 - 결과 데이터의 시작 위치

; r11 - 쓰의 끝 위치


Loop

; 소스의 위치부터 r0-r7(4bytes * 8 = 32bytes)를 로드하여 r9을 업데이트 (복사할 다음 블럭의 위치를 저장해야하므로 "!")

LDMIA    r9!,    {r0-r7}


; 결과데이터를 32bytes 저장하고 r10 업데이트 (결과를 저장할 다음 목적지 영역을 저장해야하므로 "!")

STMIA    r10!,    {r0-r7}


; 소스의 끝인지 체크

CMP    r9, r11     ; r9, r11을 비교

BNE    loop        ; Branch Not Equal. CMP 명령어가 상태 플래그를 not equal로 설정하면 분기 명령어 실행

   ; 업데이트 된 상태명령어가 not equal이 아니면 루틴이 끝남



# 3.3.3.1. Stack Operations


- 다중-레지스터 전송 명령어를 사용

- Pop : LDM, Push : STM

- ascending 스택 : 스택을 메모리 상위 주소 뱡향으로 증가

  descending stack :스택을 메모리 하위 주소 방향으로 증가

- full stack(F) : sp에 마지막으로 사용된 위치 저장(마지막 아이템)

  empty stack(E) : sp에 처음 사용되지 않은 위치를 저장(마지막 아이템 다음의 빈공간)


- stack 동작을 지원하기 위한 다양한 다중-레지스터 전송 주소지정방식이 있음

- ARM에서는 루틴의 호출과 레지스터들의 할당을 ATPCS에 정의하고 있음

* ATPCS : ARM-Thumb Procedure Call

- ATPCS에서 stack을 full descending stack으로 정의

- pop, push를 LDMFD, STMFD 명령어로 구현


> 스택 동작을 위한 어드레싱 방법


> STMFD 명령어 : full stack push 동작과 empty stack  push 동작



> Stack 처리를 위해 보존해야하는 3가지 인자

- Stack Base : 메모리 상에서 스택의 시작 위치

- Stack Pointer : 초기에는 스택 베이스를 가리키지만 데이터가 스택으로 들어가면 메모리 하단으로 이동하며 스택의 가장 맨 위를 계속 가리킴

- Stack Limit : 스택 포인터가 스택 리미트를 넘어가면 Stack Overflow가 발생


* Stack Overflow의 체크


SUB    sp,    sp, #size

CMP    sp,    r10

BLLO_stack_overflow        ; 조건


- ATPCS에서는 r10 레지스터를 "sl(stack limit)"으로 정의

- BLLO : 분기 명령어 "BL"  +  조건인자 "LO" 

=> sp가 r10보다 작으면 overflow 발생

=> sp가 stack base보다 작아질 때도 오버플로우 발생



## 3.3.4. Swap Instruction

: 메모리의 내용과 레지스터의 내용을 바꾸어주는 명령어


- 단일 수행 명령어

: 명령어의 처리가 끝날 떄까지 다른 명령어가 그 영역을 read/write 하지 못하도록 같은 버스를 사용해 어떤 영영을 read/write 함

=> "버스를 잡고있다."라고 표현


- OS안에서 세마포어나 뮤텍스 구현에 유용함

- 표기법에서 인자B(바이트 단위를 나타냄)를 사용할 수 있기때문에, 워드나 바이트 단위의 데이터 교환에 유용








'System > Embedded' 카테고리의 다른 글

[ARM] 최적화된 C프로그래밍  (0) 2015.11.10
[ARM] 32비트 ARM 명령어 2  (0) 2015.11.09
[ARM] ARM 프로세서 개요 2  (0) 2015.11.09
[ARM] ARM 프로세서 개요  (0) 2015.11.09
[ARM] ARM 임베디드 시스템  (0) 2015.11.08

+ Recent posts