diff --git a/Tutorial/README.md b/Tutorial/README.md new file mode 100644 index 0000000..6a01e68 --- /dev/null +++ b/Tutorial/README.md @@ -0,0 +1,39 @@ +# FPGA-Core Tutorial + +This tutorial tree follows `ROADMAP.md`. Each phase and subphase has a learning note +with the same structure: + +- Context +- Goals +- New Concepts +- How To Think About It +- Learning Tasks +- Pitfalls +- Tooling And Testing +- References + +The notes assume strong Linux/systems experience and beginner-to-intermediate FPGA and +digital-design experience. They intentionally avoid giving implementation code. The goal +is to explain what to learn, what to verify, and what mistakes to avoid. + +## Phase Index + +- [Phase 0 - Architecture Contract](phase-00-architecture-contract/phase-00.md) +- [Phase 1 - ALU + M Unit](phase-01-alu-m-unit/phase-01.md) +- [Phase 2 - Register File](phase-02-register-file/phase-02.md) +- [Phase 3 - Decoder](phase-03-decoder/phase-03.md) +- [Phase 4 - First CPU](phase-04-first-cpu/phase-04.md) +- [Phase 5 - Branches And Jumps](phase-05-control-flow/phase-05.md) +- [Phase 6 - Load/Store](phase-06-load-store/phase-06.md) +- [Phase 7 - Memory-Mapped UART](phase-07-uart-mmio/phase-07.md) +- [Phase 8 - GCC Toolchain Integration](phase-08-gcc-toolchain/phase-08.md) +- [Phase 9 - CSRs + M-Mode Traps](phase-09-csrs-traps/phase-09.md) +- [Phase 10 - Timer](phase-10-timer/phase-10.md) +- [Phase 11 - Interrupt Controller](phase-11-interrupt-controller/phase-11.md) +- [Phase 12 - Atomics](phase-12-atomics/phase-12.md) +- [Phase 13 - Pipeline](phase-13-pipeline/phase-13.md) +- [Phase 14 - SPI Flash + DRAM](phase-14-flash-dram/phase-14.md) +- [Phase 15 - M/S/U + Sv32](phase-15-privilege-sv32/phase-15.md) +- [Phase 16 - Linux Boot Contract](phase-16-linux-boot-contract/phase-16.md) +- [Phase 17 - Linux](phase-17-linux/phase-17.md) + diff --git a/Tutorial/phase-00-architecture-contract/phase-00-01-systemverilog-package.md b/Tutorial/phase-00-architecture-contract/phase-00-01-systemverilog-package.md new file mode 100644 index 0000000..327d836 --- /dev/null +++ b/Tutorial/phase-00-architecture-contract/phase-00-01-systemverilog-package.md @@ -0,0 +1,49 @@ +# Phase 0.1 - SystemVerilog Package + +## Context + +The package is the shared type layer for the project. It prevents every module from +inventing its own encodings and signal names. + +## Goals + +- Define enums for ALU operations, branch types, memory sizes, and instruction classes. +- Define structs for stage outputs and memory request/response payloads. +- Make later extension possible without rewriting every module boundary. + +## New Concepts + +- Packed struct: a struct with a defined bit layout, suitable for wires and registers. +- Typedef: named type alias used to make RTL readable. +- Import: SystemVerilog mechanism for using package definitions in modules. +- Encoding: the bit pattern used for enum values in synthesized hardware. + +## How To Think About It + +The package is not a dumping ground. It should contain stable contracts and shared +definitions. If a signal is private to one module, keep it private. + +## Learning Tasks + +- List every planned stage boundary and define what information must cross it. +- Separate architectural information, such as register addresses, from local control. +- Decide naming conventions before writing dependent modules. + +## Pitfalls + +- Relying on implicit enum widths and later discovering mismatched synthesis behavior. +- Putting handshake `ready` signals inside one-way payload structs. +- Adding fields "just in case" without knowing who drives or consumes them. + +## Tooling And Testing + +- Compile the package alone and then with one tiny importing module. +- Inspect elaboration messages; type errors here are usually design-contract errors. +- Keep comments on fields short but precise about direction and ownership. + +## References + +- SystemVerilog packages and typedefs: https://verificationguide.com/systemverilog/systemverilog-package/ +- LowRISC SystemVerilog style: https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ + diff --git a/Tutorial/phase-00-architecture-contract/phase-00-02-block-diagram.md b/Tutorial/phase-00-architecture-contract/phase-00-02-block-diagram.md new file mode 100644 index 0000000..82b6b11 --- /dev/null +++ b/Tutorial/phase-00-architecture-contract/phase-00-02-block-diagram.md @@ -0,0 +1,49 @@ +# Phase 0.2 - Block Diagram + +## Context + +The block diagram is the visual source of truth for the datapath. It should show what +talks to what, what is registered, and what can stall. + +## Goals + +- Draw fetch, decode, register file, ALU, M unit, memory/LSU, and writeback. +- Label stage-boundary structs from the package. +- Mark clocked state, combinational paths, handshakes, and reset behavior. + +## New Concepts + +- Datapath: the route operands and results take through the CPU. +- Control path: signals that choose operations, mux inputs, stalls, and traps. +- Mux: hardware selector that chooses one of several inputs. +- Stall: deliberately holding state while waiting for a multi-cycle event. + +## How To Think About It + +Draw movement of information, not just boxes. A useful hardware diagram answers: where is +state stored, what changes each clock, what can wait, and what happens on an exception? + +## Learning Tasks + +- Use arrows for data flow and separate arrows for control when helpful. +- Mark every register or memory with a clock edge symbol. +- Identify the longest combinational path you expect in early phases. + +## Pitfalls + +- Drawing BRAM like a zero-latency array. +- Forgetting that multi-cycle units need explicit state and stall control. +- Hiding reset and trap paths because they are visually inconvenient. + +## Tooling And Testing + +- Keep the diagram in `docs/` and update it when interfaces change. +- Compare waveform signal names against the diagram after each integration phase. +- Use the diagram as a checklist when adding ILA probes. + +## References + +- nand2tetris CPU/data-path intuition: https://www.nand2tetris.org/ +- RISC-V reader for datapath examples: http://www.riscvbook.com/ +- Vivado ILA overview: https://docs.xilinx.com/ + diff --git a/Tutorial/phase-00-architecture-contract/phase-00.md b/Tutorial/phase-00-architecture-contract/phase-00.md new file mode 100644 index 0000000..5e086a8 --- /dev/null +++ b/Tutorial/phase-00-architecture-contract/phase-00.md @@ -0,0 +1,53 @@ +# Phase 0 - Architecture Contract + +## Context + +Before writing RTL, define the vocabulary of the CPU: stage boundaries, signal bundles, +enums, reset behavior, memory map, and debug expectations. This is similar to defining an +internal kernel ABI before implementing subsystems. + +## Goals + +- Establish `rv32_pkg.sv` as the shared contract for the design. +- Decide what signals cross between fetch, decode, execute, memory, and writeback. +- Create a block diagram that can guide implementation and later refactors. + +## New Concepts + +- RTL: register-transfer level description of hardware behavior. +- Package: SystemVerilog namespace for shared types, constants, enums, and structs. +- Struct: named bundle of related signals; useful for stage outputs and bus payloads. +- Enum: symbolic encoding for choices such as ALU operation or branch type. +- Combinational logic: output depends only on current inputs. +- Sequential logic: output/state changes on a clock edge. + +## How To Think About It + +You are designing interfaces between hardware blocks, not function signatures. Every +field becomes wires or registers, so changing it later can ripple through the whole +datapath. Bias toward clarity and explicitness, not clever compression. + +## Learning Tasks + +- Sketch the datapath by hand and label every stage boundary. +- Write down which signals are data, control, status, or exception metadata. +- Decide which fields are needed now and which are placeholders for later phases. + +## Pitfalls + +- Overfitting the package to Phase 1 and then reworking it repeatedly. +- Mixing unrelated control signals into loose wires instead of structured bundles. +- Treating the diagram as disposable; it should be updated as the design changes. + +## Tooling And Testing + +- Use the package in the smallest possible test module to confirm Vivado accepts it. +- Keep package dependencies acyclic; shared types should not import implementation modules. +- Run syntax checks early, before several modules depend on a broken type. + +## References + +- SystemVerilog IEEE overview: https://www.accellera.org/downloads/standards/systemverilog +- LowRISC style guide: https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md +- RISC-V specifications: https://riscv.org/technical/specifications/ + diff --git a/Tutorial/phase-01-alu-m-unit/phase-01-01-combinational-alu.md b/Tutorial/phase-01-alu-m-unit/phase-01-01-combinational-alu.md new file mode 100644 index 0000000..a0412c1 --- /dev/null +++ b/Tutorial/phase-01-alu-m-unit/phase-01-01-combinational-alu.md @@ -0,0 +1,49 @@ +# Phase 1.1 - Combinational ALU + +## Context + +The ALU is the easiest place to learn the difference between software expressions and +hardware behavior. Every operation is hardware that exists in parallel behind a selector. + +## Goals + +- Implement RV32I arithmetic, logic, comparison, and shift operations. +- Verify every operation independently. +- Learn how signedness works in SystemVerilog. + +## New Concepts + +- Signedness: whether bits are interpreted as two's-complement signed values. +- Shift amount: RISC-V uses only the low 5 bits for RV32 shifts. +- Overflow: addition overflow is usually ignored for RV32I arithmetic results. +- Combinational completeness: every output is assigned for every input path. + +## How To Think About It + +The same 32-bit vector can be signed or unsigned depending on the operation. Make the +interpretation explicit in your design notes and tests. + +## Learning Tasks + +- Make a table of each ALU op, its operands, and expected result semantics. +- Hand-check edge cases such as `0`, `-1`, `INT_MIN`, and `INT_MAX`. +- Compare arithmetic right shift with logical right shift. + +## Pitfalls + +- Accidentally creating latches by not assigning outputs in every case. +- Using host-language intuition for signed comparisons. +- Forgetting that `slt` and `sltu` are different instructions. + +## Tooling And Testing + +- Use a small self-checking testbench before opening a waveform. +- Add waveform inspection for failing tests only; do not debug by staring first. +- Include cases where signed and unsigned answers differ. + +## References + +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ +- SystemVerilog signed arithmetic notes: https://www.chipverify.com/systemverilog/systemverilog-data-types +- Verilator warnings guide: https://verilator.org/guide/latest/warnings.html + diff --git a/Tutorial/phase-01-alu-m-unit/phase-01-02-multi-cycle-m-unit.md b/Tutorial/phase-01-alu-m-unit/phase-01-02-multi-cycle-m-unit.md new file mode 100644 index 0000000..5e0f354 --- /dev/null +++ b/Tutorial/phase-01-alu-m-unit/phase-01-02-multi-cycle-m-unit.md @@ -0,0 +1,49 @@ +# Phase 1.2 - Multi-Cycle M Unit + +## Context + +Multiply and divide belong in a dedicated unit because their timing and control behavior +are different from simple ALU operations. + +## Goals + +- Implement multiply, high-half multiply, divide, and remainder behavior. +- Learn multi-cycle control using `start`, `busy`, and `done`. +- Verify all RISC-V-defined edge cases. + +## New Concepts + +- High-half multiply: result uses bits 63:32 of a 64-bit product. +- Iterative divider: divider that computes one bit or small group of bits per cycle. +- FSM: finite-state machine controlling multi-cycle behavior. +- Latency: number of cycles from request to result. + +## How To Think About It + +Treat the M unit like a tiny asynchronous service from the core's perspective. The core +asks for work, waits, and resumes only when the result is stable. + +## Learning Tasks + +- Write down the expected result for divide-by-zero and signed overflow. +- Draw state transitions for idle, multiply, divide, done, and back-to-back requests. +- Decide when inputs are latched and when outputs are valid. + +## Pitfalls + +- Allowing operands to change while an operation is in flight. +- Returning C-language division behavior instead of RISC-V behavior. +- Forgetting `mulhsu`, which mixes signed and unsigned operands. + +## Tooling And Testing + +- Use long waveform windows to inspect division progress. +- Include tests that assert `start` while `busy` and confirm the intended behavior. +- Synthesize to confirm multiply maps to DSPs and divide maps to logic/state. + +## References + +- RISC-V M extension spec: https://riscv.org/technical/specifications/ +- Project F multiplication: https://projectf.io/posts/multiplication-fpga-dsps/ +- Project F division: https://projectf.io/posts/division-in-verilog/ + diff --git a/Tutorial/phase-01-alu-m-unit/phase-01-03-vio-validation.md b/Tutorial/phase-01-alu-m-unit/phase-01-03-vio-validation.md new file mode 100644 index 0000000..1ef3ba9 --- /dev/null +++ b/Tutorial/phase-01-alu-m-unit/phase-01-03-vio-validation.md @@ -0,0 +1,50 @@ +# Phase 1.3 - ALU + M Unit On FPGA With VIO + +## Context + +After simulation, place the arithmetic blocks on real hardware and interact with them +through Vivado VIO. This teaches the difference between simulated correctness and +synthesized hardware behavior. + +## Goals + +- Learn how to instantiate debug IP around simple modules. +- Confirm arithmetic blocks synthesize and meet the 50 MHz target. +- Practice observing handshake behavior in hardware. + +## New Concepts + +- VIO: Vivado Virtual Input/Output IP, controlled over JTAG. +- JTAG: debug/control link used by Vivado Hardware Manager. +- Timing closure: proving signals settle before the next clock edge. +- Synthesis: compiling RTL into FPGA primitives. + +## How To Think About It + +This is not about exhaustive validation. It is about learning the FPGA tool loop: +synthesize, implement, program, poke signals, observe results, and read timing reports. + +## Learning Tasks + +- Identify which signals are useful to drive and which are useful to observe. +- Read the timing summary and locate the worst path. +- Compare VIO observations with simulator waveforms. + +## Pitfalls + +- Letting Vivado optimize away debug signals before VIO/ILA can observe them. +- Confusing VIO interaction latency with the design's actual clock-cycle behavior. +- Ignoring timing warnings because the manual test "seemed to work." + +## Tooling And Testing + +- Use VIO for low-bandwidth manual checks, not automated compliance. +- Keep a short lab notebook: bitstream, clock, operands, expected, observed. +- Learn where Vivado reports resource use for LUTs, FFs, DSPs, and BRAMs. + +## References + +- Vivado Design Suite documentation: https://docs.xilinx.com/ +- Vivado debug IP overview: https://docs.xilinx.com/ +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual + diff --git a/Tutorial/phase-01-alu-m-unit/phase-01.md b/Tutorial/phase-01-alu-m-unit/phase-01.md new file mode 100644 index 0000000..2322854 --- /dev/null +++ b/Tutorial/phase-01-alu-m-unit/phase-01.md @@ -0,0 +1,49 @@ +# Phase 1 - ALU + M Unit + +## Context + +This phase builds the arithmetic core of the CPU. The ALU handles simple single-cycle +operations; the M unit handles multiplication and division with an explicit handshake. + +## Goals + +- Build and verify a combinational RV32I ALU. +- Build and verify a multi-cycle RV32M unit. +- Learn simulation-first hardware development before system integration. + +## New Concepts + +- ALU: arithmetic logic unit; performs integer arithmetic, comparisons, shifts, and logic. +- DSP slice: FPGA hard block optimized for multiplication and arithmetic. +- Multi-cycle unit: block that takes several clocks to produce a result. +- Handshake: protocol using signals such as start, busy, done, valid, or ready. + +## How To Think About It + +The ALU is pure combinational decision logic. The M unit is a small protocol machine. Do +not force division into the ALU just because it is mathematically an arithmetic operation. + +## Learning Tasks + +- Understand signed versus unsigned interpretation of the same 32 bits. +- Work through RISC-V division edge cases by hand. +- Draw the M-unit state machine before writing RTL. + +## Pitfalls + +- Implementing a combinational divider and then fighting timing forever. +- Treating signed comparisons as unsigned or vice versa. +- Dropping `done` or accepting a new `start` at the wrong time. + +## Tooling And Testing + +- Start with small deterministic test vectors, then add randomized checks. +- Use waveform inspection to confirm handshake timing, not only final results. +- Synthesize early to see whether multiply infers DSP resources. + +## References + +- RISC-V unprivileged ISA, integer and M extension chapters: https://riscv.org/technical/specifications/ +- AMD/Xilinx DSP48 documentation: https://docs.xilinx.com/ +- Project F FPGA arithmetic articles: https://projectf.io/posts/ + diff --git a/Tutorial/phase-02-register-file/phase-02-01-register-file-module.md b/Tutorial/phase-02-register-file/phase-02-01-register-file-module.md new file mode 100644 index 0000000..2b5c51d --- /dev/null +++ b/Tutorial/phase-02-register-file/phase-02-01-register-file-module.md @@ -0,0 +1,49 @@ +# Phase 2.1 - Register File Module + +## Context + +This subphase turns the architectural register model into clocked hardware with two +combinational reads and one synchronous write. + +## Goals + +- Implement 32 registers of 32 bits. +- Enforce `x0` semantics. +- Verify read, write, and collision behavior. + +## New Concepts + +- Synchronous write: state changes only at the active clock edge. +- Combinational read: output changes when the address changes. +- Read-after-write: reading an address in the same cycle it is written. +- Bypass/forwarding: returning newly written data without waiting for storage update. + +## How To Think About It + +Choose behavior deliberately. A simple non-pipelined core can tolerate many choices, but +tests and later pipeline work become easier if the behavior is documented. + +## Learning Tasks + +- Write a truth table for `write_enable`, `rd`, `rs1`, and `rs2` interactions. +- Decide how reset affects registers, if at all. +- Confirm whether the register file should initialize to zero for simulation clarity. + +## Pitfalls + +- Resetting a large register file unnecessarily in hardware. +- Making simulation behavior differ from synthesis behavior. +- Forgetting that reads from `x0` must ignore any stored value. + +## Tooling And Testing + +- Use assertions for `x0` if your simulator supports them. +- Run repeated randomized write/read sequences. +- Inspect synthesis resource use; this may infer LUT RAM or flip-flops. + +## References + +- RISC-V ABI register names: https://riscv.org/technical/specifications/ +- SystemVerilog always_ff guidance: https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md +- Vivado synthesis guide: https://docs.xilinx.com/ + diff --git a/Tutorial/phase-02-register-file/phase-02-02-vio-validation.md b/Tutorial/phase-02-register-file/phase-02-02-vio-validation.md new file mode 100644 index 0000000..b9e25c1 --- /dev/null +++ b/Tutorial/phase-02-register-file/phase-02-02-vio-validation.md @@ -0,0 +1,48 @@ +# Phase 2.2 - Optional VIO Validation + +## Context + +The register file is simple enough that simulation is usually sufficient, but VIO can +teach practical debug techniques on real FPGA state. + +## Goals + +- Practice observing and driving register-file signals in hardware. +- Confirm reset/write/read expectations after synthesis. +- Learn when hardware debug is worth the overhead. + +## New Concepts + +- Probe: signal connected to debug IP for observation. +- Debug visibility: whether a net survives optimization and can be inspected. +- Hardware Manager: Vivado tool for programming and interacting with the FPGA. + +## How To Think About It + +Use VIO here only as a learning exercise. Do not make it a habit to replace systematic +simulation with manual poking. + +## Learning Tasks + +- Select a minimal set of probes: addresses, write data, write enable, read outputs. +- Compare one same-cycle read/write case with the simulator. +- Observe how a clocked write appears relative to your VIO actions. + +## Pitfalls + +- Over-instrumenting and making the debug design harder than the actual module. +- Forgetting that manual tests rarely cover corner cases. +- Treating VIO as evidence of complete correctness. + +## Tooling And Testing + +- Keep the VIO wrapper separate from the synthesizable register-file module. +- Remove or isolate debug IP before building later phases. +- Check resource utilization with and without debug IP. + +## References + +- Vivado debug documentation: https://docs.xilinx.com/ +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- LowRISC style guide: https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md + diff --git a/Tutorial/phase-02-register-file/phase-02.md b/Tutorial/phase-02-register-file/phase-02.md new file mode 100644 index 0000000..262f6e7 --- /dev/null +++ b/Tutorial/phase-02-register-file/phase-02.md @@ -0,0 +1,49 @@ +# Phase 2 - Register File + +## Context + +The register file is the CPU's small, fast architectural storage. RISC-V has 32 integer +registers, with `x0` permanently reading as zero. + +## Goals + +- Build a 32 x 32-bit register file with two reads and one write. +- Define same-cycle read/write behavior intentionally. +- Learn how small memories map onto FPGA resources. + +## New Concepts + +- Read port: ability to read one register address in a cycle. +- Write port: ability to update one register address on a clock edge. +- Architectural register: register visible to software. +- Hardwired zero: `x0` ignores writes and always returns zero. + +## How To Think About It + +The register file is part storage and part contract with the ISA. Its behavior must be +boring, predictable, and heavily tested because every instruction depends on it. + +## Learning Tasks + +- Decide whether same-cycle read-after-write returns old or new data. +- Make a register naming table from ABI names to `x` numbers. +- Understand why two read ports are enough for RV32I/RV32M. + +## Pitfalls + +- Accidentally allowing writes to `x0`. +- Assuming FPGA block RAM behavior without checking read-port needs. +- Leaving same-cycle behavior unspecified until integration exposes it. + +## Tooling And Testing + +- Test all boundary addresses: `x0`, `x1`, `x31`. +- Test simultaneous reads of two different registers and the same register. +- Prefer a waveform check for one read/write collision case. + +## References + +- RISC-V register convention: https://riscv.org/technical/specifications/ +- AMD/Xilinx memory resources: https://docs.xilinx.com/ +- Project F memory initialization and FPGA memories: https://projectf.io/posts/ + diff --git a/Tutorial/phase-03-decoder/phase-03-01-instruction-decoder.md b/Tutorial/phase-03-decoder/phase-03-01-instruction-decoder.md new file mode 100644 index 0000000..832e162 --- /dev/null +++ b/Tutorial/phase-03-decoder/phase-03-01-instruction-decoder.md @@ -0,0 +1,49 @@ +# Phase 3.1 - Instruction Decoder + +## Context + +This subphase creates the combinational decoder module that turns raw instruction bits +into `decode_out_t` fields. + +## Goals + +- Decode source/destination registers, immediates, ALU operation, memory operation, and branch type. +- Identify legal versus illegal instructions. +- Verify all instruction formats with known encodings. + +## New Concepts + +- Sign extension: copying a sign bit into higher bits to preserve signed value. +- Zero extension: filling upper bits with zero. +- Control signal: a signal that chooses what hardware action occurs. +- Decode table: mapping from instruction fields to operation semantics. + +## How To Think About It + +Decoder bugs often look like random CPU bugs later. Invest heavily here. If an immediate +bit is wrong, the ALU and branch logic can be perfect and the program will still fail. + +## Learning Tasks + +- Create a checklist for each instruction family and expected decode fields. +- For each immediate format, manually reconstruct the value from bit positions. +- Decide how `fence`, `fence.i`, CSR, `ecall`, and `ebreak` are represented before traps exist. + +## Pitfalls + +- Copying immediate extraction logic without understanding bit order. +- Missing `jalr`'s low-bit clearing rule later in execute/control flow. +- Letting default decode outputs accidentally describe a valid NOP. + +## Tooling And Testing + +- Generate encodings with the RISC-V assembler, not by hand alone. +- Use objdump to verify that your test words are the instructions you think they are. +- Add negative tests for illegal encodings. + +## References + +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ +- RISC-V opcode repository: https://github.com/riscv/riscv-opcodes +- GNU assembler manual: https://sourceware.org/binutils/docs/as/ + diff --git a/Tutorial/phase-03-decoder/phase-03.md b/Tutorial/phase-03-decoder/phase-03.md new file mode 100644 index 0000000..b16060b --- /dev/null +++ b/Tutorial/phase-03-decoder/phase-03.md @@ -0,0 +1,49 @@ +# Phase 3 - Decoder + +## Context + +The decoder translates a 32-bit instruction word into control signals. It is where the +binary ISA becomes meaningful hardware intent. + +## Goals + +- Decode RV32I/RV32M instruction fields and immediates. +- Produce structured control output for later datapath integration. +- Learn to verify against assembler-generated encodings. + +## New Concepts + +- Opcode: primary instruction-class field. +- funct3/funct7: secondary fields that refine instruction meaning. +- Immediate: constant encoded inside an instruction, often split across bits. +- Illegal instruction: encoding the core does not implement or that is invalid. + +## How To Think About It + +The decoder is a classifier. It should not perform ALU work; it should describe what +work the datapath must perform and which operands/control paths are needed. + +## Learning Tasks + +- Draw bit layouts for R, I, S, B, U, and J formats. +- Hand-decode several assembled instructions. +- Decide where illegal instruction detection lives and how it reports failures. + +## Pitfalls + +- Misplacing B-type and J-type immediate bits. +- Forgetting sign extension on immediates. +- Treating all unknown encodings as harmless NOPs. + +## Tooling And Testing + +- Use assembler/objdump as a reference for encodings. +- Build tests around every format, not just every mnemonic. +- Keep decoder tests independent from ALU or register-file behavior. + +## References + +- RISC-V unprivileged ISA instruction formats: https://riscv.org/technical/specifications/ +- RISC-V opcode map: https://github.com/riscv/riscv-opcodes +- GNU binutils RISC-V documentation: https://sourceware.org/binutils/docs/ + diff --git a/Tutorial/phase-04-first-cpu/phase-04-01-fetch-datapath-integration.md b/Tutorial/phase-04-first-cpu/phase-04-01-fetch-datapath-integration.md new file mode 100644 index 0000000..9cb484a --- /dev/null +++ b/Tutorial/phase-04-first-cpu/phase-04-01-fetch-datapath-integration.md @@ -0,0 +1,49 @@ +# Phase 4.1 - Fetch + Datapath Integration + +## Context + +This subphase builds the first CPU loop: fetch an instruction, decode it, execute it, +write back a result, and advance PC. + +## Goals + +- Integrate instruction BRAM at `0x2000_0000`. +- Execute straight-line arithmetic and M-extension operations. +- Learn stall control around BRAM and the M unit. + +## New Concepts + +- Reset PC: address where execution begins after reset. +- FSM state: named control state such as fetch, execute, wait, writeback. +- Instruction retirement: point where an instruction's architectural effects are complete. +- Stall: holding PC/state while waiting for a dependency. + +## How To Think About It + +The CPU is a protocol participant with its memories and M unit. Correct sequencing matters +more than minimizing cycles. + +## Learning Tasks + +- Write a cycle-by-cycle timeline for a simple `addi` instruction. +- Write a cycle-by-cycle timeline for a multiply/divide instruction. +- Decide what happens on illegal instruction before Phase 9 traps exist. + +## Pitfalls + +- Double-executing an instruction because PC or state advances at the wrong time. +- Starting the M unit repeatedly while waiting for it to finish. +- Losing the destination register number while a multi-cycle op is in flight. + +## Tooling And Testing + +- Use a testbench that stops after a fixed halt condition or maximum cycle count. +- Compare final register state and intermediate writeback events. +- Inspect PC, instruction, state, register write enable, and writeback data in waveforms. + +## References + +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ +- Vivado simulation documentation: https://docs.xilinx.com/ +- Project F memory initialization: https://projectf.io/posts/initialize-memory-in-verilog/ + diff --git a/Tutorial/phase-04-first-cpu/phase-04-02-ila-verification.md b/Tutorial/phase-04-first-cpu/phase-04-02-ila-verification.md new file mode 100644 index 0000000..97365a6 --- /dev/null +++ b/Tutorial/phase-04-first-cpu/phase-04-02-ila-verification.md @@ -0,0 +1,49 @@ +# Phase 4.2 - ILA Verification On FPGA + +## Context + +ILA lets you capture internal FPGA signals during real hardware execution. This is the +first serious hardware-debug milestone for the integrated CPU. + +## Goals + +- Learn how to select useful ILA probes. +- Capture PC, instruction, writeback, and state transitions. +- Compare hardware behavior to simulation. + +## New Concepts + +- ILA: Integrated Logic Analyzer, on-chip waveform capture over JTAG. +- Trigger: condition that starts or centers a capture. +- Sample depth: number of cycles stored in on-chip debug memory. +- Probe preservation: keeping signals from being optimized away. + +## How To Think About It + +ILA is an oscilloscope for internal logic. Use it to answer a specific question, not to +record everything. Every extra probe has cost. + +## Learning Tasks + +- Choose a trigger such as PC reaching the last instruction. +- Decide which signals prove correct instruction retirement. +- Compare one hardware trace with the simulator trace for the same program. + +## Pitfalls + +- Capturing too few cycles before/after the trigger. +- Debugging a different bitstream than the source you are reading. +- Assuming ILA changes nothing; debug IP consumes resources and can affect timing. + +## Tooling And Testing + +- Archive the program image, bitstream, and source commit used for hardware tests. +- Use `dont_touch` sparingly and only on debug-critical nets. +- Check timing again after inserting ILA. + +## References + +- Vivado ILA documentation: https://docs.xilinx.com/ +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- AMD/Xilinx debug methodology: https://docs.xilinx.com/ + diff --git a/Tutorial/phase-04-first-cpu/phase-04.md b/Tutorial/phase-04-first-cpu/phase-04.md new file mode 100644 index 0000000..d4e6700 --- /dev/null +++ b/Tutorial/phase-04-first-cpu/phase-04.md @@ -0,0 +1,49 @@ +# Phase 4 - First CPU + +## Context + +This phase connects fetch, decode, arithmetic, register-file, and writeback into a minimal +non-pipelined CPU. It executes straight-line arithmetic programs from instruction BRAM. + +## Goals + +- Build the first integrated datapath. +- Learn how BRAM latency shapes control flow. +- Verify architectural state changes instruction by instruction. + +## New Concepts + +- BRAM: block RAM, dedicated on-chip FPGA memory with synchronous access. +- PC: program counter, the address of the current or next instruction. +- Writeback: stage where a result is committed to the register file. +- CPI: cycles per instruction. + +## How To Think About It + +This is not a performance exercise. It is an integration exercise. The goal is to make +one instruction at a time retire correctly while respecting real memory latency. + +## Learning Tasks + +- Draw the fetch/execute FSM. +- Decide exactly when PC advances. +- Trace a short program and record expected register state after each instruction. + +## Pitfalls + +- Treating BRAM like a combinational array. +- Updating PC while a multi-cycle M operation is still in progress. +- Writing back results for instructions that should not write a register. + +## Tooling And Testing + +- Start with very small programs and known final register values. +- Use simulation waveforms before ILA. +- Add a visible "halt" or terminal condition for test programs. + +## References + +- AMD/Xilinx block memory documentation: https://docs.xilinx.com/ +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ +- Digital design FSM overview: https://www.chipverify.com/verilog/verilog-fsm + diff --git a/Tutorial/phase-05-control-flow/phase-05-01-branch-instructions.md b/Tutorial/phase-05-control-flow/phase-05-01-branch-instructions.md new file mode 100644 index 0000000..7fdc074 --- /dev/null +++ b/Tutorial/phase-05-control-flow/phase-05-01-branch-instructions.md @@ -0,0 +1,49 @@ +# Phase 5.1 - Branch Instructions + +## Context + +Branches compare two registers and conditionally update PC to a PC-relative target. They +enable loops and conditionals. + +## Goals + +- Implement `beq`, `bne`, `blt`, `bge`, `bltu`, and `bgeu`. +- Learn signed and unsigned branch comparisons. +- Verify target calculation and fall-through behavior. + +## New Concepts + +- PC-relative addressing: target is computed from current PC plus immediate. +- Branch comparator: dedicated comparison logic for branch conditions. +- B-type immediate: split immediate format used by branch instructions. +- Taken branch: branch condition true, PC becomes target. + +## How To Think About It + +Separate three questions: what is the target, is the condition true, and which PC should +be used next? Debugging is easier when those are visible independently. + +## Learning Tasks + +- Decode a B-type immediate by hand. +- Build test cases where signed and unsigned comparisons differ. +- Trace a loop from initialization through exit. + +## Pitfalls + +- Forgetting branch offsets are multiples of 2 bytes in the encoding. +- Comparing signed values with unsigned operators. +- Counting loop iterations incorrectly because PC update timing is unclear. + +## Tooling And Testing + +- Use assembler-generated branch encodings. +- In waveforms, inspect PC, immediate, comparator result, and next PC. +- Include backward and forward branch targets. + +## References + +- RISC-V unprivileged ISA, branch instructions: https://riscv.org/technical/specifications/ +- RISC-V assembly manual: https://github.com/riscv-non-isa/riscv-asm-manual +- RISC-V opcode data: https://github.com/riscv/riscv-opcodes + diff --git a/Tutorial/phase-05-control-flow/phase-05-02-jump-instructions.md b/Tutorial/phase-05-control-flow/phase-05-02-jump-instructions.md new file mode 100644 index 0000000..f9fb19d --- /dev/null +++ b/Tutorial/phase-05-control-flow/phase-05-02-jump-instructions.md @@ -0,0 +1,49 @@ +# Phase 5.2 - Jump Instructions + +## Context + +Jumps provide unconditional control transfer and function-call mechanics. `jal` is +PC-relative; `jalr` jumps through a register plus immediate. + +## Goals + +- Implement `jal` and `jalr`. +- Store PC + 4 into the destination register. +- Verify call and return sequences. + +## New Concepts + +- Return address: address of the instruction after the call. +- Indirect jump: target comes from a register rather than only the instruction. +- J-type immediate: immediate format used by `jal`. +- Calling convention: software agreement for argument, return, and saved registers. + +## How To Think About It + +`jal` and `jalr` are both "write link, then redirect PC." The redirect source differs; +the writeback behavior is shared. + +## Learning Tasks + +- Hand-trace a call and return using `ra` (`x1`). +- Verify `jalr` target bit 0 clearing. +- Learn which registers the RISC-V ABI uses for calls. + +## Pitfalls + +- Writing the jump target instead of PC + 4 into `rd`. +- Mishandling `rd = x0`, which should discard the link. +- Forgetting `jalr` is commonly used for returns and function pointers. + +## Tooling And Testing + +- Test `jal`, `jalr`, and `jalr x0, ra, 0` return style. +- Use objdump to confirm labels assemble to expected offsets. +- Inspect register writeback and PC change in the same trace. + +## References + +- RISC-V unprivileged ISA, jumps: https://riscv.org/technical/specifications/ +- RISC-V ELF psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc +- RISC-V assembly manual: https://github.com/riscv-non-isa/riscv-asm-manual + diff --git a/Tutorial/phase-05-control-flow/phase-05.md b/Tutorial/phase-05-control-flow/phase-05.md new file mode 100644 index 0000000..48ec5ac --- /dev/null +++ b/Tutorial/phase-05-control-flow/phase-05.md @@ -0,0 +1,49 @@ +# Phase 5 - Branches And Jumps + +## Context + +This phase adds control flow. The CPU stops being a straight-line executor and gains +loops, conditionals, function calls, and returns. + +## Goals + +- Implement conditional branches. +- Implement `jal` and `jalr`. +- Learn PC selection and control-transfer testing. + +## New Concepts + +- Branch target: destination address for a taken branch. +- Fall-through: next sequential PC, usually PC + 4. +- Link register: register receiving return address for calls. +- Control hazard: later pipeline issue where fetched instructions may be wrong. + +## How To Think About It + +Control flow is just PC update logic plus optional register writeback. Keep the +comparison, target calculation, and PC selection clearly separated. + +## Learning Tasks + +- Hand-compute branch and jump targets from instruction immediates. +- Trace a simple loop and count taken versus not-taken branches. +- Understand why `jalr` clears bit 0 of the target address. + +## Pitfalls + +- Off-by-four errors between current PC and next PC. +- Using unsigned comparison for signed branches. +- Forgetting that `jal`/`jalr` write PC + 4 to `rd`. + +## Tooling And Testing + +- Test both taken and not-taken paths for every branch type. +- Use short loops with known iteration counts. +- Probe PC source selection in simulation and ILA. + +## References + +- RISC-V branch and jump semantics: https://riscv.org/technical/specifications/ +- RISC-V assembly examples: https://github.com/riscv-non-isa/riscv-asm-manual +- Computer architecture control flow overview: https://www.nand2tetris.org/ + diff --git a/Tutorial/phase-06-load-store/phase-06-01-word-load-store.md b/Tutorial/phase-06-load-store/phase-06-01-word-load-store.md new file mode 100644 index 0000000..dcf1daa --- /dev/null +++ b/Tutorial/phase-06-load-store/phase-06-01-word-load-store.md @@ -0,0 +1,49 @@ +# Phase 6.1 - Word Load/Store + +## Context + +Word loads and stores are the first data-memory operations. They are simpler because +addresses must be 4-byte aligned and all byte lanes are active. + +## Goals + +- Add data BRAM. +- Issue D-bus requests for `lw` and `sw`. +- Learn request/response sequencing for loads. + +## New Concepts + +- Word: 32-bit value in RV32. +- Aligned access: address is a multiple of access size. +- Response latency: time from request acceptance to returned data. +- Bus decoder: logic that routes an address to the right memory/peripheral. + +## How To Think About It + +A store is mostly "send address and data." A load is "send address, wait for response, +then write back." That waiting must be explicit in the control FSM. + +## Learning Tasks + +- Draw a timeline for `sw` and `lw`. +- Mark when `req_valid`, `req_ready`, `rsp_valid`, and `rsp_ready` are meaningful. +- Decide what state is retained while waiting for load data. + +## Pitfalls + +- Writing back load data before BRAM response is valid. +- Advancing PC before a load has completed. +- Forgetting to tie unused `amo` and sub-word strobes consistently. + +## Tooling And Testing + +- Start with store-then-load of one word. +- Add tests for first and last valid data BRAM addresses. +- Use waveform checks for request acceptance and response timing. + +## References + +- RISC-V unprivileged ISA loads/stores: https://riscv.org/technical/specifications/ +- Vivado BRAM documentation: https://docs.xilinx.com/ +- AXI valid/ready concepts: https://developer.arm.com/documentation/ihi0022/latest + diff --git a/Tutorial/phase-06-load-store/phase-06-02-byte-halfword-access.md b/Tutorial/phase-06-load-store/phase-06-02-byte-halfword-access.md new file mode 100644 index 0000000..2d8be27 --- /dev/null +++ b/Tutorial/phase-06-load-store/phase-06-02-byte-halfword-access.md @@ -0,0 +1,49 @@ +# Phase 6.2 - Byte And Halfword Access + +## Context + +Sub-word memory operations make C programs practical. `char`, `short`, strings, and packed +data all depend on byte and halfword accesses. + +## Goals + +- Implement `lb`, `lbu`, `lh`, `lhu`, `sb`, and `sh`. +- Handle byte lanes and sign/zero extension. +- Trap misaligned accesses locally in the LSU. + +## New Concepts + +- Sign extension: preserving negative value when widening a smaller signed type. +- Zero extension: widening an unsigned value by filling high bits with zero. +- Byte enable: control bit selecting which byte lane is written. +- Misaligned access: address not divisible by the access size. + +## How To Think About It + +Stores are about choosing byte lanes. Loads are about extracting the correct lane and +extending it correctly. Misalignment is known before the bus request starts. + +## Learning Tasks + +- Map address low bits to byte lanes. +- Work through examples loading `0x80` as signed and unsigned byte. +- Decide how halfword alignment is detected. + +## Pitfalls + +- Applying sign extension before selecting the correct byte/halfword. +- Letting a misaligned request reach memory. +- Confusing endianness with bit numbering in diagrams. + +## Tooling And Testing + +- Test every byte offset for `sb` and `lb/lbu`. +- Test halfword offsets 0 and 2 as legal and 1 and 3 as misaligned. +- Use memory initialization patterns that make byte order obvious. + +## References + +- RISC-V unprivileged ISA load/store chapter: https://riscv.org/technical/specifications/ +- Endianness overview: https://en.wikipedia.org/wiki/Endianness +- Project F memory articles: https://projectf.io/posts/ + diff --git a/Tutorial/phase-06-load-store/phase-06-03-ila-memory-bus.md b/Tutorial/phase-06-load-store/phase-06-03-ila-memory-bus.md new file mode 100644 index 0000000..a00d185 --- /dev/null +++ b/Tutorial/phase-06-load-store/phase-06-03-ila-memory-bus.md @@ -0,0 +1,48 @@ +# Phase 6.3 - ILA On The Memory Bus + +## Context + +Memory bugs often need cycle-level visibility. ILA on the D-bus lets you see requests, +responses, byte strobes, and stalls in real hardware. + +## Goals + +- Learn practical bus-level debug with ILA. +- Capture load/store transactions. +- Compare hardware memory behavior with simulation. + +## New Concepts + +- Transaction: one logical memory operation, possibly spanning multiple cycles. +- Probe grouping: collecting related signals for readable waveforms. +- Trigger condition: event that starts capture. + +## How To Think About It + +Probe at protocol boundaries. If the LSU and memory disagree, the bus waveform tells you +which side violated the contract. + +## Learning Tasks + +- Choose probes for D-bus request payload, request handshake, response payload, response handshake. +- Create one program with predictable stores and loads. +- Trigger on a target address or final store. + +## Pitfalls + +- Capturing only data and not the valid/ready signals. +- Forgetting byte strobes when debugging sub-word stores. +- Using ILA before simulation has narrowed the failure. + +## Tooling And Testing + +- Use short programs so captures fit in ILA memory. +- Keep memory addresses distinctive and easy to recognize. +- Re-run timing after adding ILA probes. + +## References + +- Vivado ILA documentation: https://docs.xilinx.com/ +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- Wishbone spec for bus-debug vocabulary: https://cdn.opencores.org/downloads/wbspec_b4.pdf + diff --git a/Tutorial/phase-06-load-store/phase-06.md b/Tutorial/phase-06-load-store/phase-06.md new file mode 100644 index 0000000..1df2913 --- /dev/null +++ b/Tutorial/phase-06-load-store/phase-06.md @@ -0,0 +1,49 @@ +# Phase 6 - Load/Store + +## Context + +Load/store connects the CPU to data memory and later to memory-mapped devices. RISC-V is +a load/store architecture: arithmetic works on registers, memory access is explicit. + +## Goals + +- Add data BRAM at `0x8000_0000`. +- Define and use the D-bus contract. +- Support word, byte, and halfword memory operations. + +## New Concepts + +- LSU: load/store unit, responsible for address generation and data formatting. +- D-bus: data-side memory request/response channel. +- Byte lane: one of the four bytes in a 32-bit word. +- Write strobe: byte-enable mask for sub-word stores. + +## How To Think About It + +Memory operations are protocol transactions. Address alignment, byte selection, sign +extension, response timing, and error classification all matter. + +## Learning Tasks + +- Draw how a byte store updates one lane of a 32-bit word. +- Decide how the LSU detects misalignment before bus issue. +- Trace a load from execute through response and writeback. + +## Pitfalls + +- Returning unshifted data for byte/halfword loads. +- Treating misalignment as a bus error instead of an architectural exception. +- Forgetting stores do not write back to the register file. + +## Tooling And Testing + +- Test all byte offsets within a word. +- Use memory dumps or waveform inspection for store byte lanes. +- Add maximum cycle timeouts to catch hung bus handshakes. + +## References + +- RISC-V load/store semantics: https://riscv.org/technical/specifications/ +- AMD/Xilinx block RAM documentation: https://docs.xilinx.com/ +- Wishbone bus spec for handshake comparison: https://cdn.opencores.org/downloads/wbspec_b4.pdf + diff --git a/Tutorial/phase-07-uart-mmio/phase-07-01-uart-tx.md b/Tutorial/phase-07-uart-mmio/phase-07-01-uart-tx.md new file mode 100644 index 0000000..3477628 --- /dev/null +++ b/Tutorial/phase-07-uart-mmio/phase-07-01-uart-tx.md @@ -0,0 +1,49 @@ +# Phase 7.1 - UART TX Module + +## Context + +The TX module serializes bytes into start bit, data bits, and stop bit at a fixed baud +rate. It is a small timing-driven FSM. + +## Goals + +- Build a standalone transmitter. +- Learn baud-rate timing from the 50 MHz system clock. +- Verify busy/send behavior before CPU integration. + +## New Concepts + +- Baud rate: symbols per second on the serial line. +- Start bit: low bit marking beginning of a frame. +- Stop bit: high bit marking end of a frame. +- Bit timer: counter that holds each serial bit for the right number of clocks. + +## How To Think About It + +UART TX is a deterministic shift-and-timer machine. The hard part is not data structure; +it is exact timing and clean handoff from "send byte" to "busy." + +## Learning Tasks + +- Compute clocks per bit at 50 MHz and 115200 baud. +- Draw TX line waveform for one byte. +- Decide when `busy` asserts and deasserts. + +## Pitfalls + +- Off-by-one errors in the bit timer. +- Accepting a new byte before the previous frame has completed. +- Forgetting idle line is high. + +## Tooling And Testing + +- Simulate one byte and inspect the waveform. +- Test back-to-back bytes. +- On hardware, send a fixed message before involving the CPU. + +## References + +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- UART framing overview: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter +- Nandland UART tutorial: https://nandland.com/uart-serial-port-module/ + diff --git a/Tutorial/phase-07-uart-mmio/phase-07-02-uart-rx.md b/Tutorial/phase-07-uart-mmio/phase-07-02-uart-rx.md new file mode 100644 index 0000000..4f71a07 --- /dev/null +++ b/Tutorial/phase-07-uart-mmio/phase-07-02-uart-rx.md @@ -0,0 +1,49 @@ +# Phase 7.2 - UART RX Module + +## Context + +The RX module samples an asynchronous serial input and reconstructs bytes. It must handle +input synchronization and sampling near bit centers. + +## Goals + +- Build a standalone receiver. +- Learn oversampling and input synchronization. +- Produce a received byte plus valid indication. + +## New Concepts + +- Asynchronous input: signal not aligned to the FPGA clock. +- Synchronizer: flip-flop chain reducing metastability risk. +- Metastability: temporary uncertain state when sampling near an input transition. +- Oversampling: sampling multiple times per bit period to find stable centers. + +## How To Think About It + +RX is less forgiving than TX because the external signal is not clocked by your FPGA. +Respect clock-domain boundary hygiene even for a slow UART pin. + +## Learning Tasks + +- Draw how a start bit is detected. +- Decide the sample point for each data bit. +- Define what `valid` means and how it is cleared. + +## Pitfalls + +- Sampling the RX pin without synchronization. +- Sampling too close to bit transitions. +- Losing bytes because there is no buffering or clear valid/ready policy. + +## Tooling And Testing + +- Simulate with ideal frames and then slightly shifted frames. +- Test receiver behavior with back-to-back bytes. +- Add a FIFO later if software cannot poll fast enough. + +## References + +- Metastability overview: https://www.01signal.com/verilog-design/clockdomains/crossing-basics/ +- Nandland UART receiver tutorial: https://nandland.com/uart-serial-port-module/ +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual + diff --git a/Tutorial/phase-07-uart-mmio/phase-07-03-bus-decoder-mmio.md b/Tutorial/phase-07-uart-mmio/phase-07-03-bus-decoder-mmio.md new file mode 100644 index 0000000..7f167d0 --- /dev/null +++ b/Tutorial/phase-07-uart-mmio/phase-07-03-bus-decoder-mmio.md @@ -0,0 +1,49 @@ +# Phase 7.3 - Bus Decoder + MMIO + +## Context + +The D-bus now routes requests to either RAM or UART registers. This is the first small +SoC-style memory map. + +## Goals + +- Decode address ranges for RAM and UART. +- Implement UART TX, RX, and status registers. +- Preserve the D-bus handshake contract. + +## New Concepts + +- Address map: assignment of address ranges to memory or devices. +- Register side effect: read or write that changes device state. +- Unmapped access: address with no valid target, later an access fault. +- Peripheral slave: bus endpoint that responds to device register accesses. + +## How To Think About It + +MMIO registers are a hardware/software ABI. Once firmware depends on them, changing +semantics becomes painful. Document behavior precisely. + +## Learning Tasks + +- Write a register table with access type and side effects. +- Decide read behavior for write-only registers and write behavior for read-only registers. +- Trace a store to UART TX through the D-bus decoder. + +## Pitfalls + +- Having two slaves respond to the same address. +- Letting no slave respond and hanging the bus forever. +- Making status bits unclear or inverted relative to software expectations. + +## Tooling And Testing + +- Unit-test the decoder separately from UART timing. +- Use ILA probes on selected slave, request, response, and UART status. +- Test unmapped access behavior once trap support exists. + +## References + +- OSDev MMIO overview: https://wiki.osdev.org/Memory_Mapped_Registers_in_C/C%2B%2B +- RISC-V privileged spec for access faults later: https://riscv.org/technical/specifications/ +- Wishbone bus spec: https://cdn.opencores.org/downloads/wbspec_b4.pdf + diff --git a/Tutorial/phase-07-uart-mmio/phase-07-04-hello-world.md b/Tutorial/phase-07-uart-mmio/phase-07-04-hello-world.md new file mode 100644 index 0000000..be47c5e --- /dev/null +++ b/Tutorial/phase-07-uart-mmio/phase-07-04-hello-world.md @@ -0,0 +1,48 @@ +# Phase 7.4 - Hello World + +## Context + +This is the first user-visible proof that software running on your CPU can communicate +with the outside world through MMIO. + +## Goals + +- Run a hand-written assembly program from instruction BRAM. +- Poll UART status and write bytes to TX data. +- Verify output in a terminal. + +## New Concepts + +- Polling: repeatedly reading status until a condition is true. +- Firmware: low-level software running directly on the hardware. +- Memory-mapped register access: using load/store instructions to control a device. + +## How To Think About It + +This milestone tests the full vertical slice: instruction fetch, decode, ALU, branches, +loads/stores, bus decode, UART, board pinout, and terminal settings. + +## Learning Tasks + +- Trace each instruction in the polling loop. +- Explain why the program waits for `tx_busy` to clear. +- Identify every hardware block involved in printing one character. + +## Pitfalls + +- Terminal configured for the wrong baud or line settings. +- Writing bytes without checking busy status. +- Debugging UART first when the actual bug is branch/load/store behavior. + +## Tooling And Testing + +- First verify UART standalone. +- In hardware, probe UART status and D-bus writes if the terminal is silent. +- Keep the program tiny and deterministic. + +## References + +- RISC-V assembly manual: https://github.com/riscv-non-isa/riscv-asm-manual +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- UART overview: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter + diff --git a/Tutorial/phase-07-uart-mmio/phase-07.md b/Tutorial/phase-07-uart-mmio/phase-07.md new file mode 100644 index 0000000..4d30804 --- /dev/null +++ b/Tutorial/phase-07-uart-mmio/phase-07.md @@ -0,0 +1,49 @@ +# Phase 7 - Memory-Mapped UART + +## Context + +This phase connects software-visible memory addresses to a peripheral. UART becomes the +first external communication path driven by CPU instructions. + +## Goals + +- Build standalone TX and RX UART blocks. +- Map UART registers into the D-bus address space. +- Print a message from software running on the CPU. + +## New Concepts + +- MMIO: memory-mapped I/O; device registers accessed with normal loads/stores. +- Address decoder: logic routing addresses to selected slaves. +- Status register: read-only register exposing peripheral state. +- FIFO: queue buffering bytes between producer and consumer. + +## How To Think About It + +The CPU should not know about UART internals. It issues stores and loads; the bus decoder +and peripheral register file translate those into device behavior. + +## Learning Tasks + +- Draw the UART register map. +- Decide what happens if software writes while TX is busy. +- Decide when RX data is consumed and status changes. + +## Pitfalls + +- Making register side effects ambiguous. +- Forgetting software must poll status before writes. +- Combining peripheral timing with CPU timing too tightly. + +## Tooling And Testing + +- Verify UART standalone before MMIO integration. +- Use a terminal for end-to-end tests and ILA for bus/peripheral mismatches. +- Keep register behavior documented for firmware authors. + +## References + +- Digilent Arty A7 USB-UART notes: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- RISC-V platform-level interrupt spec for later context: https://github.com/riscv/riscv-plic-spec +- OSDev MMIO overview: https://wiki.osdev.org/Memory_Mapped_Registers_in_C/C%2B%2B + diff --git a/Tutorial/phase-08-gcc-toolchain/phase-08-01-linker-startup.md b/Tutorial/phase-08-gcc-toolchain/phase-08-01-linker-startup.md new file mode 100644 index 0000000..ad75ccb --- /dev/null +++ b/Tutorial/phase-08-gcc-toolchain/phase-08-01-linker-startup.md @@ -0,0 +1,49 @@ +# Phase 8.1 - Linker Script + Startup Code + +## Context + +C programs assume a runtime environment. Bare metal provides none unless you create it: +memory layout, stack pointer, zeroed globals, and entry point. + +## Goals + +- Place code and read-only data in instruction memory. +- Place writable data, BSS, and stack in data memory. +- Understand what startup code must do before calling `main`. + +## New Concepts + +- Section: named region of an object file, such as `.text` or `.bss`. +- Load address: where bytes are stored in the image. +- Virtual/runtime address: where code expects them at execution. +- Stack pointer: register pointing to top of current stack frame. + +## How To Think About It + +The linker script is the contract between memory hardware and compiled software. If it +lies, the CPU may be correct and the program will still fail. + +## Learning Tasks + +- Draw the memory layout from reset PC through stack top. +- Identify which sections need initial contents and which are zero-filled. +- Understand why `.bss` must be cleared. + +## Pitfalls + +- Placing stack where it collides with `.bss` or data. +- Forgetting alignment requirements. +- Using library code that expects syscalls or a full C runtime. + +## Tooling And Testing + +- Inspect ELF sections and symbols with binutils. +- Disassemble startup code and confirm the first instructions are supported. +- Create a C program with global initialized and uninitialized variables. + +## References + +- GNU ld scripts: https://sourceware.org/binutils/docs/ld/Scripts.html +- RISC-V ELF psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc +- OSDev bare bones concepts: https://wiki.osdev.org/Bare_Bones + diff --git a/Tutorial/phase-08-gcc-toolchain/phase-08-02-first-gcc-program.md b/Tutorial/phase-08-gcc-toolchain/phase-08-02-first-gcc-program.md new file mode 100644 index 0000000..5a899d8 --- /dev/null +++ b/Tutorial/phase-08-gcc-toolchain/phase-08-02-first-gcc-program.md @@ -0,0 +1,49 @@ +# Phase 8.2 - First GCC Program + +## Context + +The first compiled program proves your hardware can execute compiler-generated code, not +just carefully hand-authored assembly. + +## Goals + +- Compile a tiny C firmware for `rv32im`. +- Convert the ELF into a memory image. +- Print through UART MMIO from C. + +## New Concepts + +- `-march`: compiler ISA target string. +- `-mabi`: ABI selection, here `ilp32` for 32-bit integer/long/pointer. +- objcopy: tool that converts ELF into raw binary or other formats. +- Disassembly: readable assembly representation of generated machine code. + +## How To Think About It + +Treat the compiler as an external producer of instructions. Your job is to verify that +every emitted instruction is implemented or fails loudly. + +## Learning Tasks + +- Compare C source to generated assembly. +- Identify every load/store used for UART access. +- Confirm no CSR or `fence.i` instructions appear before Phase 9. + +## Pitfalls + +- Using the wrong `-march` and accidentally generating unsupported instructions. +- Forgetting `volatile` on MMIO accesses in firmware. +- Assuming the compiler will preserve simple-looking loops exactly. + +## Tooling And Testing + +- Always inspect early firmware with objdump. +- Build at low optimization first, then compare optimized output later. +- Keep firmware small enough to single-step mentally. + +## References + +- GCC RISC-V options: https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Options.html +- RISC-V ELF psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc +- GNU objcopy: https://sourceware.org/binutils/docs/binutils/objcopy.html + diff --git a/Tutorial/phase-08-gcc-toolchain/phase-08-03-fill-rv32i-gaps.md b/Tutorial/phase-08-gcc-toolchain/phase-08-03-fill-rv32i-gaps.md new file mode 100644 index 0000000..1b5c883 --- /dev/null +++ b/Tutorial/phase-08-gcc-toolchain/phase-08-03-fill-rv32i-gaps.md @@ -0,0 +1,49 @@ +# Phase 8.3 - Fill Remaining RV32I Gaps + +## Context + +Compiler output will expose missing instructions and edge cases. This subphase closes +the base ISA gaps before moving into traps and privilege. + +## Goals + +- Implement remaining RV32I instructions required by compiler output. +- Decode `fence` as safe in a cacheless design. +- Make unsupported system instructions halt visibly. + +## New Concepts + +- `fence`: memory-ordering instruction. +- `fence.i`: instruction-stream synchronization instruction. +- `ecall`: environment call, later a trap. +- `ebreak`: breakpoint instruction, later a trap. + +## How To Think About It + +Incomplete ISA behavior should be loud. A clean halt with PC and instruction word is far +better than silent execution through an unsupported instruction. + +## Learning Tasks + +- Make a list of every RV32I instruction and current support status. +- Disassemble increasingly complex C programs and mark new instructions. +- Understand why `fence` can be harmless without caches while `fence.i` still needs decode policy. + +## Pitfalls + +- Treating `ecall` or `ebreak` as NOPs. +- Missing uncommon instructions emitted by switch statements or stack setup. +- Debugging firmware before checking the disassembly. + +## Tooling And Testing + +- Add illegal-instruction tests. +- Keep a cycle limit in simulations to catch hangs. +- Use compiler output as a test source, not a proof of correctness. + +## References + +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ +- GCC RISC-V options: https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Options.html +- RISC-V assembly manual: https://github.com/riscv-non-isa/riscv-asm-manual + diff --git a/Tutorial/phase-08-gcc-toolchain/phase-08-04-meaningful-c-program.md b/Tutorial/phase-08-gcc-toolchain/phase-08-04-meaningful-c-program.md new file mode 100644 index 0000000..dbdf860 --- /dev/null +++ b/Tutorial/phase-08-gcc-toolchain/phase-08-04-meaningful-c-program.md @@ -0,0 +1,49 @@ +# Phase 8.4 - Meaningful C Program + +## Context + +After minimal C works, write a program large enough to exercise stack, branches, calls, +arrays, strings, and UART interaction. + +## Goals + +- Run an interactive or semi-interactive firmware. +- Exercise real C control flow and memory behavior. +- Build confidence before adding privilege and traps. + +## New Concepts + +- Stack frame: per-call storage for return address, locals, and saved registers. +- Recursion: function calling itself, useful for stressing stack behavior. +- Jump table: compiler-generated table for some switch statements. +- Serial monitor: small firmware command interface over UART. + +## How To Think About It + +This is a system test, not a unit test. Failures can come from CPU, memory, compiler +assumptions, or firmware bugs. Use it after smaller tests pass. + +## Learning Tasks + +- Trace one function call through prologue and epilogue. +- Observe stack growth and confirm it stays within data memory. +- Add one feature at a time to firmware and inspect generated assembly. + +## Pitfalls + +- Pulling in library functions accidentally. +- Overflowing the tiny BRAM-backed stack. +- Debugging optimized code before unoptimized code works. + +## Tooling And Testing + +- Use map files to understand memory usage. +- Add firmware-visible test results through UART. +- Keep a known-good simple firmware for regression comparisons. + +## References + +- RISC-V ELF psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc +- GNU ld map files: https://sourceware.org/binutils/docs/ld/Options.html +- Embedded Artistry bare-metal articles: https://embeddedartistry.com/ + diff --git a/Tutorial/phase-08-gcc-toolchain/phase-08-05-riscv-tests-compliance.md b/Tutorial/phase-08-gcc-toolchain/phase-08-05-riscv-tests-compliance.md new file mode 100644 index 0000000..48b7fd2 --- /dev/null +++ b/Tutorial/phase-08-gcc-toolchain/phase-08-05-riscv-tests-compliance.md @@ -0,0 +1,49 @@ +# Phase 8.5 - riscv-tests Compliance + +## Context + +Official instruction tests catch ISA bugs that ad hoc tests miss. Run them before adding +trap, interrupt, and privilege complexity. + +## Goals + +- Build and run `rv32ui-p-*` and `rv32um-p-*`. +- Create a simulation harness that loads test programs. +- Establish regression coverage for later phases. + +## New Concepts + +- Compliance test: focused program checking architectural behavior. +- Harness: wrapper that provides memory, stop condition, and pass/fail reporting. +- Signature region: memory region where tests record results. +- Regression: test suite run repeatedly to catch breakage. + +## How To Think About It + +Do not treat compliance tests as optional polish. They are how you avoid stacking new +features on top of unknown ISA bugs. + +## Learning Tasks + +- Understand how a riscv-test indicates pass/fail. +- Learn how ELF sections map into your BRAM model. +- Categorize failures by decode, execute, memory, or control-flow likely cause. + +## Pitfalls + +- Running tests with the wrong ISA target. +- Assuming the test harness memory layout matches your hardware automatically. +- Ignoring one failing test because normal firmware seems to work. + +## Tooling And Testing + +- Automate running all tests once the harness exists. +- Keep failing waveforms short and reproducible. +- Re-run the suite after every meaningful datapath change. + +## References + +- riscv-tests repository: https://github.com/riscv-software-src/riscv-tests +- RISC-V architectural tests: https://github.com/riscv-non-isa/riscv-arch-test +- RISC-V unprivileged ISA: https://riscv.org/technical/specifications/ + diff --git a/Tutorial/phase-08-gcc-toolchain/phase-08.md b/Tutorial/phase-08-gcc-toolchain/phase-08.md new file mode 100644 index 0000000..7faf14c --- /dev/null +++ b/Tutorial/phase-08-gcc-toolchain/phase-08.md @@ -0,0 +1,49 @@ +# Phase 8 - GCC Toolchain Integration + +## Context + +This phase moves from hand-written assembly to compiled C. The CPU must now satisfy the +expectations of a real compiler, linker, and runtime startup. + +## Goals + +- Create linker script and startup code. +- Compile simple C programs for the implemented ISA. +- Run RISC-V compliance-style tests. + +## New Concepts + +- Linker script: file controlling where sections are placed in memory. +- crt0: startup code that prepares stack and globals before `main`. +- ELF: executable/linkable file format used by toolchains. +- ABI: software binary contract for registers, stack, calls, and data layout. + +## How To Think About It + +GCC is an unforgiving test generator. It will use legal instruction combinations you did +not think to test manually. That is exactly why this phase matters. + +## Learning Tasks + +- Understand sections: `.text`, `.rodata`, `.data`, `.bss`, and stack. +- Learn how ELF becomes a BRAM initialization image. +- Compare disassembly with your implemented instruction list. + +## Pitfalls + +- Advertising ISA extensions before hardware decodes them. +- Forgetting to initialize stack or zero `.bss`. +- Assuming "simple C" means "simple instruction stream." + +## Tooling And Testing + +- Use objdump for every early firmware image. +- Keep `-nostdlib` until you deliberately provide runtime support. +- Run compliance tests before adding traps and privilege complexity. + +## References + +- RISC-V ELF psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc +- GNU linker scripts: https://sourceware.org/binutils/docs/ld/Scripts.html +- riscv-tests: https://github.com/riscv-software-src/riscv-tests + diff --git a/Tutorial/phase-09-csrs-traps/phase-09.md b/Tutorial/phase-09-csrs-traps/phase-09.md new file mode 100644 index 0000000..199f063 --- /dev/null +++ b/Tutorial/phase-09-csrs-traps/phase-09.md @@ -0,0 +1,52 @@ +# Phase 9 - CSRs + M-Mode Trap Handling + +## Context + +This phase adds architectural control registers and the mechanism for exceptions and +interrupts. The CPU stops merely halting on faults and starts transferring control to +software handlers. + +## Goals + +- Implement key machine-mode CSRs. +- Implement CSR instructions and `mret`. +- Add trap entry for illegal instructions, breakpoints, ecalls, and access faults. + +## New Concepts + +- CSR: control and status register, special architectural register accessed by CSR instructions. +- Trap: synchronous exception or asynchronous interrupt that redirects execution. +- `mepc`: CSR holding PC to return to after a trap. +- `mcause`: CSR describing why the trap occurred. +- `mtvec`: CSR holding trap handler address. +- `mret`: instruction returning from machine-mode trap. + +## How To Think About It + +Traps are controlled context switches within the CPU. Hardware records enough state for +software to inspect the cause, handle it, and resume or terminate. + +## Learning Tasks + +- Draw the exact state updates on trap entry. +- Write a table of each exception source and its `mcause`. +- Understand which PC is saved for each exception type. + +## Pitfalls + +- Saving the wrong PC in `mepc`. +- Updating architectural state for a faulting instruction that should not retire. +- Treating all exceptions as illegal instructions. + +## Tooling And Testing + +- Create tiny programs that intentionally trigger one exception at a time. +- Inspect CSR values after trap entry in simulation. +- Test `mret` returning to a known instruction address. + +## References + +- RISC-V privileged architecture spec: https://riscv.org/technical/specifications/ +- RISC-V CSR instruction semantics: https://riscv.org/technical/specifications/ +- RISC-V educational trap handling notes: https://osblog.stephenmarz.com/ + diff --git a/Tutorial/phase-10-timer/phase-10.md b/Tutorial/phase-10-timer/phase-10.md new file mode 100644 index 0000000..c8ed1d9 --- /dev/null +++ b/Tutorial/phase-10-timer/phase-10.md @@ -0,0 +1,49 @@ +# Phase 10 - Timer + +## Context + +A timer provides a predictable interrupt source. Operating systems need timers for +scheduling, timeouts, and delays. + +## Goals + +- Implement a free-running machine timer counter. +- Implement compare register behavior. +- Generate and handle timer interrupts through the trap path. + +## New Concepts + +- `mtime`: monotonically increasing machine timer counter. +- `mtimecmp`: compare value that triggers a timer interrupt when reached. +- Interrupt: asynchronous trap caused by external or timer event. +- Pending bit: CSR bit indicating an interrupt source is waiting. + +## How To Think About It + +Timer hardware is simple; correct interrupt integration is the real lesson. You must +coordinate compare logic, pending state, enable bits, global interrupt enable, and trap entry. + +## Learning Tasks + +- Draw how `mtime`, `mtimecmp`, `mip`, and `mie` relate. +- Decide how software reads/writes a 64-bit timer from a 32-bit core. +- Trace a timer interrupt from compare match to handler entry. + +## Pitfalls + +- Generating level interrupts that never clear. +- Mishandling 64-bit register access from RV32 software. +- Taking interrupts while already in an unsafe state. + +## Tooling And Testing + +- Simulate with small compare values so tests finish quickly. +- Test enabling, disabling, pending, and clearing behavior separately. +- Add waveform probes for `mtime`, compare result, pending bit, and trap entry. + +## References + +- RISC-V privileged architecture spec: https://riscv.org/technical/specifications/ +- RISC-V ACLINT specification: https://github.com/riscv-non-isa/riscv-aclint +- Linux timekeeping background: https://docs.kernel.org/timers/ + diff --git a/Tutorial/phase-11-interrupt-controller/phase-11.md b/Tutorial/phase-11-interrupt-controller/phase-11.md new file mode 100644 index 0000000..3b90f26 --- /dev/null +++ b/Tutorial/phase-11-interrupt-controller/phase-11.md @@ -0,0 +1,51 @@ +# Phase 11 - Interrupt Controller + +## Context + +After the timer, the system needs a way to manage external interrupt sources such as UART +RX. A PLIC-like controller arbitrates and presents external interrupts to the CPU. + +## Goals + +- Build a minimal interrupt controller. +- Connect UART RX as an interrupt source. +- Learn interrupt priority, enable, pending, claim, and complete concepts. + +## New Concepts + +- PLIC: Platform-Level Interrupt Controller used by many RISC-V systems. +- Interrupt priority: ordering among pending interrupt sources. +- Claim: software reads which interrupt it should service. +- Complete: software tells the controller an interrupt has been handled. +- Edge/level interrupt: whether an event is a pulse or held condition. + +## How To Think About It + +An interrupt controller is hardware/software coordination. The device requests service, +the controller prioritizes it, the CPU traps, and software acknowledges the right places +in the right order. + +## Learning Tasks + +- Draw interrupt flow from UART RX byte to trap handler. +- Decide whether UART interrupt is level-sensitive or edge-sensitive. +- Understand claim/complete even if your first controller is simplified. + +## Pitfalls + +- Losing an interrupt event because it is only a one-cycle pulse. +- Clearing the device before software can observe why it interrupted. +- Taking an interrupt repeatedly because pending state is never cleared. + +## Tooling And Testing + +- Start with one interrupt source before adding priority. +- Test masked, unmasked, pending, claim, and complete behavior. +- Use ILA on interrupt request, pending, CPU external interrupt, and trap entry. + +## References + +- RISC-V PLIC specification: https://github.com/riscv/riscv-plic-spec +- RISC-V privileged architecture spec: https://riscv.org/technical/specifications/ +- Linux interrupt concepts: https://docs.kernel.org/core-api/genericirq.html + diff --git a/Tutorial/phase-12-atomics/phase-12.md b/Tutorial/phase-12-atomics/phase-12.md new file mode 100644 index 0000000..cdbc05d --- /dev/null +++ b/Tutorial/phase-12-atomics/phase-12.md @@ -0,0 +1,49 @@ +# Phase 12 - A Extension + +## Context + +The RISC-V A extension adds atomic memory operations. Mainline Linux expects atomics for +locking, reference counts, futexes, and synchronization. + +## Goals + +- Implement LR/SC. +- Implement AMO read-modify-write instructions. +- Run RV32A tests before Linux work. + +## New Concepts + +- Atomic operation: memory operation that appears indivisible to other agents. +- LR/SC: load-reserved/store-conditional pair. +- Reservation: remembered address that allows a later store-conditional to succeed. +- AMO: atomic memory operation combining load, operation, and store. + +## How To Think About It + +In a single-hart, single-master system atomics are conceptually simple. The value is in +building the right architectural behavior now so the Linux path is realistic later. + +## Learning Tasks + +- Study success and failure cases for `sc.w`. +- Decide when the reservation is cleared. +- List AMO operations and their signed/unsigned comparison behavior. + +## Pitfalls + +- Returning the wrong success code from `sc.w`. +- Forgetting AMOs return the original memory value. +- Assuming single-hart shortcuts will remain valid if DMA or another master is added. + +## Tooling And Testing + +- Run `rv32ua` tests from riscv-tests or architectural tests. +- Use memory traces to verify read-modify-write ordering. +- Test reservation clearing on stores to the reserved address. + +## References + +- RISC-V unprivileged ISA, A extension: https://riscv.org/technical/specifications/ +- riscv-tests: https://github.com/riscv-software-src/riscv-tests +- Linux atomic operations documentation: https://docs.kernel.org/core-api/wrappers/atomic_t.html + diff --git a/Tutorial/phase-13-pipeline/phase-13.md b/Tutorial/phase-13-pipeline/phase-13.md new file mode 100644 index 0000000..5ba2ad4 --- /dev/null +++ b/Tutorial/phase-13-pipeline/phase-13.md @@ -0,0 +1,50 @@ +# Phase 13 - Pipeline + +## Context + +Pipelining overlaps multiple instructions to improve throughput. This is optional for +the Linux goal but highly educational for computer architecture. + +## Goals + +- Insert registers between conceptual stages. +- Handle data and control hazards. +- Re-run regression tests after changing timing structure. + +## New Concepts + +- Pipeline stage: portion of instruction work separated by registers. +- Hazard: situation where overlapping instructions would produce wrong behavior. +- Forwarding: using a result before it reaches the register file. +- Flush: discarding wrong-path instructions. +- Stall: holding one or more stages until a hazard clears. + +## How To Think About It + +A pipeline is not just adding registers. It changes when values are available and when +instructions retire. Correctness depends on explicit hazard handling. + +## Learning Tasks + +- Draw instruction timelines for dependent arithmetic operations. +- Identify where branch decisions occur and what must be flushed. +- Decide how multi-cycle M and memory operations interact with the pipeline. + +## Pitfalls + +- Adding pipeline registers before defining valid/kill/stall behavior. +- Forgetting load-use hazards. +- Letting exceptions retire out of order. + +## Tooling And Testing + +- Keep a non-pipelined core as a conceptual reference. +- Run compliance tests before and after each pipeline milestone. +- Add trace logging of retired instructions if possible. + +## References + +- Computer Organization and Design RISC-V edition: https://shop.elsevier.com/books/computer-organization-and-design-risc-v-edition/patterson/978-0-12-820331-6 +- Hazard overview: https://en.wikipedia.org/wiki/Hazard_(computer_architecture) +- RISC-V formal interface concepts: https://github.com/SymbioticEDA/riscv-formal + diff --git a/Tutorial/phase-14-flash-dram/phase-14.md b/Tutorial/phase-14-flash-dram/phase-14.md new file mode 100644 index 0000000..6ba9c88 --- /dev/null +++ b/Tutorial/phase-14-flash-dram/phase-14.md @@ -0,0 +1,50 @@ +# Phase 14 - SPI Flash Boot + DRAM + +## Context + +BRAM is small and initialized by the FPGA bitstream. A Linux-capable system needs +persistent boot storage and much larger RAM, so this phase adds flash boot and DDR3L. + +## Goals + +- Boot from on-board SPI flash. +- Integrate DDR3L through the Xilinx MIG IP. +- Move executable images into DRAM and run from there. + +## New Concepts + +- SPI flash: serial nonvolatile storage on the board. +- DRAM: external dynamic memory requiring a controller and refresh. +- MIG: Xilinx Memory Interface Generator IP for DDR memory controllers. +- Bootloader: small program that prepares memory and jumps to a larger image. +- Arbiter: logic choosing which bus master accesses shared memory. + +## How To Think About It + +This is the first major SoC integration phase. External memory is not a simple array: +latency varies, calibration matters, and reset sequencing becomes important. + +## Learning Tasks + +- Understand the Arty A7 memory devices and their address ranges. +- Draw boot flow from reset to flash fetch to DRAM copy to jump. +- Decide how I-bus and D-bus arbitrate for DRAM. + +## Pitfalls + +- Assuming DRAM is ready immediately after FPGA reset. +- Ignoring MIG clocking and reset requirements. +- Mixing boot ROM, flash aperture, and DRAM addresses without a clear map. + +## Tooling And Testing + +- Validate DRAM with a standalone memory test before CPU boot. +- Use ILA around MIG app interface and bus arbiter. +- Keep a tiny BRAM-resident fallback test path while debugging flash/DRAM. + +## References + +- Digilent Arty A7 reference: https://digilent.com/reference/programmable-logic/arty-a7/reference-manual +- AMD/Xilinx MIG documentation: https://docs.xilinx.com/ +- JEDEC DDR background: https://www.jedec.org/standards-documents/focus/memory-module-design + diff --git a/Tutorial/phase-15-privilege-sv32/phase-15.md b/Tutorial/phase-15-privilege-sv32/phase-15.md new file mode 100644 index 0000000..bffe244 --- /dev/null +++ b/Tutorial/phase-15-privilege-sv32/phase-15.md @@ -0,0 +1,51 @@ +# Phase 15 - S-Mode, U-Mode, Sv32 Virtual Memory + +## Context + +Linux normally runs with privilege separation and virtual memory. This phase adds +supervisor/user modes and Sv32 address translation. + +## Goals + +- Add supervisor and user privilege modes. +- Implement Sv32 page-table walking. +- Add S-mode CSRs and page-fault traps. + +## New Concepts + +- Privilege mode: execution level controlling access rights. +- S-mode: supervisor mode, where the OS kernel normally runs. +- U-mode: user mode, where applications normally run. +- MMU: memory management unit performing address translation and protection. +- Sv32: RISC-V 32-bit virtual-memory scheme with two-level page tables. +- TLB: translation lookaside buffer, cache of recent address translations. + +## How To Think About It + +Virtual memory is both translation and permission checking. A correct page walker that +ignores permissions is not enough for an OS. + +## Learning Tasks + +- Draw Sv32 virtual address fields, page table levels, and PTE format. +- Trace one virtual load through translation to physical memory. +- Learn which traps are page faults versus access faults. + +## Pitfalls + +- Confusing physical memory protection with virtual page permissions. +- Mishandling `satp` updates and `sfence.vma`. +- Letting user mode access supervisor-only pages. + +## Tooling And Testing + +- Start with hand-built page tables and one mapped page. +- Test instruction, load, and store page faults separately. +- Add trace visibility for virtual address, PTEs, physical address, and cause. + +## References + +- RISC-V privileged architecture spec, Sv32: https://riscv.org/technical/specifications/ +- Linux memory-management docs: https://docs.kernel.org/mm/ +- OSDev paging overview: https://wiki.osdev.org/Paging + diff --git a/Tutorial/phase-16-linux-boot-contract/phase-16.md b/Tutorial/phase-16-linux-boot-contract/phase-16.md new file mode 100644 index 0000000..e88ad0f --- /dev/null +++ b/Tutorial/phase-16-linux-boot-contract/phase-16.md @@ -0,0 +1,50 @@ +# Phase 16 - Linux Boot Contract + +## Context + +Linux does not start from reset like bare-metal firmware. It expects a specific entry +state, memory layout, device tree, and usually firmware services. + +## Goals + +- Define the exact kernel entry ABI. +- Provide a device tree describing the SoC. +- Choose direct M-mode Linux or OpenSBI + S-mode Linux. + +## New Concepts + +- Device tree: data structure describing hardware to the kernel. +- DTB: compiled binary form of a device tree source. +- SBI: Supervisor Binary Interface, firmware API used by S-mode kernels. +- OpenSBI: common RISC-V machine-mode firmware implementation. +- Hart: RISC-V hardware thread, roughly a CPU core/thread. + +## How To Think About It + +This phase is about removing ambiguity before kernel bring-up. If the boot contract is +wrong, Linux often just hangs early with little output. + +## Learning Tasks + +- Read the RISC-V Linux boot protocol and list required register state. +- Draft a device tree matching your memory map and interrupt topology. +- Decide whether the first Linux attempt uses direct M-mode or OpenSBI. + +## Pitfalls + +- Passing the wrong DTB address in `a1`. +- Loading the kernel at the wrong alignment. +- Claiming device-tree compatibility with hardware you did not implement. + +## Tooling And Testing + +- Validate DTS with `dtc`. +- Keep early console strategy simple and documented. +- Build small firmware checks that print boot parameters before jumping to Linux. + +## References + +- RISC-V Linux boot protocol: https://docs.kernel.org/arch/riscv/boot.html +- RISC-V device-tree CPU bindings: https://www.kernel.org/doc/Documentation/devicetree/bindings/riscv/cpus.yaml +- OpenSBI: https://github.com/riscv-software-src/opensbi + diff --git a/Tutorial/phase-17-linux/phase-17.md b/Tutorial/phase-17-linux/phase-17.md new file mode 100644 index 0000000..34656e4 --- /dev/null +++ b/Tutorial/phase-17-linux/phase-17.md @@ -0,0 +1,50 @@ +# Phase 17 - Linux + +## Context + +This is the summit: boot a minimal Linux kernel with a BusyBox initramfs to a shell over +UART on the CPU and SoC you built. + +## Goals + +- Build a kernel matching the implemented ISA and platform. +- Provide DTB and initramfs. +- Reach an interactive shell or a controlled init process. + +## New Concepts + +- Kernel config: set of build-time options selecting architecture and drivers. +- Initramfs: initial root filesystem bundled or loaded with the kernel. +- BusyBox: compact Unix userland used in embedded systems. +- Early console: minimal logging path before normal console drivers initialize. +- Root filesystem: filesystem mounted as `/` by Linux. + +## How To Think About It + +Linux bring-up is system debugging under poor visibility. Work from known-good layers: +CPU tests, DRAM test, boot firmware, DTB validation, early console, then userspace. + +## Learning Tasks + +- Understand the kernel image format and load addresses for RV32. +- Build the smallest kernel config that matches your hardware. +- Trace boot log milestones and map them to hardware dependencies. + +## Pitfalls + +- Debugging Linux before compliance tests and DRAM tests are clean. +- Enabling drivers for devices you do not actually implement. +- Losing console output because UART binding or clock frequency is wrong. + +## Tooling And Testing + +- Use earlycon/earlyprintk-style mechanisms where applicable. +- Keep kernel, DTB, firmware, and bitstream versions tied together. +- Save full boot logs and compare against previous attempts. + +## References + +- Linux RISC-V documentation: https://docs.kernel.org/arch/riscv/ +- Buildroot: https://buildroot.org/ +- BusyBox: https://busybox.net/ +