5-Stage Pipelined Processor (RISC-V)

image

위 그림은 5-stage pipeline을 표현한 도식도로 제가 직접 그렸습니다. (출처를 분명히 남겨주세요 :))

프로젝트 개요

RISC-V의 RV32I 명령어 집합을 전부 지원하는 5-stage 파이프라인 프로세서를 Verilog로 설계하고 구현한 프로젝트입니다. 단순한 단일 사이클 프로세서가 아니라, 실제 고성능 CPU에서 사용되는 파이프라이닝을 적용하고, 파이프라이닝에서 발생하는 데이터 해저드(Data Hazard)와 제어 해저드(Control Hazard)를 Hazard Detection Unit과 Forwarding Unit으로 해결했습니다.

시뮬레이션은 iverilog + GTKWave(Linux)로 수행했고, 테스트 프로그램은 직접 머신 코드로 변환해서 Instruction Memory에 주입했습니다.

RV32I 명령어 집합

RV32I는 RISC-V의 32비트 정수 기본 명령어 집합(Base Integer Instruction Set)입니다. 37개의 명령어로 구성되며, 다음과 같은 카테고리로 분류됩니다.

  • R-Type: 레지스터-레지스터 연산 (ADD, SUB, AND, OR, XOR, SLL, SRL, SRA, SLT, SLTU)
  • I-Type: 즉시값 연산 및 로드 (ADDI, ANDI, ORI, LW, LB, LH, JALR)
  • S-Type: 저장 명령 (SW, SB, SH)
  • B-Type: 조건 분기 (BEQ, BNE, BLT, BGE, BLTU, BGEU)
  • U-Type: 상위 즉시값 로드 (LUI, AUIPC)
  • J-Type: 점프 (JAL)

각 명령어는 32비트 고정 길이이며, opcode(7비트), funct3(3비트), funct7(7비트) 필드 조합으로 연산이 결정됩니다. RISC-V 사양의 단순함 덕분에 하드웨어 디코더가 비교적 간단합니다.

5-Stage 파이프라인 구조

파이프라인은 명령어 실행을 5개 단계로 나눠 동시에 여러 명령어를 처리합니다:

IF (Instruction Fetch)

PC(Program Counter)가 가리키는 주소에서 명령어를 Instruction Memory에서 가져옵니다. 매 사이클 PC는 +4씩 증가하거나, 분기 시 Branch Target으로 업데이트됩니다.

ID (Instruction Decode)

명령어를 해석합니다. opcode로 명령어 타입을 판별하고, Register File에서 rs1/rs2 레지스터 값을 읽어옵니다. 즉시값(Immediate)은 명령어 타입에 따라 sign-extend 해서 생성합니다. 또한 Control Unit이 각 단계에서 필요한 제어 신호들(RegWrite, MemRead, MemWrite, ALUOp 등)을 생성합니다.

EX (Execute)

ALU가 실제 연산을 수행합니다. ADD/SUB/AND/OR/XOR 같은 기본 연산부터 SLT(Set Less Than), SLL(Shift Left Logical) 같은 비교/시프트 연산까지 모두 처리합니다. 브랜치 명령의 경우 여기서 조건을 평가하고 Branch Target을 계산합니다.

MEM (Memory Access)

Load/Store 명령만 이 단계를 사용합니다. Data Memory에 주소를 주고 읽기/쓰기를 수행합니다. Load 명령은 메모리에서 읽은 값을 다음 단계로 전달하고, Store 명령은 rs2 값을 메모리에 쓰고 끝납니다.

WB (Write Back)

ALU 결과나 메모리에서 읽은 값을 Register File의 rd 레지스터에 기록합니다. 이 단계가 끝나야 다른 명령어가 해당 레지스터 값을 안전하게 읽을 수 있습니다.

단일 사이클 프로세서는 한 명령어를 처리하는 데 5 단계의 시간이 필요하지만, 파이프라이닝을 하면 매 사이클마다 한 명령어씩 완료되어 이론적 처리량이 5배가 됩니다. 실제로는 해저드 때문에 이만큼 나오지는 않지만 여전히 큰 성능 향상입니다.

Hazard Detection Unit

파이프라이닝에서 가장 큰 문제는 해저드입니다. Data Hazard가 대표적인데, 예를 들어:

ADD x1, x2, x3   # x1 = x2 + x3
ADD x4, x1, x5   # x4 = x1 + x5 (x1을 읽으려 하지만 아직 WB 안 됨)

두 번째 명령어는 첫 번째 명령어가 WB 단계까지 가야 x1에 쓴 값을 읽을 수 있습니다. 그 전에 EX에 도달하면 옛날 값을 읽게 됩니다.

Load-Use Hazard는 특히 Forwarding으로도 해결 못하는 경우입니다:

LW x1, 0(x2)     # x1 = Mem[x2] (MEM 단계에서 값 확정)
ADD x4, x1, x5   # x1 필요 (EX 단계에서)

이 경우 Hazard Detection Unit이 Load 다음 명령어에서 Load의 rd를 rs1/rs2로 사용하는지 체크해서 한 사이클 stall을 삽입합니다. 구체적으로는 IF/ID 레지스터와 PC를 현재 값으로 유지(홀드)하고, ID/EX 레지스터에 NOP(버블)을 삽입합니다.

Forwarding Unit

대부분의 Data Hazard는 Stall 없이 Forwarding으로 해결 가능합니다. Forwarding은 이전 명령어의 결과가 확정되는 위치(EX 단계 끝 또는 MEM 단계 끝)에서 아직 레지스터에 쓰이지 않은 값을 직접 다음 명령어의 ALU 입력으로 보내주는 기법입니다.

Forwarding Unit은 다음 조건을 체크해서 ForwardA, ForwardB 신호를 생성합니다:

  • EX → EX forwarding: EX/MEM 레지스터의 RegWrite이 1이고, EX/MEM.rd != 0이며, EX/MEM.rd == ID/EX.rs1 (또는 rs2)
  • MEM → EX forwarding: MEM/WB 레지스터의 RegWrite이 1이고, MEM/WB.rd != 0이며, 위 조건에 해당 안 하고 MEM/WB.rd == ID/EX.rs1 (또는 rs2)

ForwardA/B에 따라 ALU 입력 MUX가 레지스터 값 / EX/MEM 결과 / MEM/WB 결과 중 하나를 선택합니다.

Control Hazard

분기 명령이 Taken인지 판정되는 시점은 EX 단계 끝입니다. 그때까지 IF/ID 단계에서 이미 두 개의 명령어가 들어와 있는데, 만약 분기가 Taken이면 이 두 명령어는 잘못된 경로의 명령어이므로 취소(flush)해야 합니다.

구현에서는 분기 확정 시 IF/ID와 ID/EX 레지스터의 제어 신호를 모두 0으로 만들어 NOP로 처리합니다. 이로 인해 분기마다 2 사이클의 패널티가 발생합니다. 더 고도화하려면 Branch Prediction이 필요하지만, 이 프로젝트에서는 단순히 Not-Taken 예측 + Flush로 처리했습니다.

Waveform 분석

REG와 MEM의 데이터를 확인함으로써, RISC-V의 instruction을 잘 수행하고 있음을 알 수 있습니다. Instruction은 머신 코드로 변환하여 Instruction Memory에 저장하였습니다.

이미지

이미지

이미지

이미지

이미지

이미지

각 파형에서 다음을 확인했습니다: PC가 매 사이클 정확히 증가하는지, 명령어가 5단계를 올바르게 통과하는지, Forwarding 발생 시 올바른 값이 ALU에 전달되는지, Hazard 발생 시 Stall/Flush가 제대로 일어나는지.

느낀 점

  • 파이프라이닝의 복잡도: 단일 사이클에서 파이프라인으로 가는 순간, 해저드 처리에 설계 노력의 대부분이 들어갑니다. 성능 향상에는 비용이 따르는 것이 확실합니다.
  • 모듈화의 중요성: 각 단계를 독립 모듈로 만들고 파이프라인 레지스터로 연결하니 디버깅이 훨씬 쉬웠습니다.
  • RISC-V 사양의 우수함: 명령어 포맷이 일관되어 디코더가 간결합니다. x86 같은 가변 길이 CISC와는 차원이 다릅니다.
  • 시뮬레이션 도구: iverilog + GTKWave 조합은 무료인데도 충분히 강력합니다. 상용 Vivado Simulator 없이도 충분히 학습 가능합니다.

이 프로젝트는 컴퓨터 구조의 이론이 실제 하드웨어로 구현되는 과정을 처음부터 끝까지 경험할 수 있는 좋은 기회였습니다. 교과서의 Fetch-Decode-Execute-Memory-Writeback 5단계 다이어그램이 실제 Verilog 코드와 Waveform으로 살아 움직이는 것을 확인하면서, 추상적인 개념이 구체적인 이해로 바뀌었습니다.

비슷한 글 추천

Comments (0)

No comments yet. Be the first to comment!