Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RV32I Single-Cycle RISC-V Processor

A fully functional 32-bit single-cycle RISC-V (RV32I) processor, designed in Verilog and verified through simulation, synthesis, and implementation in Xilinx Vivado 2025.2, targeting an Artix-7 (xc7a35tftg256-1) device. The design implements and verifies all 37 base RV32I instructions.

S. Vignesh · P. Vineeth Goud


Table of Contents


Overview

This project implements a single-cycle, non-pipelined RV32I processor core that fetches, decodes, and executes one instruction per clock cycle. It supports the full RV32I base integer instruction set: R-type, I-type, S-type, B-type, U-type, and J-type instructions, including all arithmetic/logic, shift, comparison, load/store (byte, halfword, word — signed & unsigned), branch, and jump instructions.

The design is self-checked by a comprehensive testbench (tb.v) that executes and verifies 104 instructions covering all 37 RV32I opcodes, checking:

  • The PC value at every fetch
  • The exact instruction encoding fetched from ROM
  • The architectural register write (or memory write, for stores) resulting from each instruction

The design has been synthesized, placed, and routed in Vivado targeting a Xilinx Artix-7 (xc7a35tftg256-1) part, with all timing constraints met. All results in this repository (simulation, synthesis, and implementation) were produced entirely within Vivado's simulator and implementation flow — the design has not been deployed onto a physical FPGA board.


Architecture

The processor follows the classic single-cycle datapath: an instruction is fetched from ROM using the Program Counter, decoded by the control unit, operands are read from the register file, the ALU (or branch comparator) computes a result, data memory is optionally accessed, and the result is written back to the register file — all within a single clock cycle.

Architecture of Single Cycle RISC-V Processor Fig. 1 — Architecture of the Single-Cycle RISC-V Processor.

Datapath walkthrough (per the diagram above):

  1. Fetch — The Program Counter (clocked by a Clock Divider off clk) addresses Instruction Memory, which returns the 32-bit instruction. Next PC (PC + 4, via an adder) and the branch/jump target compete at a pc_sel-controlled mux to produce the next PC value.
  2. Decode — The instruction feeds the Control Unit, which decodes opcode/fun3/fun7 into all downstream control signals: pc_sel, wr_en, sign_sel, rd1_sel, rd2_sel, branch_sel, alu_ctl, dm_wr, dm_sel, dm_sel1.
  3. Register read & immediate generationrs1/rs2 fields index the General Purpose Register file (rd1, rd2 outputs); the Imm Value Block (controlled by sign_sel) sign/zero-extends the immediate for I/S/B/U/J formats.
  4. Operand select — Two muxes choose the ALU's operands: operand A is rd1 or PC (for AUIPC/branches), and operand B is rd2 or the extended immediate (for ALU-immediate/loads/stores).
  5. Execute — The ALU (controlled by alu_ctl) performs the arithmetic/logic operation; in parallel, the Branch Comparator evaluates br_eq/br_gt from rd1/rd2, gated by branch_sel, to drive pc_sel for taken branches.
  6. Memory access — The ALU result addresses Data Memory. Writes are qualified by dm_wr and formatted per Inst[14:12] (byte/half/word — the store-formatting logic). Reads pass through an Extension Block (Inst[14:12] selects sign/zero-extension for lb/lh/lw/lbu/lhu).
  7. Writeback — A dm_sel mux chooses between the ALU result and loaded/extended memory data; a further dm_sel1 mux chooses between that value and PC+4 (for JAL/JALR link register writeback). The selected result is written back into the register file when wr_en is asserted.
  8. External I/O (as drawn)f_in[7:0] feeds the input data location data_mem[0], and f_out exposes the data at data_mem[1] — a simple external data-memory-mapped I/O hook used in this version of the datapath diagram.

Note: this figure documents the general single-cycle RV32I datapath concept used to design the RTL. The actual Verilog module/port names in this repository map 1:1 onto the blocks above (e.g. control_unit.v = Control Unit, registerfile.v = General Purpose Register, branch_comparator.v = Branch Comparator, load_unit.v/store_unit.v = the load/store side of the Extension Block, extend.v = Imm Value Block).

Key datapath signals:

Signal Width Description
pc 32 Current program counter (output, testbench-visible)
instr 32 Fetched instruction (output, testbench-visible)
result 32 Register writeback value (output, testbench-visible)
alu_ctl 4 ALU operation selector, generated by control unit
rd1_sel / rd2_sel 1 Selects ALU operand A source (PC vs rs1) and B source (imm vs rs2)
dm_sel / dm_sel1 1 Selects writeback data: ALU result / load data / PC+4 (for JAL/JALR)
is_jalr 1 Clears bit 0 of the jump target for JALR per the RISC-V spec
br_taken 1 Branch-condition result from branch_comparator

Repository Structure

.
├── codes/
│   ├── constraint_file/
│   │   └── constr.xdc                     # Vivado timing & I/O constraints
│   ├── sim/
│   │   └── tb.v                           # Self-checking testbench (104 instructions)
│   └── source/
│       ├── ALU.v                          # 32-bit ALU (add/sub/logic/shift/compare)
│       ├── adder.v                        # Generic 32-bit adder (used for PC+4)
│       ├── branch_comparator.v            # Evaluates BEQ/BNE/BLT/BGE/BLTU/BGEU
│       ├── control_unit.v                 # Main single-cycle decoder / control FSM
│       ├── data_memory.v                  # 1024 x 32-bit byte-writable data RAM
│       ├── extend.v                       # Immediate sign-extension unit (I/S/B/U/J)
│       ├── inst_Mem.v                     # 128 x 32-bit instruction ROM (test program)
│       ├── load_unit.v                    # LB/LH/LW/LBU/LHU byte/halfword extraction
│       ├── mux.v                          # Generic 2:1 32-bit multiplexer
│       ├── program_counter.v              # PC register (active-low synchronous reset)
│       ├── registerfile.v                 # 32 x 32-bit register file (x0 hardwired to 0)
│       ├── risc_top.v                     # Top-level module wiring the full datapath
│       └── store_unit.v                   # SB/SH/SW data formatting & byte-enable logic
│
├── outputs/
│   ├── architecture_diagram.png           # Fig. 1 — single-cycle datapath block diagram
│   ├── TCL Console Output.txt             # Full XSim simulation log (104 instructions, 350/350 checks)
│   └── waveform/
│       ├── Waveforms.png                  # XSim waveform capture
│       └── top_tb_behav.wcfg              # Vivado waveform configuration
│
└── reports/
    ├── synth/                             # Post-synthesis reports
    │   ├── risc_top_utilization_synth.rpt
    ├   ├── Sythesis_output.png
    │   └── Post_Synthesis.png
    └── imp/                               # Post-implementation reports
        ├── risc_top_utilization_placed.rpt
        ├── risc_top_control_sets_placed.rpt
        ├── risc_top_timing_summary_routed.rpt
        ├── risc_top_power_routed.rpt
        ├── risc_top_bus_skew_routed.rpt
        ├── risc_top_route_status.rpt
        ├── risc_top_io_placed.rpt
        ├── risc_top_drc_opted.rpt
        ├── risc_top_drc_routed.rpt
        ├── risc_top_methodology_drc_routed.rpt
        ├── risc_top_clock_utilization_routed.rpt
        ├── Post_Implementation.png
        ├── Setup_time.png
        ├── Hold_Time.png
        ├── Pulse_Width.png
        └── power.png

Module Descriptions

Module Responsibility
risc_top.v Top-level integration — instantiates and wires every submodule into the single-cycle datapath.
program_counter.v 32-bit PC register; synchronously clears to 0 on active-low reset.
inst_Mem.v 128-word instruction ROM, asynchronously read, pre-loaded with the 108-instruction test program.
control_unit.v Combinational decoder mapping opcode/fun3/fun7 → ALU control, mux selects, and write-enable signals for all 11 RV32I opcode classes (R, I-ALU, Load, Store, Branch, JAL, JALR, LUI, AUIPC).
registerfile.v 32 × 32-bit register file with two asynchronous read ports and one synchronous write port; x0 is hardwired to zero on read and write.
extend.v Produces the correctly sign/zero-extended immediate for I/S/B/U/J instruction formats.
ALU.v Performs add, subtract, XOR/OR/AND, shift-left/right (logical & arithmetic), and both signed/unsigned "set-less-than" comparisons.
branch_comparator.v Independently evaluates all six RV32I branch conditions (BEQ/BNE/BLT/BGE/BLTU/BGEU) directly from fun3.
data_memory.v 1024 × 32-bit byte-writable RAM with asynchronous read and per-byte write-enable (be[3:0]).
store_unit.v Formats store data and computes byte-enables for SB/SH/SW based on address alignment.
load_unit.v Extracts and sign/zero-extends the addressed byte/halfword/word for LB/LH/LW/LBU/LHU.
mux.v, adder.v Generic reusable building blocks used throughout the datapath.
tb.v Self-checking testbench; drives 104 instructions and independently checks PC, instruction encoding, and register/memory writeback for every one. Compatible with both Icarus Verilog and Vivado XSim.

Instruction Set Coverage

All 37 RV32I base instructions are implemented and verified:

Category Instructions
R-type (ALU) add, sub, sll, slt, sltu, xor, srl, sra, or, and
I-type (ALU-immediate) addi, slti, sltiu, xori, ori, andi, slli, srli, srai
Loads lb, lh, lw, lbu, lhu
Stores sb, sh, sw
Branches beq, bne, blt, bge, bltu, bgeu
Jumps jal, jalr
Upper-immediate lui, auipc

The testbench exercises loops, forward/backward branches (taken & not-taken), function calls via jal/jalr, and a full sweep of the register file and data memory at the end of the run.


Simulation & Verification

tb.v is a self-checking, PC-anchored testbench — each instruction is verified three independent ways:

  1. PC check — the PC at fetch time must match the expected address (catches control-flow bugs).
  2. Encoding check — the fetched 32-bit instruction word must match the expected encoding (catches ROM/addressing bugs).
  3. Destination-register check — the value written back to rd must match the expected architectural result (catches datapath bugs).

Stores are verified with a final data-memory sweep, and the entire register file is swept at the end of the run. The testbench also includes:

  • A watchdog timer (200 µs) to catch a hung or runaway PC.
  • A PC X-detector to flag any unknown/undefined state.
  • Compile-time switches: `DUMP_VCD (enable VCD waveform dump), `QUIET (suppress per-instruction trace), `STOP_ON_FAIL (halt simulation on first failure).

Sample waveform capture from Vivado XSim, showing clk, rst, inst_out, alu_out, pc_out, and the pass/fail scoreboard counters advancing correctly:

Vivado XSim waveform clk/rst toggling, pc_out/inst_out/alu_out advancing each cycle in lock-step, pass_count incrementing every check, fail_count staying at 0 throughout.

Result

Actual Vivado XSim TCL console output (run all), simulating all 104 instructions end-to-end:

============================================================
  RISC-V RV32I Single-Cycle Core - Full ISA Regression
  37 base instructions, self-checking, PC-anchored
============================================================
      reset held 3 cycles: PC=0, regfile cleared  ok

[  1] PC=0x000 IR=0xabcde0b7  lui   x1,0xABCDE        x1 <= 0xabcde000  ok
[  2] PC=0x004 IR=0x00001117  auipc x2,0x00001        x2 <= 0x00001004  ok
   ...
[104] PC=0x1ac IR=0x00200313  addi  x6,x0,2           x6 <= 0x00000002  ok

---- final data memory ----
      dmem[64] = 0x12345678  ok
      dmem[65] = 0x89abcdef  ok
      dmem[66] = 0x00ef0078  ok
      dmem[67] = 0xcdef5678  ok

---- final register file ----
      x0  = 0x00000000  ok
       ...
      x31 = 0x00000184  ok

============================================================
  Instructions executed : 104
  Checks passed         : 350
  Checks failed         : 0
------------------------------------------------------------
  RESULT: PASS - all 37 RV32I instructions verified
============================================================

Every one of the 104 executed instructions, all 4 final data-memory words, and all 32 final register values were checked — 350/350 checks passed, 0 failed. This includes independent PC, instruction-encoding, and register/memory-writeback checks for every instruction, plus loop iterations (bne x30,x0,loop taken 3×), all 6 branch conditions (taken and not-taken), and both jal/jalr control-flow paths (function call/return and unconditional jump). The full console log is available in outputs/TCL Console Output.txt.


Vivado Implementation Results

Target device: xc7a35tftg256-1 (Artix-7) | Tool: Vivado v.2025.2 | Clock: sys_clk, 20 ns period (50 MHz)

These results come from Vivado's synthesis and implementation flow — simulation-only; the design was not deployed onto physical hardware.

Timing Summary

Metric Value
Worst Negative Slack (Setup, WNS) 0.779 ns (met)
Total Negative Slack (Setup, TNS) 0 ns
Worst Hold Slack (WHS) 0.275 ns (met)
Total Hold Slack (THS) 0 ns
Worst Pulse Width Slack (WPWS) 8.750 ns (met)
Setup endpoints 7136 (0 failing)
Hold endpoints 7136 (0 failing)
Pulse-width endpoints 1537 (0 failing)

All user-specified timing constraints are met at a 20 ns (50 MHz) clock period, with ~0.78 ns of positive setup slack.

The critical path runs from p1/pc_reg[7] through the instruction decode logic, register file read, ALU, and the distributed-RAM-based data memory, into the register file write port — 18 logic levels, dominated by routing delay (≈77%) as expected for a wide single-cycle datapath on a small Artix-7 part.

Setup Setup timing

Hold Hold timing

Pulse Width Pulse width timing

Vivado's implemented timing report confirms 0 failing endpoints across all three checks (7136 setup, 7136 hold, 1537 pulse-width endpoints).

Resource Utilization

Post-Synthesis:

Resource Used Available Utilization %
LUT 1988 20800 9.56%
LUTRAM 512 9600 5.33%
FF 1024 41600 2.46%
IO 98 170 57.65%
BUFG 1 32 3.13%

Post-Implementation (Placed & Routed):

Resource Used Available Utilization %
LUT 1960 20800 9.42%
LUTRAM 512 9600 5.33%
FF 1024 41600 2.46%
IO 98 170 57.65%
BUFG 1 32 3.13%

The design uses 0 Block RAMs and 0 DSPs — both inst_Mem and data_memory are implemented entirely as distributed LUT RAM (RAMS64E), which Vivado's methodology checker flags (SYNTH-5, informational) as a timing-driven mapping choice rather than an error.

Post-Synthesis Post-synthesis utilization

Post-Implementation Post-implementation utilization

Power Summary

Metric Value
Total On-Chip Power 0.161 W
Dynamic Power 0.090 W
Static (Device) Power 0.070 W
Junction Temperature 25.8 °C
Thermal Margin 59.2 °C (12.1 W)
Confidence Level Medium (activity partially estimated)

Power summary


How to Run

Simulation (Icarus Verilog)

# Compile all source files + testbench
iverilog -g2005 -o sim.vvp codes/source/ALU.v codes/source/adder.v \
    codes/source/branch_comparator.v codes/source/control_unit.v \
    codes/source/data_memory.v codes/source/extend.v codes/source/inst_Mem.v \
    codes/source/load_unit.v codes/source/mux.v codes/source/program_counter.v \
    codes/source/registerfile.v codes/source/risc_top.v codes/source/store_unit.v \
    codes/sim/tb.v

# Run the simulation
vvp sim.vvp

Optional compile-time defines:

iverilog -g2005 -DDUMP_VCD -o sim.vvp codes/source/*.v codes/sim/tb.v      # dump riscv_tb.vcd for GTKWave
iverilog -g2005 -DQUIET     -o sim.vvp codes/source/*.v codes/sim/tb.v     # suppress per-instruction trace, summary only
iverilog -g2005 -DSTOP_ON_FAIL -o sim.vvp codes/source/*.v codes/sim/tb.v  # halt on first mismatch

Simulation / Implementation (Vivado)

# From the Vivado Tcl console, or via a project you create in the GUI:
xvlog codes/source/*.v
xelab top_tb -s tb_sim
xsim tb_sim -runall

To reproduce the implementation results above:

  1. Create a new RTL project in Vivado, add all files under codes/source/ and codes/sim/tb.v as the simulation-only testbench.
  2. Add codes/constraint_file/constr.xdc as the constraints file.
  3. Set the target part to xc7a35tftg256-1.
  4. Run Synthesis → Implementation in Vivado to reproduce the timing, utilization, and power reports in reports/.

Constraints

codes/constraint_file/constr.xdc defines:

  • Primary clock: sys_clk, 20 ns period, on port clk.
  • Input delay on reset: 5 ns max / 1 ns min relative to sys_clk.
  • False paths from reset and to the debug/observability outputs result[*], instr[*], pc[*] (these are exposed purely for testbench/waveform visibility and are not part of any real timing-critical interface).
  • Basic configuration properties (CFGBVS, CONFIG_VOLTAGE, bitstream compression, SPI bus width/config rate) for Artix-7 SPI configuration.

License

This project is licensed under the MIT License — see the LICENSE file for details.


Authors

  • S. Vignesh
  • P. Vineeth Goud

About

32-bit single-cycle RV32I RISC-V processor in Verilog, verified via simulation and Vivado synthesis/implementation — all 37 base instructions tested.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages