Back to showcase
RV32I

RISC-V RV32I

Modern 32-bit RISC-V processor implementing the base integer instruction set. Includes both single-cycle and 5-stage pipelined implementations with hazard detection and forwarding.

32-bit RV32I ISA 47 Instructions 5-Stage Pipeline Forwarding Branch Prediction

Pipeline Architecture

IF

Instruction fetch, PC+4, branch target

ID

Decode, register read, immediate gen

EX

ALU, branch resolve, forwarding MUX

MEM

Data memory read/write

WB

Register write-back

forwarding unit (EX→EX, MEM→EX)
hazard detection (load-use stall)
branch predictor (static not-taken)

Specifications

ParameterValue
ISARISC-V RV32I (base integer, 47 instructions)
Data width32 bits
Registers32 general-purpose (x0 hardwired to zero)
Instruction typesR, I, S, B, U, J formats
ALU operationsADD, SUB, AND, OR, XOR, SLL, SRL, SRA, SLT, SLTU
Branch typesBEQ, BNE, BLT, BGE, BLTU, BGEU
Memory opsLB/LH/LW/LBU/LHU (load), SB/SH/SW (store)
Pipeline5-stage (IF, ID, EX, MEM, WB) with forwarding
Hazard handlingData forwarding, load-use stall, branch flush

RHDL Implementation

The instruction decoder extracts fields from all six RISC-V instruction formats and generates the immediate value using the sign-extension logic specified by the ISA.

decoder_rv32i.rb
class DecoderRV32I < RHDL::Sim::Component
  input  :instr,    width: 32
  output :rs1,      width: 5
  output :rs2,      width: 5
  output :rd,       width: 5
  output :imm,      width: 32
  output :alu_op,   width: 4
  output :mem_read, :mem_write
  output :reg_write, :branch

  behavior do
    opcode = instr[6..0]
    funct3 = instr[14..12]
    funct7 = instr[31..25]

    rs1 <= instr[19..15]
    rs2 <= instr[24..20]
    rd  <= instr[11..7]

    # Immediate generation
    imm <= case_select(opcode, {
      0b0010011 => sign_ext(instr[31..20]),
      0b0100011 => sign_ext(
        { instr[31..25], instr[11..7] }),
      0b1100011 => sign_ext(
        { instr[31], instr[7],
          instr[30..25],
          instr[11..8], 1'b0' }),
    })
  end
end

The forwarding unit resolves data hazards by detecting when a source register matches a pending write-back destination and selecting the forwarded value.

forwarding_unit.rb
class ForwardingUnit < RHDL::Sim::Component
  # Source registers (ID/EX stage)
  input  :rs1,         width: 5
  input  :rs2,         width: 5
  # EX/MEM stage write-back
  input  :ex_mem_rd,   width: 5
  input  :ex_mem_wr
  # MEM/WB stage write-back
  input  :mem_wb_rd,   width: 5
  input  :mem_wb_wr
  # Forward select (0=reg, 1=EX, 2=MEM)
  output :fwd_a,       width: 2
  output :fwd_b,       width: 2

  behavior do
    # Priority: EX/MEM over MEM/WB
    fwd_a <= cond(
      ex_mem_wr & ex_mem_rd.neq?(0)
        & ex_mem_rd.eq?(rs1),
      1,
      cond(
        mem_wb_wr & mem_wb_rd.neq?(0)
          & mem_wb_rd.eq?(rs1),
        2, 0))

    fwd_b <= cond(
      ex_mem_wr & ex_mem_rd.neq?(0)
        & ex_mem_rd.eq?(rs2),
      1,
      cond(
        mem_wb_wr & mem_wb_rd.neq?(0)
          & mem_wb_rd.eq?(rs2),
        2, 0))
  end
end

The pipelined processor top-level connects all five stages with pipeline registers, forwarding unit, and hazard detection logic.

rv32i_pipeline.rb
class RV32IPipeline < RHDL::Sim::SequentialComponent
  input  :clk, :rst
  output :imem_addr,  width: 32
  input  :imem_data,  width: 32
  output :dmem_addr,  width: 32
  output :dmem_wdata, width: 32
  output :dmem_wr
  input  :dmem_rdata, width: 32

  # Pipeline stages
  subcomponent :fetch,   FetchStage
  subcomponent :decode,  DecodeStage
  subcomponent :execute, ExecuteStage
  subcomponent :memory,  MemoryStage
  subcomponent :wb,      WriteBackStage

  # Hazard handling
  subcomponent :fwd,     ForwardingUnit
  subcomponent :hazard,  HazardDetector
  subcomponent :regfile, RegisterFile,
    depth: 32, width: 32

  sequential clock: :clk, reset: :rst do
    # Pipeline register updates
    # Stall IF/ID on load-use hazard
    # Flush IF/ID + ID/EX on branch taken
    # Forward EX and MEM results to EX inputs
  end
end