# CLAUDE.md ## Project Overview This is a custom RISC-V RV32IM CPU core written in SystemVerilog, targeting the Digilent Arty A7 100T (xc7a100tcsg324-1) with Vivado 2025.2 or later. The goal is incremental development from a single-cycle core to a Linux-capable SoC. See ROADMAP.md for the full phased plan. ## Conventions - Language: SystemVerilog (not Verilog). Use SV features: packages, structs, enums, always_comb, always_ff, logic (not reg/wire). - All inter-stage signals are defined as structs in `rtl/pkg/rv32_pkg.sv`. Always import this package. When adding new functionality, extend the existing structs rather than adding loose wires. - Module naming: `rv32_` (e.g., `rv32_alu`, `rv32_decode`, `rv32_regfile`). - File naming: one module per file, filename matches module name. - Testbenches: `tb/tb_.sv`. Use Vivado simulator. - Clock: single clock domain, active rising edge, signal named `clk`. Target frequency: 50 MHz (derived from the Arty's 100 MHz oscillator via MMCM/2). - Reset: synchronous active-high, signal named `rst`. The Arty's `CK_RST` button is active-low; the top-level wraps it through a 2-FF synchronizer and inverts to produce the internal `rst`. The rest of the design only sees synchronous active-high. - BRAM init files: `mem/*.mem` in hex format, one 32-bit word per line. - Firmware source: `fw/` directory. Built with `riscv64-unknown-elf-gcc` (multilib build required), `-march=rv32im_zicsr_zifencei -mabi=ilp32`. The full march string is required by GCC 11+ even before CSRs are implemented; `Zifencei` is a NOP until caches exist (Phase 13+). ## Directory Structure ``` rtl/ — synthesizable source pkg/ — packages (rv32_pkg.sv) core/ — CPU core modules (ALU, M unit, decoder, regfile, datapath) periph/ — peripherals (UART, timer, PLIC, etc.) top/ — top-level and SoC integration tb/ — testbenches (tb_.sv) mem/ — BRAM init files (.mem) fw/ — firmware source (C, assembly, linker scripts) constraints/ — Vivado XDC constraint files docs/ — block diagrams, notes ``` ## ISA Target RV32IM base. Extensions added incrementally: - Zicsr (CSR instructions) — Phase 9 - Zifencei (fence.i) — implemented as NOP until caches exist - M-mode privileged — Phase 9 - S-mode + U-mode + Sv32 — Phase 14 ## Key Design Decisions - Single-cycle first, pipeline later (Phase 12). Stages are separated in the code even without pipeline registers between them. - "Single-cycle" is a logical model, not a literal one cycle per instruction. BRAM has a 1-cycle read latency, so fetch is registered and loads take 2 cycles. The datapath stalls fetch while a multi-cycle operation is in flight. - Memory bus uses valid/ready handshake from day one, even though BRAM always responds in one cycle. This is for future DRAM compatibility. - M extension (multiply/divide) lives in a dedicated multi-cycle M unit alongside the combinational ALU, with a `start`/`busy`/`done` handshake. Multiply is 2-3 cycles via DSP48s; divide is iterative (~33 cycles). The datapath stalls when the M unit is busy. Building this from day one avoids retrofitting stall logic later. - Harvard architecture (separate instruction and data memory) initially. Unified memory when DRAM is added. ## Memory Map ``` 0x0000_0000 – 0x0FFF_FFFF reserved (future SPI flash boot region, Phase 13) 0x1000_0000 – 0x1000_0FFF MMIO (UART; later: timer, PLIC) 0x2000_0000 – 0x2000_FFFF instruction BRAM (64 KB) 0x8000_0000 – 0x8000_FFFF data BRAM (64 KB) → grows to DRAM 0x8000_0000–0x8FFF_FFFF in Phase 13 ``` Reset PC = `0x2000_0000`. Linker script anchors text/rodata at `0x2000_0000` and data/bss/stack at `0x8000_0000` from Phase 8 onward. UART register layout (split, not 16550-style): ``` 0x1000_0000 TX data (W: byte to send) 0x1000_0004 RX data (R: pops one byte from RX FIFO) 0x1000_0008 status (R: bit0 = tx_busy, bit1 = rx_valid) ``` ## When Helping With This Project - Always check rv32_pkg.sv first to understand current struct definitions and enums. - Reference the RISC-V ISA spec for instruction encoding and behavior. - Prefer simulation-testable solutions. Every module should be verifiable standalone before integration. - Don't add Verilog-style code (reg, wire, always @). Use SV equivalents. - When suggesting fixes, consider that the design is single-cycle — there are no pipeline hazards yet, but the M unit and BRAM both introduce stalls. - ISA correctness is verified against `riscv-tests` (rv32ui, rv32um) starting at Phase 8.5. Hand-written testbenches are for module-level checks; the official suite is the source of truth for instruction behavior. - Vivado quirks: use (* dont_touch = "true" *) for signals that Vivado optimizes away during debug. ILA/VIO probes need to be on nets that survive synthesis. - The Arty A7 100T has: 101,440 logic cells, 4,860 Kbit BRAM, 240 DSP slices, 256MB DDR3L SDRAM, USB-UART bridge, 4 LEDs, 4 switches, 4 buttons.