Stage-by-Stage Datapath

In a single-cycle CPU, every instruction—fetch, decode, execute, memory, and write-back—completes in exactly one clock tick. Below we’ll walk through the high-level datapath, then zoom into each stage to see how data and control signals flow.

  flowchart TB

  PC["PC"]

  IM["Instruction Memory<br/>(32-bit instruction)"]

  CU["Control Unit<br/>(decodes opcode)"]

  RF["Register File"]

  IMGen["Immediate Generator"]

  ALU["ALU"]

  BC["Branch Comparator"]

  Adder4["+4 Adder"]

  BranchMux{"PC Mux<br/>(select PC+imm or PC+4)"}

  DM["Data Memory"]

  WB["Writeback Mux<br/>(select ALU result or memory data)"]

  

  %% PC and instruction memory

  PC -- "PC address" --> IM

  BranchMux -- "next PC" --> PC

  

  %% Instruction memory outputs

  IM -- "opcode<br/>(instr[6:0])" --> CU

  IM -- "funct3, funct7<br/>(instr[14:12], instr[31:25])" --> CU

  IM -- "rs1 idx<br/>rs2 idx<br/>rd idx" --> RF

  IM -- "imm bits" --> IMGen

  

  %% control signals

  CU -- "ALUOp<br/>funct3<br/>funct7" --> ALU

  CU -- "BranchSel" --> BC

  CU -- "MemRead<br/>MemWrite<br/>MemtoReg<br/>RegWrite" --> WB

  

  %% register file I/O

  RF -- "rs1 data<br/>rs2 data" --> ALU

  RF -- "rs1 data<br/>rs2 data" --> BC

  WB -- "result<br/>RegWrite<br/>rd idx" --> RF

  

  %% immediate

  IMGen -- "imm" --> ALU

  

  %% ALU outputs

  ALU -- "ALU result (addr/data)<br/>calc result" --> DM

  ALU -- "ALU result" --> WB

  

  %% data memory I/O

  RF -- "rs2 data (store data)" --> DM

  DM -- "read data" --> WB

  

  %% branch & PC update

  PC -- "PC" --> Adder4

  Adder4 -- "PC+4" --> BranchMux

  BC -- "branchTaken" --> BranchMux

Stages

1. Instruction Fetch (IF)

The IF stage uses the program counter (PC) to read a 32-bit instruction from memory. Simultaneously, a +4 adder computes the address of the next sequential instruction.

  ---
config:
  layout: elk
  theme: default
  look: classic
---
flowchart LR
  subgraph IF [Instruction Fetch]
    PC["PC"]
    IM["Instruction Memory<br>(32-bit instruction)"]
    Adder4["+4 Adder"]
    BranchMux{"PC Mux<br>(select PC+imm or PC+4)"}
    %% Internal fetch interactions
    PC -- "PC" --> IM
    PC -- "PC" --> Adder4
    Adder4 -- "PC+4" --> BranchMux
    BranchMux -- "Next PC" --> PC
  end
  %% Cross-stage signals
  IM_out(["Instruction (output)"])
  IM -- "Instruction" --> IM_out
  style IF fill:#E0F7FA,stroke:#006064,stroke-width:2px

2. Instruction Decode (ID)

In ID, the Control Unit decodes opcode bits to generate control signals, the Register File (RF) reads source registers, and the Immediate Generator extracts any immediate operand.

  ---
config:
  layout: elk
  theme: default
  look: classic
---
flowchart LR
  subgraph ID [Instruction Decode]
    CU["Control Unit<br>(decodes opcode)"]
    RF["Register File"]
    IMGen["Immediate Generator"]
  end
  %% Cross-stage signals
  IM_input(["Instruction (input)"])
  EX_output(["Control Signals (output)"])
  RD_output(["Register Data (output)"])
  IMM_output(["Immediate (output)"])
  IM_input -- "Instruction" --> CU
  IM_input -- "Instruction" --> RF
  IM_input -- "Instruction" --> IMGen
  CU -- "Control Signals" --> EX_output
  RF -- "Register Data" --> RD_output
  IMGen -- "Immediate" --> IMM_output
  style ID fill:#E8F5E9,stroke:#2E7D32,stroke-width:2px

3. Execute (EX)

During EX, the ALU performs arithmetic/logical operations using register data and immediates, while the Branch Comparator determines if a branch should be taken.

  ---
config:
  layout: elk
  theme: default
  look: classic
---
flowchart LR
  subgraph EX [Execute]
    ALU["ALU"]
    BC["Branch Comparator"]
  end
  %% Cross-stage signals
  CTRL_input(["Control Signals (input)"])
  RD_input(["Register Data (input)"])
  IMM_input(["Immediate (input)"])
  MEM_output(["Memory Access<br>(output)"])
  WB_input(["Write Back<br>(output)"])
  CTRL_input -- "Control Signals" --> ALU
  CTRL_input -- "Control Signals" --> BC
  RD_input -- "Read Data" --> ALU
  RD_input -- "Read Data" --> BC
  IMM_input -- "Immediate" --> ALU
  ALU -- "ALU Result" --> MEM_output
  ALU -- "ALU Result" --> WB_input
  BC -- "Branch Taken?" --> MEM_output
  style EX fill:#FFF3E0,stroke:#EF6C00,stroke-width:2px

4. Memory Access (MEM)

On loads and stores, the Data Memory stage reads or writes data using the address computed by the ALU.

  ---
config:
  layout: elk
  theme: default
  look: classic
---
flowchart LR
  subgraph MEM [Memory Access]
    DM["Data Memory"]
  end
  %% Cross-stage signals
  ALU_out(["ALU Result (input)"])
  WB_out(["Memory Access<br>(input)"])
  ALU_out -- "ALU Result" --> DM
  DM -- "Memory Data" --> WB_out
  style MEM fill:#F3E5F5,stroke:#6A1B9A,stroke-width:2px

5. Write-Back (WB)

Finally, the Write-back Mux selects between the ALU result and memory data, writing the chosen value back into the Register File.

  ---
config:
  layout: elk
  theme: default
  look: classic
---
flowchart LR
  subgraph WB [Write Back]
    WB_mux["Writeback Mux<br>(select ALU result or memory data)"]
    RF["Register File"]
    %% Internal writeback interaction
    WB_mux -- "Write Data" --> RF
  end
  %% Cross-stage signals
  ALU_sig(["ALU Result (input)"])
  DM_sig(["Memory Data (input)"])
  ALU_sig -- "ALU Result" --> WB_mux
  DM_sig -- "Memory Data" --> WB_mux
  style WB fill:#FFEBEE,stroke:#C62828,stroke-width:2px