diff --git a/CLAUDE.md b/CLAUDE.md index b940f76..e98e079 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,9 +26,12 @@ See ROADMAP.md for the full phased plan. 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+). + (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 @@ -47,16 +50,19 @@ 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 +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 12). Stages are separated in the code - even without pipeline registers between them. +- 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. @@ -73,10 +79,12 @@ RV32IM base. Extensions added incrementally: ## Memory Map ``` -0x0000_0000 – 0x0FFF_FFFF reserved (future SPI flash boot region, Phase 13) +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 13 +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` @@ -89,6 +97,46 @@ UART register layout (split, not 16550-style): 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. diff --git a/README.md b/README.md index 9757264..f86c2da 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ # FPGA-Core -Custom RV32IM RISC-V CPU core built from scratch in SystemVerilog. +Custom RISC-V CPU core built from scratch in SystemVerilog. Starts as RV32IM, +grows to RV32IMA + Sv32 + M/S/U privilege en route to booting Linux. ## Target - Board: Digilent Arty A7 100T (xc7a100tcsg324-1) - Toolchain: Vivado 2025.2 or later - Language: SystemVerilog -- ISA: RV32IM + Zicsr + Zifencei + M-mode privileged (extending toward Linux) +- ISA: end target **RV32IMA + Zicsr + Zifencei + M/S/U privilege + Sv32** + (Linux-capable). Built incrementally — see `ROADMAP.md`. - Clock: 50 MHz (100 MHz xtal ÷ 2 via MMCM) ## Architecture @@ -17,16 +19,16 @@ Custom RV32IM RISC-V CPU core built from scratch in SystemVerilog. unit (DSP-based multiply, iterative divide) sit side by side; the datapath stalls on M-unit and BRAM accesses. Inter-stage signals are typed structs in `rv32_pkg.sv` — the same structs become pipeline registers when the core is -pipelined later (Phase 12). +pipelined later (Phase 13, optional). ## Memory Map | Address Range | Region | |---------------------------|----------------------------------------------| -| `0x0000_0000–0x0FFF_FFFF` | reserved (SPI flash boot region, Phase 13) | +| `0x0000_0000–0x0FFF_FFFF` | reserved boot aperture (Phase 14: 16 MB SPI flash at low end) | | `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) → DRAM in Phase 13 | +| `0x8000_0000–0x8000_FFFF` | data BRAM (64 KB) → DRAM in Phase 14 | Reset PC = `0x2000_0000`. @@ -42,8 +44,9 @@ UART registers (split layout): Requires: - Vivado 2025.2 or later -- RISC-V GCC toolchain — multilib `riscv64-unknown-elf-gcc` supporting - `-march=rv32im_zicsr_zifencei -mabi=ilp32` +- RISC-V GCC toolchain — multilib `riscv64-unknown-elf-gcc`. March string + evolves with the implementation: `rv32im` (Phase 8) → + `rv32im_zicsr_zifencei` (Phase 9) → `rv32ima_zicsr_zifencei` (Phase 12). - `riscv-tests` (for compliance verification from Phase 8.5) - Serial terminal (minicom/picocom/PuTTY) at 115200 8N1 diff --git a/ROADMAP.md b/ROADMAP.md index 2c1565f..f7fabae 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,13 +5,15 @@ --- -## Memory Map (target — fixed in Phase 0, evolves through Phase 13) +## Memory Map (target — fixed in Phase 0, evolves through Phase 14) ``` -0x0000_0000 – 0x0FFF_FFFF reserved (SPI flash boot region, Phase 13) +0x0000_0000 – 0x0FFF_FFFF reserved boot aperture (256 MB decode region; + actual SPI flash is 16 MB on 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) → DRAM 0x8000_0000–0x8FFF_FFFF in Phase 13 +0x8000_0000 – 0x8000_FFFF data BRAM (64 KB) → DRAM 0x8000_0000–0x8FFF_FFFF in Phase 14 ``` Reset PC = `0x2000_0000`. Locking this in now keeps the linker script and crt0 @@ -237,9 +239,22 @@ Future role: Final. These instructions don't change. ### 6.1 — Word Load/Store (lw, sw) What: Add a data BRAM mapped at `0x8000_0000` (64 KB) and a load/store unit. -For now, only 32-bit aligned access (lw and sw). Define a simple memory bus -interface with address, write data, read data, write enable, and valid/ready -signals. +For now, only 32-bit aligned access (lw and sw). + +Define the full memory bus contract here, not just the subset Phase 6.1 uses. +Defining it once means Phase 6.2 (sub-word), Phase 7 (MMIO), Phase 9 (access +faults), Phase 12 (atomics), and Phase 14 (DRAM) all plug into the same +interface without rework. Required signals (see `CLAUDE.md` for the full +spec): + +- `req_valid` / `req_ready` — request handshake +- `rsp_valid` / `rsp_ready` — response handshake (independent) +- `req_addr`, `req_we`, `req_wdata`, `req_size`, `req_wstrb` +- `rsp_rdata`, `rsp_err` + +For Phase 6.1 specifically: `req_size` is always `2'b10` (word), `req_wstrb` +is `4'b1111` on stores, `rsp_err` is unused (BRAM never faults). The signals +exist in the bundle from day one even when tied off. Note: BRAM is 1-cycle read latency, so a load takes one extra cycle beyond the EXECUTE state. The control FSM extends to FETCH → EXECUTE → MEM_WAIT → @@ -269,9 +284,21 @@ to 32 bits, lbu zero-extends). Why: C uses char (byte) and short (halfword) types constantly. String operations are byte-by-byte. You can't run real C code without these. -Testbench focus: Sign extension (loading 0xFF as signed byte should give 0xFFFFFFFF, -as unsigned should give 0x000000FF). Unaligned access behavior (RISC-V base spec says -misaligned access can trap — decide if you want to support it or trap). +Implementation: the load/store unit drives `req_size` (00=byte, 01=halfword, +10=word) and `req_wstrb` (which byte lane within the 32-bit word is being +written). On load, the unit muxes the right byte/halfword out of `rsp_rdata` +and applies sign/zero extension based on the opcode. + +Decision for this project: **trap on misaligned access** rather than support +it in hardware. Hardware support for misaligned word access on Artix-7 is +expensive (two BRAM cycles + merge logic) and the kernel can emulate via +trap. The bus signals misalignment via `rsp_err`, and Phase 9's trap logic +turns that into a load/store address-misaligned exception. + +Testbench focus: Sign extension (loading 0xFF as signed byte should give +0xFFFFFFFF, as unsigned should give 0x000000FF). Byte lane selection (storing +0xAB at address `0x8000_0001` must update only that byte). Misaligned access +returns `rsp_err`. Future role: Final. These instructions don't change. @@ -373,16 +400,24 @@ crt0 grows when you add CSRs (setting up trap vectors in startup). What: Write a trivial main() that prints a string to the UART by writing bytes to `0x1000_0000`. Compile with: ``` -riscv64-unknown-elf-gcc -march=rv32im_zicsr_zifencei -mabi=ilp32 \ +riscv64-unknown-elf-gcc -march=rv32im -mabi=ilp32 \ -nostdlib -T linker.ld -o firmware.elf crt0.S main.c ``` Convert: `objcopy -O binary firmware.elf firmware.bin`. Convert binary to `.mem` format. Load into BRAM. Run. -Why the long march string: GCC 11+ split `Zicsr` and `Zifencei` out of the -RV32I base. `-march=rv32im` alone fails or warns. `Zifencei` is harmless -(`fence.i` is a NOP until you have caches); `Zicsr` will only be emitted by -code that uses CSR ops, so it's safe to advertise even before Phase 9. +March string discipline: advertise only what the hardware decodes. `rv32im` +is correct for Phase 8 because CSRs and `fence.i` aren't decoded yet. When +the program (or crt0) uses CSR/fence.i ops before that, the bus will return +`rsp_err` and Phase 8.3's illegal-instruction halt will catch it visibly — +much better than NOPing through a bug. Switch to `rv32im_zicsr_zifencei` in +Phase 9 once CSRs land, and `rv32ima_zicsr_zifencei` in Phase 12 once +atomics land. + +Note on GCC 11+: newer toolchains require `_zicsr` / `_zifencei` in the +march string only when source code actually uses CSR or `fence.i` +instructions (since those moved out of base RV32I in the 2019 spec). Pure +arithmetic + UART-poke C does not need them. Why: This proves your CPU is compatible with a real compiler. Any bugs in your instruction implementation will surface here — GCC will use instructions in @@ -392,12 +427,23 @@ Expect to iterate: GCC will probably emit an instruction you haven't implemented That's fine — check the illegal instruction, add it, resynthesize. This is normal. ### 8.3 — Fill Remaining RV32I Gaps -What: Implement any RV32I instructions you deferred. Common ones: all shift variants -(sll, srl, sra, slli, srli, srai), set-less-than variants (slti, sltiu), fence (NOP -for now — you have no cache), ecall/ebreak (NOP for now — you have no trap handling). +What: Implement any RV32I instructions you deferred. Common ones: all shift +variants (sll, srl, sra, slli, srli, srai), set-less-than variants (slti, +sltiu), `fence` (decode as NOP — there is no cache yet, so memory ordering +is trivially satisfied). -Why: GCC's output will exercise the full ISA. You need complete RV32I coverage for -any non-trivial C code to work. +`ecall` / `ebreak` / unrecognized opcodes / `fence.i` / any CSR op all go +to a single illegal-instruction handler that **halts the core and asserts a +testbench-visible `illegal_instr` signal** with the offending PC and +instruction word latched. Do NOT decode them as NOPs — GCC emits `ebreak` +in `__builtin_trap`, abort paths, and some divide-by-zero configurations, +and silently NOPing past those destroys debuggability. The halt becomes a +real exception in Phase 9 (`mcause = 2` illegal, `mcause = 3` ebreak, +`mcause = 11` ecall-from-M). + +Why: GCC's output will exercise the full ISA. You need complete RV32I +coverage for any non-trivial C code to work, and you need loud failures — +not silent ones — for the instructions you haven't implemented yet. Test: Compile progressively more complex C programs. String manipulation, struct usage, switch statements (these generate jump tables — exercises jalr with computed @@ -473,60 +519,154 @@ the PLIC manages them. This is required for Linux. --- -## Phase 12 — Pipeline (Optional but educational) +## Phase 12 — A Extension (Atomics) -What: Insert pipeline registers between your stages (fetch|decode|execute|memory| -writeback). Handle data hazards (forwarding/stalling) and control hazards (branch -prediction or pipeline flush). +What: Implement the RV32A atomic instructions: `lr.w` / `sc.w` (load-reserved / +store-conditional) and the AMO ops (`amoswap.w`, `amoadd.w`, `amoand.w`, +`amoor.w`, `amoxor.w`, `amomin.w`, `amomax.w`, `amominu.w`, `amomaxu.w`). +Switch the GCC march string to `rv32ima_zicsr_zifencei`. Run `rv32ua` from +`riscv-tests`. -Why: Your single-cycle core's clock speed is limited by the longest combinational -path (probably through the ALU). Pipelining lets each stage run in one short cycle. -This is the classic computer architecture exercise and deeply educational. +Why now: Mainline Linux's RISC-V kernel is built against `rv32ima` / +`rv64ima` — atomics are not optional for an unmodified kernel build. The +kernel uses LR/SC and AMO for spinlocks, refcounts, and futexes; without +them you maintain a non-standard fork. Adding A before the Linux work means +you discover atomics-related bugs in a small, focused phase rather than +wedged inside a kernel boot. -Why optional: A single-cycle core can run at maybe 50-80 MHz on the Artix-7. That's -plenty for booting Linux. Pipeline if you want to learn, not because you must. +Single-hart implementation: the reservation set is just one register +(reserved address + valid bit). LR sets it; any store to that address (or +context switch) clears it; SC checks it and either commits or returns 1. +AMOs are "read, op, write" sequences that the bus must perform atomically — +on this single-master core that's free; the load/store unit just holds the +bus across the read-modify-write. This becomes meaningful only when DRAM +or DMA enters the picture. + +Future role: Final for single-hart. If you ever go multi-hart, the +reservation set and bus become more involved. --- -## Phase 13 — SPI Flash Boot + DRAM +## Phase 13 — Pipeline (Optional but educational) -What: Add an SPI flash controller to boot from the on-board flash (instead of BRAM -initialization). Integrate AMD/Xilinx MIG IP for the DDR3 on the Arty A7. Update -your memory map: flash at boot, copy to DRAM, jump to DRAM. +What: Insert pipeline registers between your stages (fetch|decode|execute| +memory|writeback). Handle data hazards (forwarding/stalling) and control +hazards (branch prediction or pipeline flush). -Why: BRAM is tiny (a few hundred KB on Artix-7 100T). Linux needs megabytes. DRAM -gives you 256MB. Flash gives you persistent storage for the bootloader. This is -how real embedded systems boot. +Why: Your single-cycle core's clock speed is limited by the longest +combinational path (probably through the ALU). Pipelining lets each stage +run in one short cycle. This is the classic computer architecture exercise +and deeply educational. + +Why optional: A non-pipelined core can run at 50 MHz on the Artix-7. That's +plenty for booting Linux. Pipeline if you want to learn, not because you +must — and if you do, do it before DRAM/firmware work piles on. Re-running +`riscv-tests` (Phase 8.5 + 12) after pipelining catches regressions. --- -## Phase 14 — S-Mode, U-Mode, Sv32 Virtual Memory +## Phase 14 — SPI Flash Boot + DRAM -What: Add supervisor and user privilege modes. Implement Sv32 page table walking -(two-level page tables, 4KB pages). Add the satp CSR, page fault exceptions, and -sfence.vma. +What: Add an SPI flash controller to boot from the on-board flash (instead +of BRAM initialization). The Arty A7-100T has a 16 MB Quad-SPI flash; map +it at `0x0000_0000–0x00FF_FFFF` inside the reserved boot aperture. +Integrate AMD/Xilinx MIG IP for the DDR3L on the Arty (256 MB). Map DRAM at +`0x8000_0000–0x8FFF_FFFF`, replacing the data BRAM in that range. -Why: Linux runs the kernel in S-mode, user programs in U-mode. Virtual memory gives -each process its own address space and protects the kernel from user code. This is the -last major architectural piece before Linux. +Boot flow: reset PC moves to `0x0000_0000` (flash). A small first-stage +copies the kernel/firmware image from flash to DRAM, sets up the trap +vector, and jumps to DRAM. + +Why: BRAM is tiny (~600 KB total on Artix-7 100T). Linux needs megabytes. +DRAM gives you 256 MB; flash gives you persistent storage for the +bootloader. This is how real embedded systems boot. + +Bus implications: DRAM via MIG has multi-cycle, variable-latency responses. +This is the first time `req_valid`/`req_ready`/`rsp_valid`/`rsp_ready` +actually matter — the handshake you wired in Phase 6.1 finally earns its +keep. --- -## Phase 15 — Linux +## Phase 15 — S-Mode, U-Mode, Sv32 Virtual Memory -What: Port a minimal Linux kernel (or use an existing RISC-V port). Write a device -tree for your SoC. Build an initramfs with BusyBox. Boot to a shell prompt. +What: Add supervisor and user privilege modes. Implement Sv32 page table +walking (two-level page tables, 4 KB pages). Add the `satp` CSR, page-fault +exceptions (instruction/load/store page-fault), `sfence.vma`, and S-mode +CSRs (`sstatus`, `stvec`, `sepc`, `scause`, `stval`, `sie`, `sip`, +`sscratch`). -Why: This is the summit. A Linux shell running on a CPU you built from scratch. +Why: Linux runs the kernel in S-mode and user programs in U-mode. Virtual +memory gives each process its own address space and protects the kernel +from user code. This is the last big architectural piece before Linux. + +Test: a hand-written M-mode "kernel" that maps a U-mode page, traps on +syscall, returns a value, then returns to U-mode. Verify all three privilege +modes round-trip via `mret` / `sret`. + +--- + +## Phase 16 — Linux Boot Contract (SBI / Device Tree / ABI) + +What: Lock down everything Linux expects at the moment of `kernel_entry`. +This is a real subproject, not a footnote inside "port Linux". + +Required deliverables: +- **Boot register state at kernel entry**: `a0` = hart ID (0 for single-hart), + `a1` = physical address of the device tree blob, `satp = 0`, all other + state per the [RISC-V Linux boot protocol](https://docs.kernel.org/arch/riscv/boot.html). +- **Image alignment**: rv32 kernel image base must be 4 MiB-aligned in + physical memory (rv64 is 2 MiB). Reflect this in the linker layout for the + loaded kernel and the bootloader's copy destination. +- **Device tree**: hand-write a `.dts` describing CPU (with `riscv,isa = + "rv32ima_zicsr_zifencei"`, `mmu-type = "riscv,sv32"`), memory (DRAM base + + size), CLINT (timer + soft IPI), PLIC (interrupt controller bindings), + and the UART node. Compile with `dtc` to a `.dtb` and ship it in flash + alongside the kernel. +- **UART driver/binding decision**: the split TX/RX/status UART from + Phase 7 is *not* `8250/16550`-compatible. Pick one: + - (a) Add a 16550-subset wrapper (THR/RBR shared at offset 0, LSR at + offset 5, IER at offset 1, FCR/LCR mostly stubs). Then the in-tree + `8250_dw` or `of_serial` driver works with a stock binding. + - (b) Write a small custom Linux serial driver and define a + `compatible = "fpgacore,uart"` binding. More work, more learning. +- **Boot firmware**: choose between + - (a) **Direct M-mode Linux**: use the kernel's `CONFIG_RISCV_M_MODE` + path. Less portable, no SBI required, fewer moving pieces. Reasonable + for a learning bring-up. + - (b) **OpenSBI + S-mode Linux**: build OpenSBI as the M-mode firmware, + Linux runs in S-mode. Standard production path, more Vivado/Linux build + surface area. + +Why: Skipping this phase means hitting all of these problems simultaneously +during Phase 17 bring-up, where the failure mode is "kernel hangs silently +in early boot" with no console. Doing it explicitly turns Phase 17 into a +debugging exercise on a known-good ABI surface. + +--- + +## Phase 17 — Linux + +What: Build a minimal RISC-V Linux kernel against the device tree and +boot path defined in Phase 16. Build an initramfs with BusyBox. Load +kernel + DTB + initramfs to flash. Boot to a shell prompt over UART. + +Why: This is the summit. A Linux shell running on a CPU you built from +scratch. --- ## Quick Reference: What You Need Installed - Vivado 2025.2 or later (synthesis, simulation, ILA, VIO) -- RISC-V GCC toolchain (`riscv64-unknown-elf-gcc`, multilib build — needs to - support `rv32im_zicsr_zifencei` / `ilp32`) -- `riscv-tests` repo cloned and buildable (Phase 8.5 onward) +- RISC-V GCC toolchain (`riscv64-unknown-elf-gcc`, multilib build). March + string evolves: `rv32im` (Phase 8) → `rv32im_zicsr_zifencei` (Phase 9) → + `rv32ima_zicsr_zifencei` (Phase 12). +- `riscv-tests` repo cloned and buildable (Phase 8.5 onward; rv32ua added at + Phase 12) +- Device-tree compiler `dtc` (Phase 16+) +- OpenSBI source (Phase 16+, only if you choose the SBI boot path) - Terminal program (minicom, picocom, or PuTTY) for UART, 115200 8N1 - Text editor you like for SystemVerilog - The RISC-V ISA spec (Volume 1: Unprivileged, Volume 2: Privileged) — free PDFs +- The Linux RISC-V boot protocol doc (`Documentation/arch/riscv/boot.rst`)