이 글은 김영근 교수님의 컴퓨터 구조 강의를 듣고 정리한 내용입니다.
본 글에서는 Instruction Set Architecture에 대한 구성을 RISC-V를 기반으로 설명하려고 한다.
먼저 RISC-V는 UC Berkeley에서 만든 Open ISA이며,
우리가 많이 사용하고 있는 ARM도 RISC를 기반으로 한 ISA이다.
이러한 RISC-V의 구성을 통해서 아래와 내용과 같이 ISA에 대해 더 자세히 살펴보려고 한다.
RISC-V의 명령어를 살펴보면 디자인 원칙을 자연스레 알 수 있다.
몇가지 예시를 통해 어떤 원칙에 기반하여 명령어를 만들었는지부터 먼저 살펴보자.
① Simplicity favors regularity
첫번째 원칙은 규칙성을 통한 단순화이다.
add a, b, c // a ← b + c
sub a, b, c // a ← b - c
위의 구문은 RISC-V의 Assembly Code 형태를 나타낸 것인데,
1개의 명령어는 3개의 피연산자를 가지고 있으며, 이 중 2개는 source, 1개는 destination으로 사용된다.
Operations에 대한 글에서 소개하겠지만, 모든 산술 연산 명령어는 이러한 구성을 가진다.
② Smaller is faster
두번째 원칙은 작을수록 빠르다는 것이다.
CPU에서는 많은 명령어가 실행될 것이고, 이때마다 Register 호출 작업이 실행될 것인데,
그럼 여러개의 Register 중 어떤 Register가 호출 되었는지 확인하는 작업이 필요할 것이다.
이때 RISC-V에서는 32개라는 적은 숫자의 Register를 사용하였는데,
이는 단 5 bit 만으로 Register를 구분할 수 있고, 이 bit 수가 적다는 것은
결국 명령어의 실행 속도 또한 빨라진다는 것을 의미한다.
③ Good design demands good compromises
세번째 원칙은 좋은 설계를 위해선 적절한 타협이 필요하다는 것이다.
이전 글에서 ISA에 대해 설명하면서, Register는 매우 비싸기 때문에 저장 공간이 작다고 말했었다.
RISC-V 에서는 Register를 32개만 사용하고 있으며, 1개 Register의 저장 공간은 64bit 이다.
추가적으로, 다음 실행될 명령어의 주소를 가리키는 Program Counter(PC)를 포함한다.
RISC-V 에서 사용되는 명령어의 유형과 명령어에서 피연산자의 주소 지정 방식에 대해서 살펴보자.
RISC-V에는 명령어의 동작 관점에서 아래와 같이 3가지 유형의 존재한다.
또한 RISC-V에서는 명령어의 구조 관점에서 아래와 같이 4가지 유형이 존재하며,
기본적으로 명령어의 크기가 32bit이다.
(아래 Addressing Mode와는 개념이 조금 다르다.)
또한 명령어 내에 존재하는 Field는 다음과 같은 의미를 지닌다.
RISC-V의 명령어에는 연산자(op) / 피연산자(rs1,rs2) 등에 대한 정보로 이루어져 있다.
Addressing Mode는 피연산자 데이터를 어떤 방식으로 가져오는지를 말하고, 4가지 방식으로 나뉜다.
(명령어의 구성 등에 대해서는 아직 언급한 적이 없으니 간단하게만 소개하겠다.)
위 2️⃣ Format에서 볼 수 있듯이 명령어 내에 Immediate Value를 포함하는 방식에 대해 생각해보자.
※ Immediate를 활용하는 명령어에 대한 더 자세한 내용은 여기 참고
I-Type 명령어의 경우 Imm 값에 12 bit가 할당되고 이를 Register의 값에 더하는 방식으로 활용되는데,
Register는 32(or 64)bit 값을 저장하기 때문에, Register↔︎Imm 간 연산 시 bit 수 차이가 있다는 것이다.
이러한 bit 수를 해결하기 위한 방법들이 Zero / Sign Extension 이다.
2가지 방법 모두 적은 자릿수를 가진 값의 bit 를 확장시켜 bit 자릿수를 동등하게 맞춰주는 것인데
아래와 같이 확장된 bit에 어떤 값을 채워주냐에 따른 차이점이 있다.
① Zero Extension : 확장된 bit를 모두 0으로 채워주는 방식, 기존 부호 무시
② Sign Extension : 확장된 bit를 기존 값의 MSB(최상위 bit)로 채워주는 방식, 기존 부호 유지
기본적으로 메모리의 값은 2의 보수 형태로 저장되기 때문에 (※ 2의 보수 설명)
①, ② 방식 모두 양수에 대해서는 문제가 없으나 음수를 확장할때는 차이가 발생한다.
예를 들어, 8bit의 -5(2의 보수 : )에 대해 16bit로 확장 시킨다고 가정해보자.
그럼 Zero Extension에서는 확장 과정에서 최상위 bit가 양수를 의미하는 0으로 채워지기 때문에,
기존 음수가 완전히 다른 양수 값으로 바뀌는 것과, Sign Extension에서는 경우 동일한 것을 알 수 있다.
① Zero Extension 예시
② Sign Extension 예시
따라서 Zero Extension은 AND/OR 과 같이 논리 연산에서 주로 쓰이며,
Sign Extension은 +/- 과 같은 산술 연산에서 주로 사용된다.
RISC-V에서 차용하고 있는 메모리 할당 방식을 살펴보자.
먼저 Endian이란 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻하며,
이 중 Byte를 배열하는 방법을 Byte ordering이라고 한다.(Wikipedia)
Endian은 아래와 같이 2가지 방식으로 나뉘는데 RISC-V에서는 Little Endian을 차용하고 있다.
메모리에 데이터를 저장할때, 데이터의 크기에 따라 제약을 부여하는 경우가 있다.
(ex. Word는 4의 배수 주소 / DoubleWord는 8의 배수 주소에만 저장)
아래 그림의 Aligned가 제약을 부여한 경우이고, Not Aligned가 제약을 부여하지 않은 경우인데
RISC-V의 경우, 제약을 부여하지 않는 Not Aligned 방식을 차용 중이다.