RISC-V RV32I는 RISC-V 프로세서의 기본 32비트 정수 명령어 집합 아키텍처(ISA)입니다. 이는 완전한 32비트 컴퓨터를 구현하는 데 필요한 최소한의 명령어 집합을 제공하며, 다양한 확장을 통해 기능을 확장할 수 있는 기반이 됩니다.
주요 특징은 다음과 같습니다.
- 32비트 아키텍처: 32비트 정수 레지스터와 주소를 사용합니다.
- 로드-스토어 설계: 산술 및 논리 연산은 레지스터에서 수행되며, 메모리 접근은 별도의 명령어로 처리됩니다.
- 고정 길이 명령어: 모든 명령어가 32비트로 고정되어 있어 디코딩이 단순해집니다.
- 간단한 주소 지정 방식: 즉시값, 레지스터, 변위 주소 지정 모드를 지원합니다.
- 32개의 범용 레지스터: x0에서 x31까지의 32비트 레지스터가 있으며, x0는 항상 0으로 고정되어 있습니다.
CPU의 모든 동작의 핵심인 레지스터를 좀 더 자세히 살펴봅시다.
| x0 | zero | 항상 0으로 고정된 레지스터 |
| x1 | ra | 반환 주소 (Return Address) |
| x2 | sp | 스택 포인터 (Stack Pointer) |
| x3 | gp | 전역 포인터 (Global Pointer) |
| x4 | tp | 스레드 포인터 (Thread Pointer) |
| x5 - x7 | t0 - t2 | 임시 레지스터 (Temporaries) |
| x8 | s0/fp | 저장 레지스터/프레임 포인터 |
| x9 | s1 | 저장 레지스터 |
| x10 - x11 | a0 - a1 | 함수 인자/반환 값 |
| x12 - x17 | a2 - a7 | 함수 인자 |
| x18 - x27 | s2 - s11 | 저장 레지스터 |
| x28 - x31 | t3 - t6 | 임시 레지스터 |
RISC-V ISA는 아주 체계적으로 정리되어 있어서 아래 그림만 이해한다면 모든 동작이 유추 가능합니다. (Reference: The risc-v reader: an open architecture chapter2.2 pp15~16) Instruction의 동작은 아래 네가지로 구분할 수 있습니다.
- Integer Computation
- Control Transfer
- Load and Stores
- Misc
예를 들어 slli(shift left logical immediate)은 immediate이 붙었기 때문에 Fig.2의 I-type이 된다. Fig.3의 opcode map에서 slli를 찾아보면 각 Field를 아래와 같이 Decoding할 수 있습니다. 여기서 특이한 점은, 보통 immediate type은 bit31~20까지를 immediate value로 사용하는데, Shift의 경우 Shift 시켜야 할 양이 가변적이기 때문에 shamt를 5bit 할당한 것입니다.
- bit6~0의 0010011이 OPCODE
- FUNCT3=001
- FUNCT7=0000000
- shamt: Shift amount
- rd: desination register
- rs1: shift를 수행할 operand
모든 Instruction을 동일한 방식으로 분석하면 기능과 형태를 파악할 수 있습니다. 자세한 내용은 Ratified 된 RISC-V Spec Document를 참고하면 됩니다.


Fig.1 Instruction Functions

Fig.2 Instruction Formats

Fig.3 RV32I opcode map
C Program을 Compile하여 Assembly로 변환될 때 어떻게 위 Instruction과 연결되는지 살펴봅시다.
1. Integer computation instructions: 여기서는 Register 연산만 허용되기 때문에 직관적으로 이해할 수 있습니다.
- a0 = a1 + a2 -> add a0, a1, a2 ; a0 = destination reg, a1 = op1, a2 = op2
- a0 = a0 - 1 -> addi a0, a0, -1
2. Control transfer instructions: jump와 branch 두 가지 종류가 있습니다.
- Jump: Unconditional이며, link를 가질 수 있습니다. 여기서, link란 ra의 return point와 연결되어 있다는 의미입니다. 그리고, Jump할 target이 direct일 수도 있고 indirect일 수도 있습니다.
- jal ra, foo: foo로 바로 뛰어갑니다.
- jalr ra, a0: a0 register에 있는 값으로 뛰어갑니다.
- jalr zero, ra: return address로 바로 뛰어갑니다.
- Branch: Condition이 있는 Jump로 보면 됩니다.
- beq a0, a1, bar -> if (a0==a1) jump to bar
- Conditions: beq, bne, blt, bge, bltu, bgeu (u가 붙지 않으면 signed ordering)
3. Loads and Stores: base + displacement의 형태로 계산하여 address를 지정합니다.
- lw a0, 4(a1) -> address (a1 + 4)를 load 하여 a0에 저장합니다.
- sw a0, 4(a1) -> a0 Register의 값을 address (a1 + 4)에 저장합니다.
- lb/lbu, lh/lhu가 있는데, lb a0, 4(a1) -> 4(a1)의 값이 0xf0라고 하면 0b11110000 이 값은 MSB에 1을 갖는 음수 값으로 sign extension에 의해 32bit registerdp 0b111111111111111111110000 (0xfffffff0)로 쓰입니다. lbu의 경우는 상위의 bit를 모두 0으로 채우면 되는데, h의 경우는 half word이므로 동일한 룰로 사용하면 됩니다.
Decoding을 위해 알아야할 중요한 정보는 fig.2의 Intruction format입니다.
1. R-Type: Instruction code가 0x33, 0xb3으로 끝나면 R-type ALU instruction입니다. func7, func3 모두를 디코딩해야 ALU가 어떤 type인지 알 수 있고, 그 외는 직관적으로 알 수 있습니다.. sub a0, a1, a2의 예는 아래와 같습니다.

2. I-Type
- I-Type ALU operation: 0x13, 0x93으로 끝나는 instruction입니다. addi a0, a0, -1 은 아래 그림과 같습니다. imm12는 signed integer로 0x800 ~ 0x7ff 또는 -2^11 ~ 1 - 2^11 의 범위를 가집니다.

- I-Type load: 0x03, 0x83으로 끝나는 Instruction입니다. lw a0,4(a1) 은 아래 그림과 같습니다.

- I-Type jalr: 0x67, 0xe7로 끝나는 Instruction입니다. jalr ra, 4(a0) 는 아래 그림과 같습니다.

3. S-Type: 0x23, 0xa3으로 끝나는 Instruction으로 Store instruction을 수행합니다. 특이하게 imm7-high와 imm5-low를 조합하여 12-bit signed value를 가집니다. sw a0, 4(a1)은 아래와 같습니다.

4. B-Type: 0x63, 0xe3으로 끝나는 Instruction입니다. 좀 더 복잡한 immediate value를 decoding 하게 되는데, beq a0, a1, pc+12를 예를 들어봅시다. 여기서 a, c, imm7-10:5, b 순서로 값을 조합하게 되는데, 여기서는 0b000000000110가 된다. 여기서 one bit을 shift left 하고 sign extension 한 값이 최종 immediate value입니다. (0b000000001100)

5. U-Type: AUIPC (0x17, 0x97) LUI (0x37, 0xb7)로 끝나는 Instruction입니다. Destination register의 upper part를 imm20으로 update 하는 기능을 수행합니다. 즉 20bit에 12개의 0을 붙여서 destination regiter에 값을 씁니다. AUIPC는 pc value를 동일하게 업데이트합니다. 예는 lui a0, 0 xdead로 아래 그림과 같습니다.

6. J-Type: U-Type의 변형입니다. jal pc+12의 예로 설명하자면, immediate value는 d, imm20-19:12, e, imm20-10:1를 순서대로 조합하여 사용하게 됩니다. 0b0 00000000 0 0000000110 = 6으로 1bit shift left 하고 sign extension 하면 12가 됩니다.

'RISC-V' 카테고리의 다른 글
| [Background] Design Methods (Why HLS?) (6) | 2024.10.27 |
|---|---|
| [Background] Why RISC-V? (1) | 2024.10.27 |
| [Background] RISC-V 설계를 들어가며 (0) | 2024.10.27 |
| [Extension Design] CUSTOM Instruction Design (0) | 2024.07.23 |
| [FPGA] Vitis platform generation and application SW (0) | 2024.07.23 |