Chapter2-2

윤강훈·2024년 10월 23일

Computer Architecture

목록 보기
4/11

RISC-V ISA

이번에 정리할 내용은 RISC를 기반으로한 개방형 표준 ISA인 RISC-V에 대한 내용입니다.
개방형 표준 ISA라는 것은 학계와 산업에 라이센스나 로열티가 없이 자유롭게 제공됨을 뜻합니다.

2010년 UC 버클리 연구자들에 의해 시작되었고, 80x86 ISA보다 더 간단하고 우아하다고 설명합니다.

최근 많은 공급업체들이 빠르게 채택하고 있으며, 마이크로컨트롤러부터 슈퍼컴퓨터까지 모든 수준의 컴퓨팅 시스템에 적합하다는 장점이 있습니다.

  • RISC-V ISA는 "fixed-length" instruction(4byte)

Operand

  • RISC-V에서 operand(피연산자)는 반드시 레지스터에 담겨있어야만 합니다. 총 32개의 64bit 범용 레지스터가 존재합니다. 여담으로 Intel architecture에서는 메모리에서 직접 연산이 가능한 경우도 있습니다.

  • 명시적인 피연산자는 일반적으로 3개이며, 대부분의 명령어는 아래와 같습니다.

    명령어 destination, source 1, source 2

  • 또한 연산 결과를 연산에 사용했던 레지스터가 아닌 다른 레지스터에 저장하는데, 그 이유는 단순히 RISC 방식이 레지스터가 더 많기 때문입니다.

  • 피연산자의 위치 지정, 즉 메모리에 접근하는 방법은 메모리 주소를 지정하는 방식입니다.
    64bit에서는 0~264-1 개의 주소를 표현할 수 있지만, 이것은 현재 컴퓨터 사양을 고려했을 때 너무 큰 수치이므로 실제로는 0~248-1개 정도만을 사용하고 있습니다.
    또한 메모리와 레지스터 간에는 64bit 단위로 데이터가 전송됩니다

  • 하지만 조금 벗어나게도 명령어의 크기는 32bit입니다.

    • 단순성: 32비트 고정 길이 명령어는 디코딩이 단순하고 효율적이며, 하드웨어 설계가 간결해집니다.

    • 효율적인 메모리 사용: 고정된 명령어 크기는 메모리와 대역폭을 효율적으로 사용할 수 있게 해줍니다.

    • 명령어 기능성: 32비트 명령어로도 충분히 복잡한 연산을 표현할 수 있고, 필요한 경우 확장 명령어를 사용하여 처리할 수 있습니다.

    • 64비트는 데이터 처리 단위: 64비트 아키텍처는 레지스터와 데이터 처리에 해당하며, 명령어의 크기는 별개의 문제입니다.

      명령어도 64bit architecture에 맞게 전면적인 수정을 거쳤을 경우의 문제점은 인텔의 IA-64가 가장 잘 보여줍니다. 기존에 사용하던 IA-32와의 호환이 전혀되지않았기 때문에 결국 실패를 인정하고 명령어는 32bit로 그대로 유지한 CPU를 새로 만들어 냈습니다.

Modular

RISC-V는 모듈형으로 설계 되었으며, 설계의 유연성을 높이고 다양한 응용 분야에 맞게 필요한 기능만 선택적으로 사용할 수 있게 합니다.

또한 기본 베이스 ISA와 다양한 확장 ISA들로 구성됩니다.

  • 기본 베이스

    • RV32I: 32bit 정수 기반 기본 ISA
    • RV64I: 64bit 정수 기반 기본 ISA
    • RV128I: 128bit 정수 기반 기본 ISA
  • 확장

  • 선택적 사용이 가능하다는 뜻은 만약 정수 연산이 필요한 계산기를 만들 것이라면 I와 M만 가져와서 사용할 수 있다는 것을 뜻합니다.

Register

RISC-V는 총 33개의 user-visible registers를 갖고 있다. 그 중 32개는 범용 레지스터, 나머지 하나는 programm counter(PC)이다.

  • 32 general purpose(integer) registers
    • 64-bit wide

    • 레지스터 x0는 하드웨어적으로 상수 "0"으로 고정: 자주 사용되기 때문에 메모리에 접근해서 가져오는 것보다 미리 지정해놓는 것이 더 효율적

      1. 레지스터 간 데이터 이동에 사용: add x22, x21, x0
      2. 음수로 만들 때 사용: sun x22, x0, x22
    • x1은 return address: 함수가 호출된 후 다시 돌아올 명령어의 주소를 저장

  • programm counter
    • 현재 실행 중인 명령어의 주소와 다음에 실행될 명령어의 주소를 저장
    • 만약 branch나 jump 명령어가 발생하면 그 주소로 이동
    • 이러한 과정으로 프로그램의 흐름을 제어

Signed and Unsigned Numbers

  • signed numbers: 음수, 0, 양수 표현
  • unsigned numbers: 0, 양수 표현

부호는 어디에 어떻게 나타내야할까?
간단하게는 MSB를 부호로 사용할 수 있습니다. 이해하기 쉽다는 장점이 있지만, 뺄셈을 지원하기 위해서는 추가적인 회로 설계가 필요합니다.

2's Complement

그래서 고안된 것이 2의 보수로 음수를 표현하는 방법입니다.

  • 모든 음수는 MSB에 1이 있음
  • 숫자가 양수인지 음수인지 확인하기 위해서는 한 비트만 확인하면 됨.
  • 변환 방법: 모든 비트를 반전(0->1, 1->0) 시키고 +1

sign extension

n-bit의 이진수를 m-bit의 이진수로 변환(m>n)
-> MSB(부호bit)를 가져와서 모든 비트를 채우도록 복제

Signed load에서 16bit 숫자를 32bit 레지스터에 저장할 때 부호 확장 방식을 사용

unsign extension

남은 bit를 0으로 차우도록 복제

unsigned load에서 사용

Question

opcode에 할당된 bit는 6bits이고, A와 B 타입 모두 6bits만큼을 사용하고 있기 때문에 26개의 명령어를 가질 수 있습니다.

Q1) 타입이 2개 존재하므로 MSB의 비트를 구분자로 사용하거나, opcode의 길이만으로 어떤 Type인지 구분하는 방법

Q2) MSB에 0 또는 1을 할당하는 방법이라면 25 + 210개의 명령어를 가질 수 있을 것이고,
길이만으로 구분을 할 수 있다면 26 + 211개의 명령어를 가질 수 있을 것이다.

Instruction Format

32bit Instruction은 여러 개의 field로 나누어집니다.

R-type을 기준으로 나누어보면
1. opcode: 어떠한 타입의 명령어인지 나타냅니다.
2. rd: definition register를 나타냅니다.
3. funct3: 특정한 타입의 명령어 중에서 어떠한 기능을 하는 명령어인지 나타냅니다.
4. rs1, rs2: 명령어에 사용될 소스 레지스터입니다.
5. funct7: 연산의 세부사항을 결정합니다.
+) imm: 연산에 즉시 추가될 값을 지정합니다.

R-Type(register type)

Arithmetic

  1. 두 개의 source 레지스터와 하나의 dstination 레지스터를 사용

  2. 오직 register operands만을 사용

    • Why? 레지스터가 메모리 접근보다 속도가 빠르고, 레지스터를 많이 갖고 있다

Format

  1. Assembly(어셈블리)

    ADD rd, rs1, rs2
  2. Semantics(의미론)

    GPR[rd] <- GPR[rs1] + GPR[rs2]
     PC <- PC + 4(명령어의 길이가 32-bit이기 때문에 4개 늘어남)
  3. Variations

    • Arithmetic: {ADD, SUB}
    • Compare: {signed, unsigned} x {SLT(Set if Less Than)}
    • Logic: {AND, OR, XOR}
    • Shift: {SLL, SRL, SRA (Left, Right-Logical, Right-Arithmetic)}
  • 세부 동작

    1. R-type의 opcode는 0110011로 동일

    2. ADD와 SUB를 하나로, shift 연산 두 개를 하나로 묶으면 총 9개의 세부 명령어가 존재하므로 funct3는 3bits만 할당

    3. RISC-V에서 범용레지스터는 32개이므로 레지스터마다 5bits를 할당

    4. funct7은 비트할당의 일관성을 이유로 7bits가 할당되어있어 128개의 세부 명령어를 만들 수 있지만, 미래확장성을 고려하여 전부 다 사용하고 있지는 않음

  • 명령어 리스트

Shift

  • SLL: rs1의 값을 rs2에 저장된 시프트 양만큼 왼쪽으로 이동시키고, 그 결과를 rd에 저장. 이후 LSB는 0비트로 대체, MSB는 버려짐 (= 2n을 곱하는 것과 같은 효과)

  • SRL: rs1의 값을 rs2에 저장된 시프트 양만큼 오른쪽으로 이동시키고, 그 결과를 rd에 저장. 이후 MSB는 0비트로 대체, LSB는 버려짐 (= 2n을 나누는 것과 같은 효과)

  • rs2의 값은 0~31: rs2에 있는 값의 하위 6비트만 사용

  • SRA: 똑같이 오른쪽으로 시프트 후 부호 비트로 채워줌

Compare

  • SLT(Set Less Than)는 signed comparision을 수행

    • 비교 연산이 True라면 rd에 1을, False라면 0을 저장
  • SLTU(Set on Less Than Unsigned)는 unsigned comparision을 수행

    • 만약 SLTU 1>3-1>3 이라면 max>3max>3 이므로 참이 됨. (절대값 연산이 아님)

Logical Bitwise

  • and (&)
  • or (|)
  • xor (^)

I-type

  • Arithmetic operation에서 rs2 대신 imm(constant) 사용
  • 자주 사용되는 것을 빠르게 하기 위한 방법

Format

  • Assembly
    	```
    	ADDI rd, rs1, imm
    	```
  • Semantics
    	```
    	GPR[rd] <- GPR[sr1] + sign-extend (imm)
    	PC <- PC +4
    	```
  • Variations
    • Arithmetic: {ADDI}

    • Compare: {signed, unsigned} x {SLTI(Set if Less Than)}

    • Logic: {ANDI, ORI, XORI}

    • Shift: {SLLI, SRLI, SRAI (Left, Right-Logical, Right-Arithmetic)}

      여기서 R-type과 다른 점은 SUBI가 없다는 것인데 그 이유는 imm에서 음수 표현까지 지원하기 때문.

      imm[11:0] = [-204810, 204710]

Question

  • add 명령어를 활용하라고 했기 때문에 g+0을 한 후 f에 넣는 방법을 사용
    -> add x4, x3, x0

  • rs1이 0이면 rd = 1, 아니라면 rd = 0

  • rs1에 대한 논리적 반전 연산을 실행(-1 = 111111...)

필드비트 수비트
imm = -5012111111001110
rs1 = 1500001
funct3 = ADD3000
rd = 15501111
opcode70010011

Load

레지스터는 제한적이고, 데이터는 보통 큽니다.

산술 연산은 레지스터에서 수행되어야 하므로 "데이터 전송 명령어(data transfer instruction)"가 필요합니다.

format
  • Assembly
    LW rd, offset(base register)
  • Semantics
    byte_address = sign-extend(offset) + GPR[base]
    GPR[rd] <- Mem[byte_adress]
    PC <- PC +4
  • Variations
    • LD (Double word)

    • LW (word)

    • LWU (Unsigned word)

    • LH (Half word)

    • LHU (Half unsigned word)

    • LB (byte)

    • LBU (Unsigned byte)

      여기서 Unsigned는 zero-extend를 뜻하며, 8bit를 불러 오고 나머지 비트는 0으로 채우는 것을 의미하며 signed는 부호 비트로 채우는 것을 의미합니다.

Question

필드비트 수비트
imm = 812000000001000
rs1 = 2500010
funct3 = LW3010
rd = 14501110
opcode70000011

  • 011 or 110

S-type

Store

  • 저장은 기본 메모리 주소, 저장할 데이터의 2개의 레지스터가 필요함
  • 저장은 레지스터 파일에 값을 쓰지 않음, 즉 rd가 없음
  • 또한 imm 필드를 두개로 나누는데, 이는 명령어 구조를 유사하게 하여 하드웨어의 복잡성을 줄이기 위함
format
  • Assembly
    SW rs2, offset(base register)
  • Semantics
    byte_address = sign-extend(offset) + GPR[base]
    Mem[byte_adress] <- GPR[rs2]
    PC <- PC +4
  • Variations
    • SD (Double word)
    • SW (word)
    • SH (Half word)
    • SB (byte)
Question

필드비트 수비트
imm = 8700000000
rs2 = 14501110
rs1 = 2500010
funct3 = sw3010
imm = 8501000
opcode70100011
profile
Just do it.

0개의 댓글