1. MIPS 시스템
- 1989년대 스탠포드대학에서 John Hennessy가 그의 동료들과 함께 개발되었다
- Silicon Graphics, Nintendo, Cisco의 제품에서 사용되고 있다.
- 디자인 원리
· 규칙적인 것이 간단성을 위해 좋다
· 많이 발생되는 사항을 빨리 처리한다
· 적을수록 빠르다
· 좋은 설계는 좋은 절충안을 요구한다
2. 설계 원칙 1 - 규칙적인 것이 간단성을 위해 좋다
- 일관성 있는 명령어의 형태
- 같은 수의 피연산자 (두 개의 source와 한 개의 destination)
- 하드웨어로 구현하기 쉽다
- 명령어
1) 덧셈 (Addition)
≫ High-level code : a = b + c;
≫ MIPS assembly code : add a, b, c
2) 뺄셈 (Subtraction)
≫ High-level code : a = b – c;
≫ MIPS assembly code : sub a, b, c
3. 설계 원칙 2 - 많이 발생되는 사항을 빨리 처리한다
- MIPS : 단순하고 많이 사용되는 명령어를 포함함
- 명령어를 해석하고 실행하는 하드웨어는 단순하고 빠르다
- 복잡한 명령어는 여러 개의 단순한 명령어로 수행된다
≫ High-level code : a = b + c – d;
≫ MIPS assembly code : add t, b, c # t = b + c
sub a, t, d # a = t – d
- 컴퓨터 구조 분류
· RICS (Reduced Instruction Set Computer) : MIPS
· CISC (Complex Set Instruction Set Computer) : Inter의 IA-32
4. 설계 원칙 3 - 적을수록 빠르다
- MIPS는 적은 수의 레지스터를 포함한다
- 32개의 레지스터로부터 데이터를 획득하는 것이 1000개의 레지스터 또는 메모리로부터 데이터를 획득하는 것보다 빠르다
1) MIPS 레지스터
(1) 레지스터 세트
레지스터 이름 | 레지스터 번호 | 사용법 |
$0 | 0 | 상수값 0 |
$at | 1 | 어셈블러 임시용 |
$v0 - $v1 | 2 – 3 | 프로시저 리턴 값 |
$a0 - $a3 | 4 – 7 | 프로시저 인자 |
$t0 - $t7 | 8 – 15 | 임시 변수 |
$s0 - $s1 | 16 – 23 | 저장 변수 |
$t8 - $t9 | 24 – 25 | 임시 변수 |
$k0 - $k1 | 26 – 27 | 운영체제 임시용 |
$gp | 28 | 전역 포인터 |
$sp | 29 | 스택 포인터 |
$fp | 30 | 프레임 포인터 |
$ra | 31 | 프로시저 반환 주소 |
(2) 레지스터를 사용한 명령어 예시
≫ High-level code : a = b + c;
≫ MIPS assembly cod : add $s0, $s1, $s2 # $s0 = a, $s1 = b, $s2 = c
≫ High-level code : a = b + c – d;
≫ MIPS assembly code : sub $t0, $s2, $s3 #$s0 = a, $s1 = b, $s2 = c, $s3 = d
add $s0, $s1, $t0
(3) 워드 주소 메모리
① load 명령어 (lw)
- 메모리로부터 레지스터에 1워드를 전송
- 형식 : lw dest offset(base)
- lw $s3, 1($0) # $0 주소에 1더한 곳(00000001)의 데이터를 $3에 넣기
② store 명령어 (sw)
- 레지스터로부터 메모리에 1워드를 전송
- 형식 : sw src, offset(base)
- sw $t4, 0x3($0) # 0x3은 0x000003이므로 $0자리에 더하면 주소가 000003인 곳에 $t4의 값을 넣는다
(4) 바이트 주소 메모리
① load 명령어 (lw)
- 워드 주소 메모리와 같은 형식이지만 한 워드가 4바이트로 바뀐 것이다
- 형식 : lw dest, offset(base)
- lw $s3, 4($0) # base인 $0주소에 4를 더해 주소가 00000004인 곳의 데이터를 $s3에 넣어준다
② store 명령어 (sw)
- 워드 주소 메모리와 같은 형식이지만 한 워드가 4바이트로 바뀐 것이다
- 형식 : sw src, offset(base)
- sw $t7, 12($0) # base인 $0주소에 12를 더해 주소가 0000000C인 곳에 $t7의 값을 넣는다
(5) Big-Endian VS Little-Endian
- Endina : 컴퓨터의 메모리와 같은 1차원 공간에 여러 개의 연속된 대상을 배열하는 방법
① Big-Endian
- 큰 단위부터 들어가는 Endian
- 장점 : 사람이 숫자를 읽고 쓰는 방식과 같아 디버그를 편하게 해준다
- 저장 예시
≫ $t0의 값이 0x23456789일 때, Big-Endian 시스템에서 수행한 후 $s0의 값은?
② Little-Endian
- 작은 단위가 먼저 들어가는 Endian
- 산술 연산이 메모리의 주소가 낮은 쪽에서부터 높은 쪽으로 가면서 처리되는 것과 같음
- 장점 : 첫 바이트만 확인하면 되기 때문에 짝수, 홀수 검사를 할 때 처리 속도가 빠름
- 저장 예시
≫ $t0의 값이 0x23456789일 때, Little-Endian 시스템에서 수행한 후 $s0의 값은?
5. 설계 원칙 4 – 좋은 설계는 좋은 절충안을 요구한다
- 다중 명령어 형태는 융통성을 제공한다
· add, sub : 3개의 레지스터 피연산자 사용
· lw, sw : 2개의 레지스터 피연산자와 상수 사용
- 적은 수의 명령어 형태를 유지한다
1) 기계어
- 명령어들의 이진 표현 (0 또는 1로 표현)
- 32비트 명령어
- 명령어 형태
· R-Type
· I-Type
· J-Type
2) R-Type
- 레지스터 타입
- 3개의 레지스터 피연산자(오파랜드)
· rs, rt : source 레지스터
· rd : destination 레지스터
- 다른 필드
· op : operation 코드 (모든 R-Type 명령어의 op 코드는 0이다)
· funct : function (더하기, 빼기 등의 계산을 구별해줌)
· shamt : shift 명령어에서 사용되는 shift의 양
- 사용 예시
≫ add rd, rs, rt / sub rd, rs, rt
3) I-Type
- 즉시 값(상수 값) 타입 (Immediate)
- 3개의 피연산자 (오퍼랜드)
· rs, rt : 레지스터 피연산자
· mm : 16비트 즉시 값(immediate)
- 다른 필드
· ip : operation 코드
· 각각의 I-Type 명령어는 개별적인 op코드 값을 갖는다
- 사용 예시
≫ Addi rt, rs, imm / lw rt, imm(rs) / sw rt, imm(rs)
4) J-Type
- Jump 타입
- 분기 명령어 형식
- 1개의 피연산자 (오퍼랜드)
· addr : 주소 피연산자
- 다른 필드
· op : operation 코드
6. 기계어 코드 해석
- opcode가 0이면 R-type 명령어이므로 func 비트를 통해 명령어 기능 분석
- opcode가 0이 아니라면 I-Type 또는 J-Type 명령어다
1) 조건부 분기 (beq, branch if equal)
- 형식 : beq register1, register2, L1
- 만약 register1과 register2의 값이 같으면 L1에 해당하는 곳을 분기하라는 의미
- 사용 예시
- 현재 $s0은 0 + 4이므로 4가 값으로 들어 있다. 그리고 $s1은 0 + 1로 1이 값으로 들어있는데 shift를 2번하므로 1 * 2 * 2가 되어 4를 갖고 있다. 그렇기 때문에 $s0과 $s1은 같은 값을 가져 target으로 분기하게 된다. (분기하였기 때문에 beq 아래 코드 2줄은 실행 X)
2) 조건부 분기 (bne, branch if not equal)
- 형식 : bne register1, register2, L1
- 만약 register1과 register2의 값이 다르다면 L1으로 분기하라는 의미
- 사용 예시
- bqe의 예시와 똑같이 현재 #s0은 4라는 값을 갖고 있고 $s1역시 4라는 값을 같고 있다. 하지만 bne는 서로 다른 경우에 분기하므로 target으로 분기하지 않고 아래 코드를 계속 진행한다.
3) 무조건 분기 (j, jump)
- 형식 : j L1
- 이 명령어를 만나면 무조건 L1으로 분기한다
- 사용 예시
- 다른 명령들과 상관없이 j target을 만나면 무조건 target 레이블로 분기한다.
4) 무조건 분기 (jr, jump register)
- 형식 : jr register
- 이 명령어를 만나면 register의 값을 주소로 생각하고 해당 주소로 분기
- 사용 예시
- 현재 $s0은 0x2010이라는 값을 갖고 있다. 그러므로 jr $s0을 만나면 $s0의 값인 0x2010 을 주소로 하여 해당 주소로 이동한다.
5) if문
- if (i == j)에 대한 명령을 i와 j가 같지 않으면 분기해 실행하지 않도록 작성한다
6) if / else 문
- if (i == j)에 대한 명령을 i와 j가 같지 않으면 분기하도록 작성한다. 이때, 분기하지 않아 if문을 실행하게 되면 else문에 대한 명령은 실행하면 안되므로 else 명령 다름으로 분기할 수 있도록 if문 명령을 모두 실행하면 j done 명령을 실행하게 한다.
7) whiel loop
- 조건에 맞기 전까지는 while이라는 레이블에 while문에 해당하는 명령어를 넣어 while 레이블로 계속 분기한다. 만약 조건에 맞을 경우 done을 통해 done을 분기하여 while을 끝낸다.
8) for loop
- 조건에 맞지 않을 때가지 for이라는 레이블에 for문에 해당하는 명령어를 넣어 for 레이블로 계속 분기한다. 만약 조건에 맞을 경우 done을 통해 done을 분기하여 for을 끝낸다.
9) Less Than 비교
- slt (set on less than) : slt $t1, $s0, $t0일 때, $t0보다 $s1이 작다면 $t1 = 1, 아니라면 $t0 = 0이 된다.
< 퀴즈 >
Q1) MIPS 디자인(설계) 원칙으로 옳지 않은 것은? ( )
(1) 규칙적인 것이 간단성을 위해 좋다
(2) 많이 발생되는 사항을 빨리 처리한다
(3) 적을수록 빠르다
(4) 좋은 설계는 절충안을 요구하지 않는다
Q2) 고급 언어에서 a = b + c라는 코드가 있을 때, 다음 중 이 코드와 같은 의미인 MIPS 어셈블리 코드를 고르시오. ( )
* 레지스터 $s0 = a, $s1 = b, $s2 = c 이다)
(1) add $s1, $s0, $s2
(2) add $s0, $s1, $s2
(3) addi $s0, $s1, $s2
(4) addi $s1, $s0, $s2
Q3) 레지스터 이름과 사용법이 잘 매치 되어 있지 않는 것을 고르시오. ( )
(1) $0 – 상수값 0
(2) $t0 – 임시 변수
(3) $gp – 전역 변수
(4) $sp – 스택 포인터
Q4) $t0의 값은 0x98765432이다. 이때, Little-Endian 시스템으로 다음 코드를 수행했을 때 $s0의 값은? ( )
sw $t0, 0($0)
lb $s0, 1($0)
Q5) 다음 형식에 맞는 명령어 타입을 짝지으시오.
a. R-Type b. I-Type c. J-Type |
(1) ( )
(2) ( )
(3) ( )
Q6) 다음 코드에서 색이 칠해진 코드가 실행되면 경우에 따라 어떻게 동작하는지 간단하게 쓰시오.
addi $s1, $0, 0 addi $s1, $0, 0 addi $t0, $0, 12 for : beq $s0, $t0, done add $s1, $s1, $s0 addi $s0, $s0, 1 j for done: |
Q7) 아래 high-level 코드를 보고 MIPS assembly 코드에 뭐가 들어가야 할지 쓰시오.
Hight-level code | MIPS assembly code |
int num = 0; int i; for ( i = 10; i > 0; i = i - 1){ num = num + i; } |
# $s0 = i, $s1 = num addi $s1, $0, 0 addi $s0, $0, 10 addi $t0, $0, 0 loop: ( 1 ) ( 2 ) add $s1, $s1, $s0 sub $s0, $s0, 1 j loop done: |
< 정답 >
A1) (4)
좋은 설계는 절충안을 요구하지 않는 것이 아니라 ‘좋은 절충안을 요구한다’이다.
A2) (2)
우선 add와 addi 중 add가 맞다. 왜냐하면 addi는 정수 가산 명령으로 레지스터에 정수를 더해 저장하는 명령이다. 하지만 위 명령에서는 레지스터와 레지스터를 더하므로 add가 아닌 addi다. 그리고 순서는 add dst, src1, src2이기 때문에 저장될 위치인 $s0이 가장 앞에 와야 한다. (add 명령은 operand로 레지스터만 가질 수 있다)
A3) (3)
$gp는 전역 변수가 아니라 전역 포인터다.
A4) 0x00000054
A5) (1) b. I-Type
(2) c. J-Type
(3) a. R-Type
A6) $s0이 for문을 돌면 12가 되기 전까지는 $t0과 같지 않으므로 그냥 다음 코드로 넘어가고 만약 $s0이 12라면 $t0과 같으므로 조건에 성립에 done으로 분기한다.
A7) (1) slt $t1, $s0, $t0
(2) bnq $t1, $0, done
< 참고 자료 >
https://madplay.github.io/post/big-endian-little-endian
'Study > 컴퓨터 구조' 카테고리의 다른 글
컴퓨터 구조 - 캐시 기억장치 (0) | 2020.06.06 |
---|---|
컴퓨터 구조 - 분기 명령어와 서브루틴 (0) | 2020.05.29 |
컴퓨터 구조 - 메모리 구조와 레지스터 (0) | 2020.05.23 |
컴퓨터 구조 - 명령어를 효과적으로 실행하기 위한 기법 (0) | 2020.05.23 |
컴퓨터 구조 - 어셈블리 언어 (0) | 2020.04.18 |
댓글