# 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 string evolves with the implemented ISA: - Phase 8 (pre-CSR bare-metal): `-march=rv32im -mabi=ilp32` - Phase 9+ (CSRs/Zifencei decoded): `-march=rv32im_zicsr_zifencei -mabi=ilp32` - Phase 12+ (atomics): `-march=rv32ima_zicsr_zifencei -mabi=ilp32` Don't advertise an extension before its ops are decoded — the compiler is free to emit them, and "trap as illegal" early is much better than NOPing. ## 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 End target: **RV32IMA + Zicsr + Zifencei + M/S/U privilege + Sv32** (Linux-capable). Extensions land incrementally: - RV32IM base — Phases 1-8 - Zicsr (CSR instructions) + M-mode trap handling — Phase 9 - Zifencei (`fence.i`) — decoded but NOP until caches exist (Phase 14+) - A (atomics: LR/SC + AMO) — Phase 12 (required for mainline Linux) - S-mode + U-mode + Sv32 — Phase 15 ## Key Design Decisions - Single-cycle first, pipeline later (Phase 13, optional). 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 boot aperture (256 MB decode region) actual SPI flash device is 16 MB (Arty A7), mapped at 0x0000_0000–0x00FF_FFFF in Phase 14 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 14 ``` 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) ``` This is fine for bare-metal firmware but is *not* directly Linux-compatible: the in-tree `8250/16550` driver expects a different register layout. Phase 16 (Linux Boot Contract) revisits this — either add a 16550-compatible wrapper or write a custom Linux serial driver + device-tree binding. ## Memory Bus Contract Single channel, valid/ready handshake, defined once in `rv32_pkg.sv` and reused by every master/slave (CPU, instruction BRAM, data BRAM, MMIO, DRAM later). Master drives a request, slave drives a response. ``` Request (master → slave): req_valid : logic — request is valid this cycle req_ready : logic — slave accepts (driven by slave) req_addr : logic [31:0] — byte address req_we : logic — 1 = write, 0 = read req_size : logic [1:0] — 00=byte, 01=halfword, 10=word req_wdata : logic [31:0] — write data (lane-aligned per wstrb) req_wstrb : logic [3:0] — byte enables (writes only) Response (slave → master): rsp_valid : logic — response is valid this cycle rsp_ready : logic — master accepts (driven by master) rsp_rdata : logic [31:0] — read data (reads only) rsp_err : logic — fault: unmapped, misaligned, access violation ``` Notes: - `req_valid`/`req_ready` and `rsp_valid`/`rsp_ready` are independent handshakes; a slave may take multiple cycles between accepting a request and producing a response (BRAM = 1 cycle, DRAM = many). - `req_wstrb` is mandatory from Phase 6.2 onward (sub-word stores). For Phase 6.1 (word-only), tie strobes to `4'b1111` on stores. - `rsp_err` lets the bus signal misalignment, unmapped MMIO, and (later) page faults uniformly. The CPU's trap logic (Phase 9) consumes it as load/store access-fault exceptions. - Atomics (Phase 12) extend this with an `req_amo` opcode field rather than a new bus. ## 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.