에피소드 II: RV32I 명령어 집합
또 하나의 중요한 정보원은 물론 RISC-V 참조 매뉴얼입니다. 이 매뉴얼을 보면 RISC-V 표준에 여러 가지 종류가 있다는 것을 알 수 있습니다. 가장 단순한 것(RV32I, 즉 32비트 기본 정수 명령어 집합)부터 시작합시다. 그다음에 한 번에 하나씩 기능을 추가하는 방법을 살펴보겠습니다. 이것은 RISC-V의 매우 좋은 특징입니다. 명령어 집합이 모듈식이므로 아주 작고 자체적으로 완결된 커널에서 시작할 수 있으며, 이 커널은 표준을 준수하게 됩니다. 즉, 표준 도구(컴파일러, 어셈블러, 링커)로 이 커널용 코드를 생성할 수 있다는 뜻입니다. 그런 다음 저는 2장(13쪽부터 30쪽까지)을 읽기 시작했습니다. 130쪽의 표도 함께 보면, 실제로 서로 다른 명령어는 11개뿐입니다! (예를 들어 저는 AND, OR, ADD 등을 같은 명령어로 봅니다. 연산은 추가 매개변수일 뿐입니다.) 지금은 전체적인 그림만 파악하려는 것이므로, 아직 세부 사항까지 깊이 들어갈 필요는 없습니다. 이 11개 명령어를 전체적으로 살펴봅시다:
| 명령어 | 설명 | 알고리즘 |
|---|---|---|
| Branch | 조건부 점프, 6가지 변형 | if(reg OP reg) PC<-PC+imm |
| ALU 레지스터 | 레지스터 3개를 사용하는 ALU 연산, 10가지 변형 | reg <- reg OP reg |
| ALU 즉시값 | 레지스터 2개를 사용하는 ALU 연산, 9가지 변형 | reg <- reg OP imm |
| Load | 메모리에서 레지스터로, 5가지 변형 | reg <- mem[reg + imm] |
| Store | 레지스터에서 메모리로, 3가지 변형 | mem[reg+imm] <- reg |
LUI |
상위 즉시값 로드 | reg <- (im << 12) |
AUIPC |
PC에 상위 즉시값 더하기 | reg <- PC+(im << 12) |
JAL |
점프 후 링크 | reg <- PC+4 ; PC <- PC+imm |
JALR |
레지스터 기준 점프 후 링크 | reg <- PC+4 ; PC <- reg+imm |
FENCE |
멀티코어를 위한 메모리 순서 지정 | (여기서는 자세히 다루지 않으며, 지금은 건너뜁니다) |
SYSTEM |
시스템 콜, 중단점 | (여기서는 자세히 다루지 않으며, 지금은 건너뜁니다) |
6가지 분기 명령어는 두 레지스터에 대한 비교 결과에 따라 동작하는 조건부 점프입니다.
ALU 연산은
register <- register OP register또는register <- register OP immediate형태를 가질 수 있습니다.다음으로 로드와 스토어가 있으며, 이들은 바이트, 16비트 값(하프워드라고 부릅니다) 또는 32비트 값(워드라고 부릅니다)을 대상으로 동작할 수 있습니다. 또한 바이트 및 하프워드 로드는 부호 확장을 수행할 수 있습니다. 원본/대상 주소는 레지스터의 값에 즉시값 오프셋을 더해서 얻습니다.
나머지 명령어는 좀 더 특수합니다(처음 읽을 때는 설명을 건너뛰어도 됩니다. 이 명령어들이 무조건 점프, 함수 호출, 멀티코어를 위한 메모리 순서 지정, 시스템 콜과 브레이크를 구현하는 데 쓰인다는 점만 알면 됩니다):
LUI(load upper immediate, 상위 즉시값 로드)는 상수의 상위 20비트를 로드하는 데 사용됩니다. 그런 다음 하위 비트는ADDI나ORI를 사용해 설정할 수 있습니다. 32비트 상수 하나를 레지스터에 로드하는 데 명령어가 두 개나 필요하다는 것이 처음에는 이상해 보일 수 있지만, 모든 명령어의 길이가 32비트이므로 사실 이는 영리한 선택입니다.AUIPC(add upper immediate to PC, PC에 상위 즉시값 더하기)는 현재 프로그램 카운터에 상수를 더하고 그 결과를 레지스터에 저장합니다. 이 명령어는JALR과 함께 사용하여 32비트 PC 상대 주소에 도달하기 위한 것입니다.JAL(jump and link, 점프 후 링크)은 PC에 오프셋을 더하고, 점프 다음 명령어의 주소를 레지스터에 저장합니다. 이 명령어는 함수 호출을 구현하는 데 사용할 수 있습니다.JALR도 같은 일을 하지만, 오프셋을 레지스터에 더합니다.FENCE와SYSTEMS는 각각 멀티코어 시스템에서 메모리 순서 지정을 구현하고 시스템 콜/브레이크를 구현하는 데 사용됩니다.
요약하면, 분기(조건부 점프), ALU 연산, 로드와 스토어, 그리고 무조건 점프와 함수 호출을 구현하는 데 쓰이는 두어 가지 특수 명령어가 있습니다. 메모리 순서 지정과 시스템 콜을 위한 함수도 두 가지 있습니다(하지만 이 두 가지는 당분간 무시하겠습니다). 좋습니다. 결국 명령어는 9개뿐이니 해볼 만해 보입니다... 이 시점에서 저는 모든 것을 이해하지는 못했으므로, 제가 가장 단순하다고 생각하는 부분(레지스터 파일과 ALU)부터 시작하고, 그다음에 명령어 디코더와 각 부분이 어떻게 서로 연결되는지 살펴보겠습니다.