Files
FPGA-Core/CLAUDE.md
T
imple e98b3694ab Refine Arty A7 bring-up and memory-map assumptions
Replace the first broad project sketch with more concrete hardware assumptions
for implementation on the Arty A7 100T.

Document the 50 MHz MMCM-derived clock, synchronous active-high reset after the
board reset synchronizer, and a split instruction/data BRAM map with reset PC at
0x2000_0000. Update the UART layout to separate TX, RX, and status registers,
and make the README/roadmap agree on the address ranges that firmware and the
future linker script will use.

Also split the M extension out of the combinational ALU into a dedicated
multi-cycle unit with start/busy/done handshaking, describe BRAM latency and
stall behavior in the single-cycle logical model, and add riscv-tests as the
planned compliance check once GCC-generated programs are running.
2026-04-28 11:29:01 +02:00

108 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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_<block>` (e.g., `rv32_alu`, `rv32_decode`, `rv32_regfile`).
- File naming: one module per file, filename matches module name.
- Testbenches: `tb/tb_<module>.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_<module>.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_00000x8FFF_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.