Instruction Set Architecture(ISA)
명령어는 컴퓨터가 이해할 수 있는 언어이자 프로세서가 돌아가게 하기 위한 최소 단위이다. 소프트웨어와 하드웨어의 인터페이스라고 할 수 있다.
여기에서 잠시 생각해보자. 인텔 CPU와 AMD CPU는 같을까? 동일한 아키텍쳐를 가진다. 그 이유는 OS를 설치해보면 알 수 있는데, 동일한 명령어 집합을 공유하고 있기 때문이다. 그러나 마이크로아키텍쳐는 다르다. 동일한 명령어 집합을 사용하면서, 회사마다 회로 설계방식은 다를 수 있기 때문이다.
지난 글에서 언급했듯 마이크로아키텍쳐는 CPU의 하드웨어 설계를 말한다. 컴퓨터는 2개의 상태를 가지는데, register와 memory이다. 우리는 명령어를 사용해서 각각의 상태를 관리해주어야 한다. 관리하고 명령하는 것을 operation이라고 한다.
ISA Classification: CISC vs. RISC
주로 CISC와 RISC로 나눌 수 있다.
CISC
하드웨어 기반으로 만든 방식, 최근에 사용하는 RISC보다 복잡한 회로 구성으로 인해 Complex Instruction Set Computer라고 한다. 사용자가 필요할 때마다 새로운 명령어를 만드는 바람에, 하드웨어 설계가 꽤 복잡하고 명령어 길이가 제각각이다. 사용하는 명령어 개수가 적다보니, 메모리에 올라가는 메모리 수도 적기 때문에 메모리 사용량이 적다. 최근에는 하위호환성(Backward compatibility)이 좋아서 서버나 데스크탑에 주로 사용된다.
RISC
소프트웨어 기반으로 만든 방식으로 최근 스마트폰에 주로 사용된다. 고정 길이 명령어를 가지고 있으며 기존 명령어를 조합해서 새로운 명령어를 내린다. 그래서 실제로 사용할 때 명령어의 개수가 많고 메모리를 차지하는 명령어 수가 많다. 한 사이클에 하나의 명령어를 처리하는 방식이다.
자세히 차이점을 비교해보자.
Cycle Per Instruction
CISC는 명령어 하나당 수행하는 시간이 제각각이기 때문에 clock cycle도 서로 다르다. RISC는 하나의 cycle에 하나의 명령어를 처리한다.
Instruction Per Program
CISC는 프로그램 당 명령어 수가 적다. 그 이유는 하드웨어에서 복잡하게 설계된 명령어 하나를 쓰기 때문이다. 이에 반해, RISC는 프로그램 당 명령어 수가 많다. 예를 들어서 mult 명령어를 한다고 하면, CISC는 mult 하나를 사용한다. RISC는 기존 명령어를 조합하여 사용하기 때문에 Load, Load, Prod, Store로 총 4개를 사용한다.
Emphasize for Performance
CISC는 프로그램 당 명령어 수가 적어서 Ic에서 효율적이다. RISC는 한 명령어당 한 사이클에 처리하기 때문에 CPI에서 우세하다.
Code Size
CISC는 하드웨어에서 미리 설계된 것을 가져다 쓰기 때문에 코드 사이즈가 작다. 그래서 컴파일러가 코드를 최적화하기 매우 용이하다. 그러나 RISC는 하드웨어 설계가 최소화된 형태에 소프트웨어가 적절히 여러 개의 명령어를 조합하기 때문에 코드가 길다.
Design
CISC는 하드웨어 기반, RISC는 소프트웨어 기반이다.
Required Memory
CISC는 명령어 개수가 적고 메모리를 많이 사용하지 않는다. RISC는 명령어 개수가 많고 메모리에 명령어를 올려놓고 사용하기 때문에 메모리 사용량이 많을 수 밖에 없다.
Implementation Complexity
구현의 복잡도는 당연히 CISC가 높을 수 밖에 없다.
# of Registers
CISC는 하드웨어 기반으로 복잡하게 회로를 설계해두기 때문에 레지스터를 많이 넣을 수가 없다. 반면 RISC는 최소한의 하드웨어 설계이기 때문에 공간이 남는다. 그래서 RISC는 보통 레지스터가 32개, CISC는 16개다.
Instruction
CISC는 명령어가 제각각이고 RISC는 명령어 길이가 고정되어 있다.
Pipelining
RISC가 병렬처리하기 쉽다. 그 이유는 명령어 길이가 고정되어 있기 때문이다.
Code expansion
RISC의 코드 사이즈가 크기 때문에 코드 확장성이 좋지 않다. CISC는 코드가 심플하기 때문에 확장성이 좋다.
Power Consumption
CISC는 에너지 소모량이 크기 때문에 전원을 직접 연결해서 사용해야 한다. 반면 RISC는 임베디드 시스템에 적합할 정도로 에너지 소모가 적다.
ISA 설계 시 고려할 부분
명령어 집합을 설계하면서 고려해야 할 부분은 5가지가 있다.
- Where are operands stored? : operand를 어디에 저장해야 할까? register, memory, stack 등에 저장할 수 있다.
- How many explicit operands are there? : operand를 몇 개까지 허용할 것인가? 0~3개
- How is the operand location specified(How to access memory)? : operand는 어디에 저장해야 하며 메모리에는 어떻게 접근할까? register, immediate, indirect 등
- What type and size of operands are supported? : operand의 종류와 크기는 어디까지 지원할 것인가? byte, int, float, double...
- What operations are supported? : 어떤 명령어를 지원할 것인가? add, sub, mul, move...
하나씩 살펴보자.
Storing Operand
보통 3가지 중에 하나에 저장한다.
- Stack architecture
- Accumulator architecture
- General-purpose register architecture
RISC-V는 32개의 General Purpose Register를 가지고 있고 이곳에 저장한다. load-store 아키텍쳐의 특성상 메모리 접근을 최소화해서 속도를 높인다.
general purpose register이란?
범용 레지스터로 CPU 내에서 데이터를 임시 저장하고 연산을 수행하는데 사용되는 레지스터이다. 메모리보다 접근 속도가 빨라 주로 사용된다.
# of Operands
ALU는 2개나 3개의 피연산자를 갖는데, RISC-V는 3개의 피연산자를 가진다.
- add a, b <- a = a + b
- add a, b, c <- a = b + c
Addressing Mode
메모리는 바이트 단위로 주소 지정이 가능한 배열이다. 명령어 집합을 설계할 때 고려할 사항으로 특정 레지스터로 어떻게 접근하며, 메모리에서 어떻게 데이터를 가져오고 데이터를 저장할지에 대한 고민이 있었다. 특히 32bit word를 어떻게 가져올 것인지, byte 주소를 word로 어떻게 매핑할 것인지에 대한 고민이 있었다.
이때, Word는 프로세서가 한 번에 처리할 수 있는 데이터 크기이다. 주로 32bit 시스템에서는 4byte(32bit), 64bit 시스템에서는 8byte(64bit)이다. 일부에서는 레지스터의 크기나 메모리 bandwidth의 크기라고 정의하는데, 이것은 틀린 말이다. 시스템마다 word의 크기는 달라진다. bytes는 8bits, half word는 16bits, words는 32bit, double words일 때는 64bits이다. 주소는 바이트 위치를 지정하고 메모리는 각각 한 바이트를 저장한다. 그리고 메모리 주소는 각 워드의 처음 시작 주소를 가리킨다. 연속된 워드들의 주소 차이는 32bit 시스템에서 4bytes, 64bit 시스템에서는 8bytes이다.
Byte Order
- little endian : LSB가 가장 낮은 주소에 들어가는 방식
- big endian : MSB가 가장 낮은 주소에 들어가는 방식
RISC-V는 little endian 방식이다.
예를 들어서, 아래와 같은 프로그램이 있다고 하자.
#include <stdio.h>
int main(void) {
unsigned int i = 0x003377ff;
unsigned char *p;
int j;
p = (unsigned char*)&i;
for (j = 0; j < 4; j++) {
printf("Byte %d: %2x\n", j, p[j]);
}
}
little endian 방식은 Bytes 0 : ff, Bytes 1 : 77, Bytes 2 : 33, Bytes 3 : 00이다. Big endian 방식은 그 반대로 들어간다.
Type And Size of Operands
보통 opcode에 operand의 타입이 지정되어 있다. 예를 들어서 ldb는 load byte라는 뜻, ldw는 load word라는 뜻이다. 우리가 이 타입과 operand에 대해 이해하려면 기본적으로 각 타입과 크기를 잘 알고 있어야 한다.
C type | RV32 | RV64 |
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long | 4 | 8 |
long long | 8 | 8 |
void* | 4 | 4 |
float | 4 | 4 |
double | 8 | 8 |
long double | 16 | 16 |
RISC-V 아키텍쳐
ISA 설계 시 고려사항을 알았으니, 이제 RISC-V에 적용해보자.
- where are operands stored?
- RISC-V에는 32개의 범용 레지스터가 있고 각각의 레지스터는 64bit(8bytes)이다.
- How many explicit operands are there?
- 피연산자 개수는 3개이며 메모리에서 값을 직접적으로 가져올 수 없다. (Load-Store 아키텍쳐) 참고로, 인텔은 피연산자가 2개인데 그 이유는 RISC와 CISC 차이에서 보면 언급했지만 CISC 기반인 인텔의 레지스터 개수가 부족하기 때문이다.
- How is the operand location specified(How to access memory)?
- 64bit 주소 체계를 쓰고 실제로는 48bit만 사용한다. 명령어 길이는 32bit 고정길이를 사용한다.
- Register에서 값을 가져오는 방식(Register), 직접 operand에 값을 작성하는 방식(immediate), Base 레지스터에 offset을 더하는 방식(Base), Program Counter에 의존하는 방식(PC-relative)
Register
레지스터는 하드웨어 내에 내장되어 CPU가 특정 값을 임시로 저장할 수 있게 한다. RISC-V에서는 Load-Store 방식으로 레지스터에 있는 값만 가져와 연산할 수 있다. RISC-V는 33개의 레지스터를 가지고 있다. 32개의 범용 레지스터와 특수 기능 레지스터인 Program Counter 1개로 구성된다. x0 레지스터는 항상 0이고 x1 레지스터는 함수 호출 시 리턴 값을 가지도록 한다. 각 레지스터는 64bit 크기를 가지고 있고 RV32I는 32bit이다.
Operation
RISC-V는 3가지 타입의 명령어를 가진다. 산술, 논리 연산을 가능하게 하는 ALU, 메모리에서 데이터를 가져오고 저장하는 Data Transfer, 메모리 주소를 점프하거나 순차적으로 접근할 때 사용하는 Control이 있다.
Signed and Unsigned Numbers
컴퓨터 내에서는 이진수로 모든 것을 표현한다.
'CS > 컴퓨터구조' 카테고리의 다른 글
[컴퓨터구조] 2-4. Linking (1) | 2024.12.18 |
---|---|
[컴퓨터구조] 2-3. Calling Convention (0) | 2024.12.18 |
[컴퓨터구조] 2-2. RISC-V ISA (0) | 2024.12.01 |
[컴퓨터구조] 1. Computer Architecture (1) | 2024.10.22 |