본문 바로가기

RISC-V/RISC-V 개념

[RISC-V] RV32I ISA - Computation

 지난 포스팅에서는 RV32I를 하드웨어와 소프트웨어라는 두 가지 관점에서 분류해 보았습니다. 이전 내용이 궁금하시다면 아래 글을 참고해주세요.

https://idkihg.tistory.com/143

 

[RISC-V] RV32I ISA

RV32I ISA(Instruction Set Architecture)는 최소한의 명령어로 모든 계산을 수행한다는 RISC-V의 철학을 보여줍니다. 전체 명령어는 총 47개 뿐이며, 모든 명령어 길이는 32비트로 고정되어 있습니다. 1. 하드

idkihg.tistory.com

 

 

2) ISA Loads & Stores

https://idkihg.tistory.com/147

 

[RISC-V] RV32I ISA - Loads & Stores

지난 포스팅에서는 RV32I를 하드웨어와 소프트웨어라는 두 가지 관점에서 분류해 보았습니다. 이전 내용이 궁금하시다면 아래 글을 참고해주세요.https://idkihg.tistory.com/143 [RISC-V] RV32I ISARV32I ISA(Inst

idkihg.tistory.com

 

 

3) ISA Control Transfer

http://idkihg.tistory.com/148

 

[RISC-V] RV32I ISA - Control Transfer

지난 포스팅에서는 RV32I를 하드웨어와 소프트웨어라는 두 가지 관점에서 분류해 보았습니다. 이전 내용이 궁금하시다면 아래 글을 참고해주세요.https://idkihg.tistory.com/143 [RISC-V] RV32I ISARV32I ISA(Inst

idkihg.tistory.com

 

 

4) ISA Miscellaneous Instructions

https://idkihg.tistory.com/149

 

[RISC-V] RV32I ISA - Miscellaneous Instructions

지난 포스팅에서는 RV32I를 하드웨어와 소프트웨어라는 두 가지 관점에서 분류해 보았습니다. 이전 내용이 궁금하시다면 아래 글을 참고해주세요. 아울러 소프트웨어적 분류를 바탕으로 연재 중

idkihg.tistory.com

 

 

 이번 글에서는 CPU의 두뇌인 ALU가 실제로 데이터를 처리하는 핵심, Computation 명령어들을 하나씩 파헤쳐 보겠습니다.

 

1. Computation 연산의 분류

 RV32I의 연산 명령어는 데이터(Operand)를 어디서 가져오느냐에 따라 크게 세 가지 타입으로 나뉩니다. 32비트라는 한정된 공간 안에 정보를 효율적으로 담기 위한 RISC-V의 설계 철학이 돋보이는 부분입니다.

 ✔  R-type (Register-Register): 두 개의 소스 레지스터(rs1, rs2) 값을 연산하여 목적지 레지스터(rd)에 저장합니다.

  • add, sub, and, or, xor, sll, srl, sra, slt, sltu

  I-type (Register-Immediate): 레지스터 하나와 명령어에 포함된 12비트 상수(Immediate)를 연산합니다.

  • addi, andi, ori, xori, slli, srli, srai, slti, sltiu

 ✔  U-type (Upper Immediate): 32비트 전체 값을 다루기 위해 상위 20비트를 처리하는 특수 명령어입니다.

  • lui, auipc

 

 

2. 산술 연산 명령어 (Arithmetic)

 산술 연산은 가장 기본이 되는 더하기와 빼기입니다. 하드웨어 설계를 단순하게 유지하기 위해 RV32I는 오버플로우(Overflow)를 하드웨어적으로 트랩하지 않고 소프트웨어가 판단하도록 맡깁니다. 종류는 add, sub, addi 총 3가지 입니다. 아래는 산술 연산 명령어의 하드웨어 비트 필드 구조입니다.

 

 - add rd, rs1, rs2                 =>     x[rd] = x[rs1] + x[rs2]

 - sub*  rd, rs1, rs2               =>     x[rd] = x[rs1] - x[rs2]

 - addi rd, rs1, imm[11:0]     =>     x[rd] = x[rs1] + imm[11:0]

 

 *subi가 없는 이유는 addi에 음수 상수를 넣으면 뺄셈이 되기 때문입니다.

 

 

3. 논리연산 명령어(Logical)

 논리 연산은 비트 단위(Bitwise)로 데이터를 가공할 때 사용합니다. 특정 비트를 추출하거나, 0 또는 1로 세팅할 때 필수적입니다. 종류는 and, or, xor, andi, ori, xori 총 6가지 입니다. 아래는 논리 연산 명령어의 하드웨어 비트 필드 구조입니다.

 - and rd, rs1, rs2                 =>     x[rd] = x[rs1] & x[rs2]

 - or rd, rs1, rs2                    =>     x[rd] = x[rs1] | x[rs2]

 - xor rd, rs1, rs2                  =>     x[rd] = x[rs1] ^ x[rs2]

 - andi rd, rs1, imm[11:0]     =>     x[rd] = x[rs1] & sign-extended(imm[11:0])**

 - ori rd, rs1, imm[11:0]        =>     x[rd] = x[rs1] | sign-extended(imm[11:0])

 - xori rd, rs1, imm[11:0]      =>     x[rd] = x[rs1] ^ sign-extended(imm[11:0])

 

**I-Type 논리 연산의 경우 sign-extended를 반영하여 연산이 이루어집니다.

ex1) 양수 확장

rs1 = 0x1234_5678, imm[11:0] 0x00F

 - andi rd, rs1, imm[11:0]

=> rd = 0x1234_5678 & 0x0000_000F

=> rd = 0x0000_0008

 

ex2) 음수 확장

rs1 = 0x1234_5678, imm[11:0] 0xFFF

- andi rd, rs1, imm[11:0]

=> rd = 0x1234_5678 & 0xFFFF_FFFF

=> rd = 0x1234_5678

 

 

4. 이동 연산 명령어(Shift)

 Shift 연산은 비트를 왼쪽이나 오른쪽으로 밀어내는 연산입니다. RV32I에서는 논리 이동(Logical)과 산술 이동(Arithmetic)을 모두 지원합니다.

 

  • sll / slli (Shift Left Logical): 비트를 왼쪽으로 밀고, 빈 자리는 0으로 채웁니다.
  • srl / srli (Shift Right Logical): 비트를 오른쪽으로 밀고, 빈 자리는 0으로 채웁니다. 
  • sra / srai (Shift Right Arithmetic): 비트를 오른쪽으로 밀되, 최상위 비트(MSB, 부호 비트)를 유지하며 채웁니다. 

 

종류는 위와 같이 총 6개 입니다. 아래는 이동 연산 명령어의 하드웨어 비트 필드 구조입니다.

 - sll rd, rs1, rs2            =>     x[rd] = x[rs1] << x[rs2]

 - srl rd, rs1, rs2            =>     x[rd] = x[rs1] << x[rs2]

 - sra rd, rs1, rs2           =>     x[rd] = x[rs1] >> x[rs2]

 - slli rd, rs1, shamt      =>     x[rd] = x[rs1] << shamt***

 - srli rd, rs1, shamt      =>     x[rd] = x[rs1] << shamt

 - srai rd, rs1, shamt     =>     x[rd] = x[rs1] >> shamt

 

***I-Type shift에서 shamt(shift amount)는 5bit(0~31)만 사용합니다. 32bit 레지스터에서 32번 이상 미는 것은 의미가 없기 때문입니다.

 

 

5. 비교 연산 명령어(Set Less Than)

비교 연산은 두 값을 비교하여 조건이 참이면 1, 거짓이면 0을 목적지 레지스터(rd)에 저장합니다. 주로 조건문이나 루프의 기초가 됩니다.

  • slt / slti: 부호가 있는 정수(Signed)로 비교합니다.
  • sltu / sltiu: 부호가 없는 정수(Unsigned)로 비교합니다.

종류는 위와 같이 총 4개 입니다. 아래는 비교 연산 명령어의 하드웨어 비트 필드 구조입니다.

 - slt rd, rs1, rs2                   =>      x[rd] = x[rs1] < x[rs2] (signed)

 - sltu rd, rs1, rs2                 =>      x[rd] = x[rs1] < x[rs2] (Unsigned)

 - slti rd, rs1, imm[11:0]       =>      x[rd] = x[rs1] < sign-extended(imm[11:0])) (Signed)

 - sltiu rd, rs1, imm[11:0]     =>      x[rd] = x[rs1] < sign-extended(imm[11:0])) (Unsigned)****

 

****sltiu에서 imm은 부호 확장이 먼저 일어난 후, 비교 자체는 Unsigned로 수행됩니다. 이 특성은 특정 값이 0인지 확인하거나, 범위를 체크하는 최적화에 자주 사용됩니다.

 

 

6. U-type 연산 명령어 (Upper Immediate)

RV32I는 모든 명령어 길이가 32비트로 고정되어 있습니다. 따라서 32비트 크기의 상수를 한 번에 레지스터에 넣을 수 없죠. (Opcode 등이 자리를 차지하니까요!) 이를 해결하기 위해 상위 20비트를 먼저 다루는 U-type 명령어가 존재합니다.

 

  • lui (Load Upper Immediate):
    • 전달받은 20비트 상수를 레지스터의 **[31:12]**에 넣고, 하위 12비트는 0으로 채웁니다.
    • 보통 addi와 조합하여 32비트 전체 상수를 완성할 때 사용합니다.
  • auipc (Add Upper Immediate to PC):
    • 20비트 상수를 상위 비트에 두고, 현재의 PC(Program Counter) 값을 더해 rd에 저장합니다.
    • 현재 위치를 기준으로 멀리 떨어진 데이터나 함수에 접근할 때(Relative Addressing) 필수적입니다.

종류는 위와 같이 총 2개 입니다. 아래는 U-Type 연산 명령어의 하드웨어 비트 필드 구조입니다.

 - lui rd, imm[31:12]           =>     rd = {imm[31:12], 12'b0}   

 - auipc rd, imm[31:12]     =>     rd = PC + {imm[31:12], 12'b0}   

 

 

7. 요약 및 마무리

지금까지 RV32I의 핵심 연산 그룹인 Computation 명령어들을 살펴보았습니다.

  1. Arithmetic: 더하고 빼기 (add, sub)
  2. Logical: 비트 단위 연산 (and, or, xor)
  3. Shift: 비트 밀기 (sll, srl, sra)
  4. Compare: 크기 비교 (slt)
  5. Upper: 큰 숫자 다루기 (lui, auipc)

이 명령어들만 있으면 세상의 거의 모든 복잡한 계산을 수행할 수 있습니다. 이것이 바로 RISC(Reduced Instruction Set Computer)가 추구하는 단순함의 미학입니다.