2. RV32I Architecture - Block Diagram Design

손은상·어제

RISC-V

목록 보기
5/5

개요

  • 이 글은 RV32I single-cycle processor기본 모듈Block Diagram 설계 과정을 다룬다.
  • 먼저 single-cycle 구조에서 필요한 최소한 state 요소를 정리하고, 이후 설계철학을 바탕으로 각 Instruction type의 데이터 흐름을 분석한다.


1. Single-cycle 구조에서 State 요소

Single-cycle processor 에서는 하나의 Instruction이 한 Clock 주기 안에 완료 되어야 한다.

Data path 대부분의 연산을 combinational circuit으로 구현하고, **state(상태)를 갖는 요소를 최소한으로 제한하는 것이 구조를 단순하게 만든다.

본 설계는 state를 갖는 PC, Register File, Memory를 sequential logic으로 구현했고,
나머지 연산은 combiantial logic으로 구현했다.

다만, 하나의 clock 내에 연산을 끝내기 위해, Register FileMemoryReading를 combiantional logic으로, 무결성을 위해 Writing을 sequential logic으로 구현했다.

이러한 선택은 제어를 단순하게 만들고 설계 흐름을 명확하게 하지만,
clock 주기가 전체 연산 경로에 의해 제한된다는 trade-off를 가진다.
이는 single-cycle 구조가 가지는 근본적인 한계이기도 하다.


2. CPU 기본 모듈

Instruction cycle
Fetch -> Decode -> Excute -> Memory -> Write_back

크게 6가지 Module으로 위 cycle을 구현한다.

2-1. Program Counter (PC)

  • 명령어 주소 계산. 계산한 명령어 주소는 Instruction Memory의 input으로 들어가 명령어를 찾는다.

  • 각 명령어를 구분하는 기준이 되는 module이다.

  • 따라서 clk에 따라 값을 갱신하고, sequential circuit로 구현한다.

2-2. Instruction Memory

  • 명령어들을 저장하는 Memory. 여기 다 쓸 필요는 없으므로, ROM으로 구현한다. 따라서 combinational circuit로 구현한다.

2-3. Register File

  • Register들을 저장하는 메모리 장소이다. 총 32개의 레지스터가 존재하며 각 레지스터는 32 bit의 크기를 갖는다.
Figure 2. R-type RV32I Register type
  • 두 개의 source register (rs1, rs2)를 동시에 읽고, 하나의 destination register (rd) 에 값을 저장하도록 만들었다.

  • 레지스터에 Writingpostive clock edge에만 발생하도록해 무결성을 보장했다.

  • Reading은 즉각적인 값 전달을 위해 combinational circuit으로 구현한다.

2-4. ALU

  • 계산만 하면 되는 module이다. R-type에 존재하는 계산 방식을 모두 가지는 combinational circuit로 구현하면 될 듯하다.

2-5. Data Memory

  • 무결성을 위해 clk에 따라 값의 입력과 출력이 결정되게 구현한다. 즉 sequence circuit.
  • DRAM의 역할을 할 예정이며, memory address가 총 32 bit이니 이에 맞춰 용량을 구현할 예정이다.

2-6. Control Unit

  • Instruction type과 그들의 종류에 따라 module의 작동을 세부적으로 control 할 module이다. 비트단위 signal을 보내 각 module을 통제한다. combinational circuit으로 구현한다.



3. 설계 철학

Computer Organization Design 에서 설계 철학을 강조한다. 따라서 나만의 설계철학을 바탕으로 구현했다.

1. ISA 중심 설계

  • RISC-V ISA의 각 명령어 타입 (R, I, S, B, J ,U)를 하나씩 이해하며 필요한 데이터 경로와 모듈을 점진적으로 추가하는 (Bottom up) 방식을 채택
    (ISA 내용 : https://velog.io/@liquetxnx22/1.-%EC%9D%B4%EB%A1%A0-RV32I-ISA-%EC%A0%95%EB%A6%AC)

  • R-type 명령어는 memory access와 branch를 포함하지 않기 때문에, 가장 단순한 data path를 가진다. 이 data path를 바탕으로 Instruction type의 경로를 확장해 나간다.

  • 이유 : 직접 설계해보며 하드웨어 설계 감각 증진

2. Simplicity favors regularity

  • 예외적인 전용 경로 추가보다, 기존 경로 재사용을 늘리고, 규칙적인 구조 유지를 우선함

  • 예시 :

    • AUIPC 명령어는 Branch adder가 아닌 ALU에서 PC + IMM으로 처리

    • PC 선택PC MUX로 통합 등

  • 이유 : 구조가 규칙적이면 제어가 단순해지고 확장이 쉬워진다.

3. 명확한 역할 분리

  • Control unitOpcode 기반의 정적 제어만 생성.
    (각 Instruction type에 따른 signal만 생성)

0-2. Top module

가장 추상화된 top module을 설계한다. 크게 Data가 흐르는 Data Path와 그 Data의 흐름을 제어하는 Control Path로 나눈다.

Data path는 명령어가 각 값들을 계산하는 과정을 모두 나타낸다.
Control pathControl Unit으로 구현하며, 명령어의 Opcode, funct7, funct3의 정보를 받고 명령어의 계산 과정을 제어할 신호를 보낸다.

이후 각 Instruction type에 따라 data path를 설계하고 그에 맞는 필요한 control signal을 만든다.

1. R-type

Figure 3. R-type 명령어 처리 흐름도
  • Register_Filers1, rs2, rd 정보를 얻어야 하니 이것들을 input으로 갖는다. 또한 계산결과를 레지스터파일에 다시 저장해야하니 또다른 input, WD을 갖는다.

  • Control_UnitOpcode, funct7, funct3input으로 입력받는다.

    • OpcodeInstr type을 결정한다

    • 위 예시는 R-type임으로 funct7, funct3에 따라 무슨 계산을 할지, ALU에 알려줘야하는 signal이 필요하다. -> 총 10가지 명령어이니, ALU_CONTROL [3:0] 으로 구현
      또한

    • Register_File에 결과값을 입력해야 한다. 즉 Register_File에 작성 요청 signal이 필요하다. (RegWrite)

    • 따라서 output으로 [3:0] ALU_controlRegWrite을 갖는다.


2. I-type

2-1 IMM 값 연산 명령어

Figure 4. I-type 명령어 처리 흐름도
Figure 5. R-type 명령어 처리에서 추가된 부분 (분홍색)
  • I-typeIMM 값을 계산 인자로 사용하기에 ImmGen이라는 sign-extension 수행 block이 필요했다.

  • ImmGenInstr의 12bit를 sign-extension을 시행하는 block 이다.

  • Instr 상에서 Imm 값의 위치는 각 Instr type 마다 다르다. 따라서 ImmSrc를 추가해 무슨 타입인지 ImmGen에 알려 적절히 상수값 계산을 하게 돕는다.

ImmGen이 Opcode을 입력받지 않고 ImmSrc로 따로 입력받아 Instruction type 결정하는 이유

-> 모듈간 역할을 정확히 분리하기 위해서. Control unit은 signal만 보내고, Immgen은 부호 확장만 하게해 모듈을 명확히 추상화해 분리한다. 이를 통해 유지 보수성과 파이프라인 확장성에 더 도움이 된다.
->예를 들어 내가 Hazard 같은거 구현하려 할때, Immgen은 그대로 두고 Control unit만 업데이트하면 됨.

  • Imm 값과, 기존 Register File의 값 중 무슨 값을 ALUinput으로 사용할지 결정하는 ALU MUX를 추가했다.
  • 또한 ALU MUX의 값을 결정하는 ALUSrc을 새롭게 Control Unit에서 받는다.

(후에 알게된 사실인데 ALU Decoder을 굳이 module로 둘 필요는 없다. 각 module들을 top module에서 이을 때, top module에 mux로 구분지으면 될 듯하다.)


2-2 LW 명령어와 JALR 명령어

Figure 6. 전 단계에서 추가된 부분(분홍색)

LW 명령어

LW 명령어의 Data FlowControl Flow는 다음과 같다.

Figure 7. LW 명령어의 흐름도
  • LW 명령어는 1. 주소값 계산 후, 2.주소에 맞는 Data를 메모리에서 찾아, 3. Register File에 저장한다.

  • Data Loading을 위한 Memory block이 추가되었다.

  • 기존 ALU 연산 결과Memory Data Register File에 저장될 값을 고르는 Write Back Mux를 추가했다.

JALR 명령어

Figure 8. JALR 명령어의 흐름도
  • JALR 명령어는 1. 주소값 계산 후, 2. 계산한 주소 값을 다음 분기 주소로 사용하고, 3. 기존의 분기 주소는 Register File에 저장한다.

  • 기존에 4-byte 씩 증가시켰던 Instr address에 더불어, 새롭게 분기할 주소를 결정할 필요가 있었다.

-따라서 PC block을 변경하려 했는데, 그 과정 중 Chip Design 설계철학을 고려하기 시작했다.

  • 따라서 PC adder 라는 일반적인 4-byte

  • 기존 ALU 연산 결과Memory Data Register File에 저장될 값을 고르는 Write Back Mux를 추가했다.

profile
1렙 대학생

0개의 댓글