Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

miniRISC32

A 32-bit multi-cycle RISC processor designed from scratch in Verilog. Custom ISA, structural ALU, hardwired FSM control unit, and BRAM-based memory — synthesized and tested on a Xilinx Artix-7 FPGA (Nexys A7) at 100 MHz.

Built as part of the Computer Organization and Architecture Laboratory course at IIT Kharagpur (Autumn 2025).

Architecture Overview

┌──────────────────────────────────────────────────────────────────────┐
│                     CONTROL PATH (FSM)                               │
│              23 states · hardwired · Moore-style                     │
│          FETCH → IF_WAIT → IR_LATCH → DECODE → EXEC → WB             │
└───────────────┬───────────────────────────────┬──────────────────────┘
                │ control signals               │ opcode/funct
┌───────────────▼───────────────────────────────▼──────────────────────┐
│                           DATAPATH                                   │
│                                                                      │
│  ┌────┐   ┌──────────┐   ┌──────┐   ┌────────┐                        │
│  │ PC │→→ │ Instr.   │→→ │ Reg  │→→ │  ALU   │                        │
│  │Unit│   │ Register │   │ File │   │ (16 op)│                        │
│  └────┘   └──────────┘   └──────┘   └────────┘                        │
│      ↕                         ↕                                     │
│  ┌─────────┐             ┌─────────┐                                 │
│  │  IMEM   │             │  DMEM   │                                 │
│  │ (BRAM)  │             │ (BRAM)  │                                 │
│  └─────────┘             └─────────┘                                 │
└──────────────────────────────────────────────────────────────────────┘

This is a multi-cycle processor (not single-cycle or pipelined). Each instruction takes 4–7 clock cycles, depending on the instruction type. Hardware resources such as the ALU, register file, and memories are reused across cycles, while intermediate registers (IR, ALU output register, register-data buffers) preserve values between execution stages.

flowchart TB
    CP["Control Path (23-state FSM)"]
    DP["Datapath"]
    PC["PC Unit"]
    IR["Instruction Register"]
    RF["Register File"]
    ALU["16-op ALU"]
    IM["Instruction Memory"]
    DM["Data Memory"]

    CP -->|control signals| DP
    DP -->|opcode/funct| CP
    PC --> IR --> RF --> ALU
    IM --> IR
    RF <--> DM
Loading

What's Implemented

ISA (Custom, MIPS-inspired)

  • 4 instruction formats: R-type (6-bit funct), I-type (16-bit immediate), J-type (26-bit target), and program control.
  • 17 R-type ALU operations: ADD, SUB, AND, OR, XOR, NOR, NOT, SLT, SGT, SL, SRL, SRA, INC, DEC, HAM (popcount), MOVE, CMOV (conditional move).
  • 15 I-type immediate operations: ADDI, SUBI, ANDI, ORI, XORI, NORI, SLTI, SLAI, SRLI, SRAI, INCI, DECI, NOTI, HAMI, LUI.
  • Memory: LD and ST with base+offset addressing.
  • Branches: BZ (zero), BMI (negative), BPL (positive), BR (unconditional), all PC-relative.
  • Subroutines: CALL (push return address to stack, jump) and RET (pop and jump back).
  • Control: NOP, HALT (stops the processor).

Full encoding tables are in docs/problem_statement.md and docs/GRP11_ISA_RISC.pdf.

ALU — Structural Design

The ALU is not a behavioral case statement over a + b. Each operation is a standalone module instantiated inside ALU_32.v:

Unit Modules Design
Arithmetic ADD_32, SUB_32, INC_32, DEC_32 Ripple-carry adder built from full_adder instances. Subtraction via complement-add.
Logic AND_32, OR_32, XOR_32, NOR_32, NOT_32 Bitwise combinational gates.
Shift SLL_32, SRL_32, SRA_32, LUI_32 Barrel-shift style using loop-based generation. SRA preserves sign bit.
Comparator SLT_32, SGT_32 Built on top of subtraction result and sign/overflow analysis.
Hamming HAM_32 Binary reduction tree — 16 full adders at level 1, widening adders at each subsequent level, producing a 6-bit popcount in O(log n) depth.

All submodules are parameterized (#(parameter N = 32)) and reused at different widths inside the Hamming tree.

Control Unit — 23-State Hardwired FSM

The FSM in controlpath.v implements Moore-style control. Key design decisions:

  • BRAM latency handling: Synchronous BRAM returns data one cycle after the address is presented. The FSM adds S_IF_WAIT and S_IR_LATCH states during fetch, and S_LD_WAIT during loads, to accommodate this.
  • Branch target pre-computation: During S_DECODE, the ALU speculatively computes PC + sign_ext(imm) for branches. Non-branch instructions increment the PC in this same state. Branch instructions defer the PC update to S_BRANCH, where flags are checked.
  • CALL/RET as multi-step sequences: CALL takes 3 states (decrement SP → write return address to memory → jump). RET takes 3 states (read return address → increment SP → jump).
  • HALT → IDLE: The S_IDLE state self-loops with all control signals deasserted. The halted output signals the FPGA wrapper to stop the clock and display results.

Cycle Counts by Instruction Type

Type States Traversed Cycles
R-type ALU FETCH→IF_WAIT→IR_LATCH→DECODE→EXEC_R→WB_ALU 6
I-type ALU FETCH→IF_WAIT→IR_LATCH→DECODE→EXEC_I→WB_ALU 6
LD FETCH→IF_WAIT→IR_LATCH→DECODE→MEM_ADDR→LD_READ→LD_WAIT→WB_LD 8
ST FETCH→IF_WAIT→IR_LATCH→DECODE→MEM_ADDR→ST_WRITE 6
Branch FETCH→IF_WAIT→IR_LATCH→DECODE→BRANCH 5
CALL FETCH→IF_WAIT→IR_LATCH→DECODE→CALL_SP_DEC→CALL_WRITE→CALL_JUMP 7
RET FETCH→IF_WAIT→IR_LATCH→DECODE→RET_READ→RET_SP_INC→RET_JUMP 7

Memory

  • Instruction memory: BRAM ROM, initialized from a .coe file via Vivado IP Catalog (Block Memory Generator). Read-only, 8-bit address (256 words).
  • Data memory: BRAM RAM with write-enable, same addressing scheme. Pre-loaded with test data via a separate .coe file.
  • Both memories are byte-addressable at the ISA level; the hardware converts to word-aligned BRAM addresses via addr[9:2].

FPGA Integration

The FPGA top module (src/fpga/fpgatop_booth.v) adds:

  • Button debouncer with configurable debounce time (10 ms at 100 MHz).
  • Three-state wrapper FSM (IDLE → RUN → DONE) — a single button press resets and starts the processor; HALT stops it.
  • Clock gating — the processor clock is AND-gated with processor_enable to freeze execution in IDLE/DONE.
  • Multiplexed LED output — a slide switch selects upper or lower 16 bits of the result register for display on the board's 16 LEDs.
  • Pin constraints (constraints.xdc) for the Nexys4 DDR / Nexys A7 board.

Custom Assembler

programs/assembler.py translates assembly source into .coe files for BRAM initialization. It supports the complete ISA including R-type, I-type, branches, LD/ST with offset(Rn) syntax, CALL/RET, HALT, and NOP.

Test Programs

1. Booth's Multiplication (programs/asm/booth.asm)

Multiplies two 16-bit signed integers using the radix-2 Booth algorithm.

Algorithm: Iterates 16 times. On each iteration, inspects the LSB of Q (multiplier) and Q₋₁ (previous LSB). If {Q[0], Q₋₁} = 10, subtract M from accumulator A. If {Q[0], Q₋₁} = 01, add M to A. Then arithmetic-right-shift the {A, Q} pair. The final 32-bit signed product is assembled from the upper (A) and lower (Q) halves.

Register allocation:

  • R1 = M (multiplicand), R2 = Q (multiplier, later result)
  • R3 = A (accumulator), R4 = Q₋₁, R5 = iteration counter

Test case: M and Q are loaded from data memory (mem.coe). With M = -16 (0xFFFFFFF0) and Q = 13 (0x0000000D), expected product = -208 (0xFFFFFF30).

Result: R2 = -208 after ~1500 cycles. Verified in simulation and on FPGA.

2. Hamming Weight Sum (programs/asm/ham.asm)

Computes the total popcount (Hamming weight) across 5 integers stored in data memory.

Test data (from mem.coe):

Address Value Hamming Weight
24 0x00000018 2
32 0x00000019 3
40 0x0000001A 3
48 0x0000001B 4
56 0x0000001C 3

Register allocation:

  • R3 = running total, R4 = loop counter (5 → 0)
  • R5 = memory pointer, R6 = loaded value, R7 = HAM result

Result: R3 = total Hamming weight sum. Verified in simulation and on FPGA via multiplexed LED output.

Project Structure

miniRISC32/
├── src/
│   ├── alu/                         # ALU and submodules
│   │   ├── ALU_32.v                 # Top-level ALU (16-op MUX)
│   │   ├── ALU_tb.v                 # ALU unit testbench
│   │   ├── arithmetic/              # ADD, SUB, INC, DEC, full_adder
│   │   ├── logic/                   # AND, OR, XOR, NOR, NOT
│   │   ├── shift/                   # SL, SRL, SRA, LUI
│   │   ├── comparator/              # SLT, SGT
│   │   ├── ham/                     # HAM (popcount via reduction tree)
│   │   └── mux/                     # 2:1, 4:1, 8:1, 16:1 multiplexers
│   ├── core/                        # Processor core
│   │   ├── top_module.v             # Top-level integration
│   │   ├── datapath.v               # Registers, ALU, MUXes, buffers
│   │   ├── controlpath.v            # 23-state hardwired FSM
│   │   ├── pc_unit.v                # Program counter with 4-way next-PC MUX
│   │   ├── reg_bank.v               # 32-entry register file (R0 = 0)
│   │   └── sign_extend.v            # 16→32 sign/zero extension
│   └── fpga/                        # FPGA-specific modules
│       ├── fpgatop_booth.v          # Board wrapper for Booth test
│       ├── fpgatop_ham.v            # Board wrapper for Hamming test
│       ├── debouncer.v              # Button debouncer
│       └── constraints.xdc          # Nexys A7 pin assignments
├── programs/
│   ├── asm/                         # Assembly source files
│   │   ├── booth.asm
│   │   └── ham.asm
│   ├── coe/                         # BRAM initialization files
│   │   ├── booth.coe
│   │   ├── ham.coe
│   │   └── mem.coe
│   └── assembler.py                 # Custom assembler (.asm → .coe)
├── sim/
│   ├── tb_booth.v                   # Booth multiplier testbench
│   └── tb_ham.v                     # Hamming weight testbench
├── docs/
│   ├── problem_statement.md         # Full project specification
│   └── GRP11_ISA_RISC.pdf           # ISA design report
└── .gitignore

How to Build and Run

Prerequisites

  • Xilinx Vivado Design Suite (tested with Vivado 2023.x / 2024.x)
  • Digilent Nexys4 DDR or Nexys A7-100T board (for FPGA demo)
  • Python 3.x (for the assembler)

Simulation (Vivado)

  1. Create a new Vivado project targeting xc7a100tcsg324-1.
  2. Add all .v files under src/ as design sources.
  3. Add sim/tb_booth.v or sim/tb_ham.v as simulation sources.
  4. Use Vivado IP Catalog → Block Memory Generator to create BRAM IPs:
    • blk_mem_gen_booth: Single-port ROM, width=32, depth=256, init from programs/coe/booth.coe (or ham.coe).
    • blk_mem_gen_mem: Single-port RAM, width=32, depth=256, init from programs/coe/mem.coe.
  5. Run behavioral simulation. Set simulation time to at least 10 µs.
  6. Check the transcript for PASS/FAIL messages.

FPGA Synthesis

  1. Set fpgatop_booth (or fpgatop_ham) as the top module.
  2. Add src/fpga/constraints.xdc as a constraints file.
  3. Run Synthesis → Implementation → Generate Bitstream.
  4. Program the Nexys A7 board.
  5. Press BTNC (reset) — the processor runs the program and halts.
  6. Toggle SW[0] to view upper/lower 16 bits of the result on the LEDs.

Assembler Usage

python3 programs/assembler.py programs/asm/booth.asm programs/coe/booth.coe

Design Decisions and Tradeoffs

Multi-cycle vs. single-cycle: Allowed hardware reuse (one ALU for everything) and a faster clock, at the cost of FSM complexity. A real next step would be pipelining.

Structural ALU: The assignment required building from gates up. The ripple-carry adder is O(n) delay but straightforward to implement and verify. The Hamming weight tree is a genuinely efficient O(log n) design.

BRAM vs. register arrays: BRAM is area-efficient and industry-standard for on-chip memory. It introduces one cycle of read latency, which required adding wait states to the FSM — a real-world hardware design constraint.

Hardwired vs. microprogrammed control: Hardwired is faster (combinational logic) but harder to extend. For a fixed ISA this is the right call.

Clock gating for HALT: Rather than a global enable, the FPGA wrapper AND-gates the clock with processor_enable. Simple but effective for a demo; in production you'd use Vivado's clock-enable primitives.

Authors

  • Dasari Veera Venkata Abhinav Teja (23CS10017)
  • Pulugu Lokeswara Reddy (23CS10065)

IIT Kharagpur, Department of Computer Science and Engineering

About

Custom 32-bit multi-cycle RISC processor designed in Verilog, synthesized and tested on Nexys A7 FPGA (Xilinx Vivado)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages