Add BIOS and tiny kernel roadmap phases
This commit is contained in:
@@ -29,8 +29,8 @@ See ROADMAP.md for the full phased plan.
|
|||||||
- Firmware source: `fw/` directory. Built with `riscv64-unknown-elf-gcc`
|
- Firmware source: `fw/` directory. Built with `riscv64-unknown-elf-gcc`
|
||||||
(multilib build required). March string evolves with the implemented ISA:
|
(multilib build required). March string evolves with the implemented ISA:
|
||||||
- Phase 8 (pre-CSR bare-metal): `-march=rv32im -mabi=ilp32`
|
- Phase 8 (pre-CSR bare-metal): `-march=rv32im -mabi=ilp32`
|
||||||
- Phase 9+ (CSRs/Zifencei decoded): `-march=rv32im_zicsr_zifencei -mabi=ilp32`
|
- Phase 12+ (CSRs/Zifencei decoded): `-march=rv32im_zicsr_zifencei -mabi=ilp32`
|
||||||
- Phase 12+ (atomics): `-march=rv32ima_zicsr_zifencei -mabi=ilp32`
|
- Phase 15+ (atomics): `-march=rv32ima_zicsr_zifencei -mabi=ilp32`
|
||||||
Don't advertise an extension before its ops are decoded — the compiler is
|
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.
|
free to emit them, and "trap as illegal" early is much better than NOPing.
|
||||||
|
|
||||||
@@ -55,14 +55,14 @@ End target: **RV32IMA + Zicsr + Zifencei + M/S/U privilege + Sv32** (Linux-capab
|
|||||||
|
|
||||||
Extensions land incrementally:
|
Extensions land incrementally:
|
||||||
- RV32IM base — Phases 1-8
|
- RV32IM base — Phases 1-8
|
||||||
- Zicsr (CSR instructions) + M-mode trap handling — Phase 9
|
- Zicsr (CSR instructions) + M-mode trap handling — Phase 12
|
||||||
- Zifencei (`fence.i`) — decoded but NOP until caches exist (Phase 14+)
|
- Zifencei (`fence.i`) — decoded but NOP until caches exist (Phase 17+)
|
||||||
- A (atomics: LR/SC + AMO) — Phase 12 (required for mainline Linux)
|
- A (atomics: LR/SC + AMO) — Phase 15 (required for mainline Linux)
|
||||||
- S-mode + U-mode + Sv32 — Phase 15
|
- S-mode + U-mode + Sv32 — Phase 18
|
||||||
|
|
||||||
## Key Design Decisions
|
## Key Design Decisions
|
||||||
|
|
||||||
- Single-cycle first, pipeline later (Phase 13, optional). Stages are separated
|
- Single-cycle first, pipeline later (Phase 16, optional). Stages are separated
|
||||||
in the code even without pipeline registers between them.
|
in the code even without pipeline registers between them.
|
||||||
- "Single-cycle" is a logical model, not a literal one cycle per instruction.
|
- "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
|
BRAM has a 1-cycle read latency, so fetch is registered and loads take 2
|
||||||
@@ -82,10 +82,10 @@ Extensions land incrementally:
|
|||||||
```
|
```
|
||||||
0x0000_0000 – 0x0FFF_FFFF reserved boot aperture (256 MB decode region)
|
0x0000_0000 – 0x0FFF_FFFF reserved boot aperture (256 MB decode region)
|
||||||
actual SPI flash device is 16 MB (Arty A7),
|
actual SPI flash device is 16 MB (Arty A7),
|
||||||
mapped at 0x0000_0000–0x00FF_FFFF in Phase 14
|
mapped at 0x0000_0000–0x00FF_FFFF in Phase 17
|
||||||
0x1000_0000 – 0x1000_0FFF MMIO (UART; later: timer, PLIC)
|
0x1000_0000 – 0x1000_0FFF MMIO (UART; later: timer, PLIC)
|
||||||
0x2000_0000 – 0x2000_FFFF instruction BRAM (64 KB)
|
0x2000_0000 – 0x2000_FFFF instruction BRAM (64 KB)
|
||||||
0x8000_0000 – 0x8000_FFFF data BRAM (64 KB) → grows to DRAM 0x8000_0000–0x8FFF_FFFF in Phase 14
|
0x8000_0000 – 0x8000_FFFF data BRAM (64 KB) → grows to DRAM 0x8000_0000–0x8FFF_FFFF in Phase 17
|
||||||
```
|
```
|
||||||
|
|
||||||
Reset PC = `0x2000_0000`. Linker script anchors text/rodata at `0x2000_0000`
|
Reset PC = `0x2000_0000`. Linker script anchors text/rodata at `0x2000_0000`
|
||||||
@@ -99,7 +99,7 @@ UART register layout (split, not 16550-style):
|
|||||||
```
|
```
|
||||||
|
|
||||||
This is fine for bare-metal firmware but is *not* directly Linux-compatible:
|
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
|
the in-tree `8250/16550` driver expects a different register layout. Phase 19
|
||||||
(Linux Boot Contract) revisits this — either add a 16550-compatible wrapper
|
(Linux Boot Contract) revisits this — either add a 16550-compatible wrapper
|
||||||
or write a custom Linux serial driver + device-tree binding.
|
or write a custom Linux serial driver + device-tree binding.
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ typedef struct packed {
|
|||||||
logic [1:0] size; // 00=byte, 01=halfword, 10=word
|
logic [1:0] size; // 00=byte, 01=halfword, 10=word
|
||||||
logic [31:0] wdata; // lane-aligned per wstrb
|
logic [31:0] wdata; // lane-aligned per wstrb
|
||||||
logic [3:0] wstrb; // byte enables (writes only)
|
logic [3:0] wstrb; // byte enables (writes only)
|
||||||
logic [3:0] amo; // AMO op encoding (D-bus only, Phase 12+)
|
logic [3:0] amo; // AMO op encoding (D-bus only, Phase 15+)
|
||||||
} mem_req_t;
|
} mem_req_t;
|
||||||
|
|
||||||
// Payload — slave → master
|
// Payload — slave → master
|
||||||
@@ -153,7 +153,7 @@ Handshake notes:
|
|||||||
producing a response (BRAM = 1 cycle, DRAM = many).
|
producing a response (BRAM = 1 cycle, DRAM = many).
|
||||||
- `wstrb` is mandatory from Phase 6.2 onward (sub-word stores). For
|
- `wstrb` is mandatory from Phase 6.2 onward (sub-word stores). For
|
||||||
Phase 6.1 (word-only), tie strobes to `4'b1111` on stores.
|
Phase 6.1 (word-only), tie strobes to `4'b1111` on stores.
|
||||||
- `amo` is unused (drive `4'h0`) until Phase 12; on the I-bus it is always
|
- `amo` is unused (drive `4'h0`) until Phase 15; on the I-bus it is always
|
||||||
unused.
|
unused.
|
||||||
- `err` is **only** raised by a slave to report access faults it owns:
|
- `err` is **only** raised by a slave to report access faults it owns:
|
||||||
unmapped address, peripheral-access violation, eventually DRAM ECC fault.
|
unmapped address, peripheral-access violation, eventually DRAM ECC fault.
|
||||||
@@ -173,8 +173,8 @@ separately. Per-phase progression:
|
|||||||
| 4 | CPU fetch ↔ instruction BRAM | (none yet) |
|
| 4 | CPU fetch ↔ instruction BRAM | (none yet) |
|
||||||
| 6 | unchanged | LSU ↔ data BRAM (single slave) |
|
| 6 | unchanged | LSU ↔ data BRAM (single slave) |
|
||||||
| 7 | unchanged | LSU ↔ decoder → {data BRAM, UART MMIO} |
|
| 7 | unchanged | LSU ↔ decoder → {data BRAM, UART MMIO} |
|
||||||
| 9-12 | unchanged | + timer (Phase 10), PLIC (Phase 11) |
|
| 12-15 | unchanged | + timer (Phase 13), PLIC (Phase 14) |
|
||||||
| 14 | I-bus → arbiter → DRAM | D-bus → arbiter → DRAM (or MIG dual-port) |
|
| 17 | I-bus → arbiter → DRAM | D-bus → arbiter → DRAM (or MIG dual-port) |
|
||||||
|
|
||||||
Both buses can fault independently; classification happens in the masters
|
Both buses can fault independently; classification happens in the masters
|
||||||
(see below).
|
(see below).
|
||||||
@@ -195,17 +195,17 @@ transaction adds context to produce the precise architectural cause:
|
|||||||
| Store/AMO address misaligned | 6 | LSU (size + addr LSBs, pre-issue) |
|
| Store/AMO address misaligned | 6 | LSU (size + addr LSBs, pre-issue) |
|
||||||
| Store/AMO access fault | 7 | LSU (D-bus `rsp.err` on write) |
|
| Store/AMO access fault | 7 | LSU (D-bus `rsp.err` on write) |
|
||||||
| Ecall from M-mode | 11 | decoder |
|
| Ecall from M-mode | 11 | decoder |
|
||||||
| Instruction page fault | 12 | I-side MMU (Phase 15) |
|
| Instruction page fault | 12 | I-side MMU (Phase 18) |
|
||||||
| Load page fault | 13 | D-side MMU (Phase 15) |
|
| Load page fault | 13 | D-side MMU (Phase 18) |
|
||||||
| Store/AMO page fault | 15 | D-side MMU (Phase 15) |
|
| Store/AMO page fault | 15 | D-side MMU (Phase 18) |
|
||||||
|
|
||||||
Two consequences:
|
Two consequences:
|
||||||
- The LSU detects misalignment **before** issuing a bus request — never
|
- The LSU detects misalignment **before** issuing a bus request — never
|
||||||
send a request you already know will trap.
|
send a request you already know will trap.
|
||||||
- Page faults (Phase 15) are raised by the MMU during translation,
|
- Page faults (Phase 18) are raised by the MMU during translation,
|
||||||
before the (translated) request hits the bus.
|
before the (translated) request hits the bus.
|
||||||
|
|
||||||
### Atomics (Phase 12)
|
### Atomics (Phase 15)
|
||||||
|
|
||||||
LR/SC and AMO use the `amo` field of `mem_req_t` (D-bus only) rather than
|
LR/SC and AMO use the `amo` field of `mem_req_t` (D-bus only) rather than
|
||||||
a separate bus. Single-hart reservation tracking lives in the LSU.
|
a separate bus. Single-hart reservation tracking lives in the LSU.
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ grows to RV32IMA + Sv32 + M/S/U privilege en route to booting Linux.
|
|||||||
unit (DSP-based multiply, iterative divide) sit side by side; the datapath
|
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
|
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
|
`rv32_pkg.sv` — the same structs become pipeline registers when the core is
|
||||||
pipelined later (Phase 13, optional).
|
pipelined later (Phase 16, optional).
|
||||||
|
|
||||||
## Memory Map
|
## Memory Map
|
||||||
|
|
||||||
| Address Range | Region |
|
| Address Range | Region |
|
||||||
|---------------------------|----------------------------------------------|
|
|---------------------------|----------------------------------------------|
|
||||||
| `0x0000_0000–0x0FFF_FFFF` | reserved boot aperture (Phase 14: 16 MB SPI flash at low end) |
|
| `0x0000_0000–0x0FFF_FFFF` | reserved boot aperture (Phase 17: 16 MB SPI flash at low end) |
|
||||||
| `0x1000_0000–0x1000_0FFF` | MMIO (UART; later: timer, PLIC) |
|
| `0x1000_0000–0x1000_0FFF` | MMIO (UART; later: timer, PLIC) |
|
||||||
| `0x2000_0000–0x2000_FFFF` | instruction BRAM (64 KB) |
|
| `0x2000_0000–0x2000_FFFF` | instruction BRAM (64 KB) |
|
||||||
| `0x8000_0000–0x8000_FFFF` | data BRAM (64 KB) → DRAM in Phase 14 |
|
| `0x8000_0000–0x8000_FFFF` | data BRAM (64 KB) → DRAM in Phase 17 |
|
||||||
|
|
||||||
Reset PC = `0x2000_0000`.
|
Reset PC = `0x2000_0000`.
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ Requires:
|
|||||||
- Vivado 2025.2 or later
|
- Vivado 2025.2 or later
|
||||||
- RISC-V GCC toolchain — multilib `riscv64-unknown-elf-gcc`. March string
|
- RISC-V GCC toolchain — multilib `riscv64-unknown-elf-gcc`. March string
|
||||||
evolves with the implementation: `rv32im` (Phase 8) →
|
evolves with the implementation: `rv32im` (Phase 8) →
|
||||||
`rv32im_zicsr_zifencei` (Phase 9) → `rv32ima_zicsr_zifencei` (Phase 12).
|
`rv32im_zicsr_zifencei` (Phase 12) → `rv32ima_zicsr_zifencei` (Phase 15).
|
||||||
- `riscv-tests` (for compliance verification from Phase 8.5)
|
- `riscv-tests` (for compliance verification from Phase 8.5)
|
||||||
- Serial terminal (minicom/picocom/PuTTY) at 115200 8N1
|
- Serial terminal (minicom/picocom/PuTTY) at 115200 8N1
|
||||||
|
|
||||||
|
|||||||
+148
-33
@@ -21,17 +21,22 @@ Recommended casual target: reach Phase 8, where GCC-built C runs on your CPU and
|
|||||||
`riscv-tests` gives you confidence in the ISA implementation. Everything after
|
`riscv-tests` gives you confidence in the ISA implementation. Everything after
|
||||||
that is enrichment or a long-term expedition.
|
that is enrichment or a long-term expedition.
|
||||||
|
|
||||||
|
Recommended middle-game target: continue through Phases 9-11 and build your own
|
||||||
|
BIOS, ELF loader, and tiny kernel before attempting Linux. This gives you a
|
||||||
|
playable computer with your own firmware and command line without requiring the
|
||||||
|
full Linux boot contract.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Memory Map (target — fixed in Phase 0, evolves through Phase 14)
|
## Memory Map (target — fixed in Phase 0, evolves through Phase 17)
|
||||||
|
|
||||||
```
|
```
|
||||||
0x0000_0000 – 0x0FFF_FFFF reserved boot aperture (256 MB decode region;
|
0x0000_0000 – 0x0FFF_FFFF reserved boot aperture (256 MB decode region;
|
||||||
actual SPI flash is 16 MB on Arty A7,
|
actual SPI flash is 16 MB on Arty A7,
|
||||||
mapped at 0x0000_0000–0x00FF_FFFF in Phase 14)
|
mapped at 0x0000_0000–0x00FF_FFFF in Phase 17)
|
||||||
0x1000_0000 – 0x1000_0FFF MMIO (UART; later: timer, PLIC)
|
0x1000_0000 – 0x1000_0FFF MMIO (UART; later: timer, PLIC)
|
||||||
0x2000_0000 – 0x2000_FFFF instruction BRAM (64 KB)
|
0x2000_0000 – 0x2000_FFFF instruction BRAM (64 KB)
|
||||||
0x8000_0000 – 0x8000_FFFF data BRAM (64 KB) → DRAM 0x8000_0000–0x8FFF_FFFF in Phase 14
|
0x8000_0000 – 0x8000_FFFF data BRAM (64 KB) → DRAM 0x8000_0000–0x8FFF_FFFF in Phase 17
|
||||||
```
|
```
|
||||||
|
|
||||||
Reset PC = `0x2000_0000`. Locking this in now keeps the linker script and crt0
|
Reset PC = `0x2000_0000`. Locking this in now keeps the linker script and crt0
|
||||||
@@ -165,7 +170,7 @@ extension (bit 31 of the instruction is always the sign bit in RISC-V — that's
|
|||||||
deliberate design choice). Verify that the decoder handles all R-type, I-type, S-type,
|
deliberate design choice). Verify that the decoder handles all R-type, I-type, S-type,
|
||||||
B-type, U-type, and J-type correctly.
|
B-type, U-type, and J-type correctly.
|
||||||
|
|
||||||
Future role: When you add CSR instructions (Phase 9), you'll add a new case to the
|
Future role: When you add CSR instructions (Phase 12), you'll add a new case to the
|
||||||
decoder and a new field to the struct. The structure of the decoder doesn't change.
|
decoder and a new field to the struct. The structure of the decoder doesn't change.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -260,8 +265,8 @@ 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).
|
For now, only 32-bit aligned access (lw and sw).
|
||||||
|
|
||||||
Define the full memory bus contract here, not just the subset Phase 6.1
|
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
|
uses. Defining it once means Phase 6.2 (sub-word), Phase 7 (MMIO), Phase 12
|
||||||
(access faults), Phase 12 (atomics), and Phase 14 (DRAM) all plug into the
|
(access faults), Phase 15 (atomics), and Phase 17 (DRAM) all plug into the
|
||||||
same interface without rework. The contract (see `CLAUDE.md` for the full
|
same interface without rework. The contract (see `CLAUDE.md` for the full
|
||||||
spec) is two payload structs (`mem_req_t`, `mem_rsp_t`) plus loose
|
spec) is two payload structs (`mem_req_t`, `mem_rsp_t`) plus loose
|
||||||
valid/ready signals on each channel:
|
valid/ready signals on each channel:
|
||||||
@@ -442,8 +447,8 @@ program (or crt0) emits a CSR or `fence.i` op before then, the **decoder**
|
|||||||
asserts `illegal_instr` and the Phase 8.3 halt latches the offending PC and
|
asserts `illegal_instr` and the Phase 8.3 halt latches the offending PC and
|
||||||
instruction word — catching the bug visibly rather than NOPing through it.
|
instruction word — catching the bug visibly rather than NOPing through it.
|
||||||
(Illegal instructions are a decode event, not a bus event; the memory bus
|
(Illegal instructions are a decode event, not a bus event; the memory bus
|
||||||
is not involved.) Switch to `rv32im_zicsr_zifencei` in Phase 9 once CSRs
|
is not involved.) Switch to `rv32im_zicsr_zifencei` in Phase 12 once CSRs
|
||||||
land, and `rv32ima_zicsr_zifencei` in Phase 12 once atomics land.
|
land, and `rv32ima_zicsr_zifencei` in Phase 15 once atomics land.
|
||||||
|
|
||||||
Note on GCC 11+: newer toolchains require `_zicsr` / `_zifencei` in the
|
Note on GCC 11+: newer toolchains require `_zicsr` / `_zifencei` in the
|
||||||
march string only when source code actually uses CSR or `fence.i`
|
march string only when source code actually uses CSR or `fence.i`
|
||||||
@@ -469,7 +474,7 @@ testbench-visible `illegal_instr` signal** with the offending PC and
|
|||||||
instruction word latched. Do NOT decode them as NOPs — GCC emits `ebreak`
|
instruction word latched. Do NOT decode them as NOPs — GCC emits `ebreak`
|
||||||
in `__builtin_trap`, abort paths, and some divide-by-zero configurations,
|
in `__builtin_trap`, abort paths, and some divide-by-zero configurations,
|
||||||
and silently NOPing past those destroys debuggability. The halt becomes a
|
and silently NOPing past those destroys debuggability. The halt becomes a
|
||||||
real exception in Phase 9 (`mcause = 2` illegal, `mcause = 3` ebreak,
|
real exception in Phase 12 (`mcause = 2` illegal, `mcause = 3` ebreak,
|
||||||
`mcause = 11` ecall-from-M).
|
`mcause = 11` ecall-from-M).
|
||||||
|
|
||||||
Why: GCC's output will exercise the full ISA. You need complete RV32I
|
Why: GCC's output will exercise the full ISA. You need complete RV32I
|
||||||
@@ -480,10 +485,11 @@ Test: Compile progressively more complex C programs. String manipulation, struct
|
|||||||
usage, switch statements (these generate jump tables — exercises jalr with computed
|
usage, switch statements (these generate jump tables — exercises jalr with computed
|
||||||
addresses), recursive functions.
|
addresses), recursive functions.
|
||||||
|
|
||||||
### 8.4 — Milestone: Meaningful C Program
|
### 8.4 — Milestone: Meaningful Standalone C Program
|
||||||
What: Write something real — a serial monitor that accepts commands over UART
|
What: Write something real but still self-contained — a checksum demo, tiny
|
||||||
and responds. Or a tiny Forth interpreter. Something interactive that proves
|
benchmark, string/array exercise, or simple UART-driven calculator. Keep the
|
||||||
the core is solid.
|
full serial monitor for Phase 9, where it becomes the BIOS instead of a one-off
|
||||||
|
test program.
|
||||||
|
|
||||||
Why: Confidence builder. You now have a working RISC-V computer that runs
|
Why: Confidence builder. You now have a working RISC-V computer that runs
|
||||||
compiled C and talks over serial. Everything after this is enrichment.
|
compiled C and talks over serial. Everything after this is enrichment.
|
||||||
@@ -499,7 +505,7 @@ BRAM, runs the core until the test signals completion, and reports pass/fail.
|
|||||||
Why: Hand-written testbenches catch the bugs you thought to look for. The
|
Why: Hand-written testbenches catch the bugs you thought to look for. The
|
||||||
official suite catches the ones you didn't — corner cases in shifts, sign
|
official suite catches the ones you didn't — corner cases in shifts, sign
|
||||||
extension on byte loads, immediate decoding for every format, M-extension
|
extension on byte loads, immediate decoding for every format, M-extension
|
||||||
overflow cases. Doing this *before* Phase 9 means you're building trap
|
overflow cases. Doing this *before* Phase 12 means you're building trap
|
||||||
handling on a known-good ISA implementation, not stacking unknowns.
|
handling on a known-good ISA implementation, not stacking unknowns.
|
||||||
|
|
||||||
Expect to find bugs. That's the point. Fix each one, re-run the suite, move
|
Expect to find bugs. That's the point. Fix each one, re-run the suite, move
|
||||||
@@ -510,7 +516,116 @@ becomes regression coverage for the rest of the project.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 9 — CSRs + M-Mode Trap Handling [Hard stretch]
|
## Phase 9 — GCC-Built BIOS / Serial Monitor [Easy/fun]
|
||||||
|
|
||||||
|
What: Build a small BIOS in freestanding C/assembly with its own linker script,
|
||||||
|
startup code, UART driver, and command loop. It runs from the existing BRAM
|
||||||
|
image and gives you an interactive prompt over serial.
|
||||||
|
|
||||||
|
Suggested commands:
|
||||||
|
|
||||||
|
- `help` — list commands
|
||||||
|
- `peek <addr>` / `poke <addr> <value>` — inspect and edit memory-mapped state
|
||||||
|
- `dump <addr> <len>` — hex-dump memory
|
||||||
|
- `fill <addr> <len> <value>` — initialize memory
|
||||||
|
- `regs` — print a software-maintained register/trap snapshot when available
|
||||||
|
- `memtest` — run a small RAM/BRAM test
|
||||||
|
- `load` — receive a raw binary or hex stream over UART
|
||||||
|
- `run <addr>` — jump to a loaded program
|
||||||
|
- `reboot` — return to reset or spin until manual reset
|
||||||
|
|
||||||
|
Important hardware contract: a UART-loaded program needs somewhere executable
|
||||||
|
to live. In the early Harvard design, instruction BRAM is fetch-only unless you
|
||||||
|
deliberately expose a writable path. Pick one simple learning-friendly option:
|
||||||
|
|
||||||
|
- make instruction BRAM dual-port, with the CPU fetch path on one port and a
|
||||||
|
D-bus write/debug port on the other;
|
||||||
|
- add a small "program RAM" window that is writable through the D-bus and
|
||||||
|
fetchable through the I-bus;
|
||||||
|
- or defer `run`/ELF execution until you add one of those executable write paths.
|
||||||
|
|
||||||
|
Why: This is the first point where the board feels like your own computer. It
|
||||||
|
also becomes a practical debug tool for later hardware and firmware work.
|
||||||
|
|
||||||
|
Test: Boot to a prompt, use `poke`/`peek` on UART registers and RAM, load a tiny
|
||||||
|
raw program, jump to it, and have it return or print a message.
|
||||||
|
|
||||||
|
Future role: The BIOS can remain as a recovery/debug monitor even after flash,
|
||||||
|
DRAM, and Linux enter the picture.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 10 — Minimal ELF Loader [Easy/fun]
|
||||||
|
|
||||||
|
What: Teach the BIOS to receive and run the simplest possible RISC-V ELF32
|
||||||
|
binaries. Support statically linked, non-relocatable, little-endian
|
||||||
|
`EM_RISCV` executables with `PT_LOAD` segments only. Copy each loadable segment
|
||||||
|
to its physical address, zero the `memsz - filesz` tail for BSS, set a known
|
||||||
|
stack pointer, and jump to `e_entry`.
|
||||||
|
|
||||||
|
Deliberate limits:
|
||||||
|
- no dynamic linking
|
||||||
|
- no relocations
|
||||||
|
- no virtual memory
|
||||||
|
- no privilege separation
|
||||||
|
- no demand paging
|
||||||
|
- no filesystem
|
||||||
|
|
||||||
|
ABI for tiny programs: start with a tiny fixed contract. For example, `a0`
|
||||||
|
receives a pointer to a BIOS call table, `sp` points at the top of data RAM,
|
||||||
|
and returning from `main` jumps back to the monitor with an integer status.
|
||||||
|
Before Phase 12 traps exist, programs are trusted firmware payloads, not isolated
|
||||||
|
user processes.
|
||||||
|
|
||||||
|
Why: ELF loading is a major confidence milestone. You are no longer baking every
|
||||||
|
program into the FPGA bitstream; you can compile a new binary, send it over
|
||||||
|
serial, and run it on your CPU.
|
||||||
|
|
||||||
|
Test: Compile several tiny programs with fixed link addresses: hello world,
|
||||||
|
integer arithmetic, memory copy, and a program that returns a status code to
|
||||||
|
the BIOS. Confirm the BIOS rejects malformed or unsupported ELF files loudly.
|
||||||
|
|
||||||
|
Future role: This loader becomes the conceptual ancestor of the later bootloader
|
||||||
|
that copies kernels from flash to DRAM.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 11 — Tiny Kernel + Command Shell [Easy/fun]
|
||||||
|
|
||||||
|
What: Build a tiny kernel as a separate GCC-built ELF loaded by the BIOS.
|
||||||
|
Before Phase 12, "kernel" means bare-machine firmware with a command shell, not
|
||||||
|
an isolated privileged OS. It owns a simple console, command parser, memory
|
||||||
|
allocator, and a table of services. Keep it intentionally small: no MMU, no
|
||||||
|
userspace isolation, no real filesystem.
|
||||||
|
|
||||||
|
Suggested kernel features:
|
||||||
|
|
||||||
|
- banner and prompt
|
||||||
|
- `help`, `uptime`, `mem`, `dump`, `loadelf`, `run`, `reboot`
|
||||||
|
- direct UART console driver or BIOS-backed console calls
|
||||||
|
- bump allocator for kernel data structures
|
||||||
|
- simple program table showing loaded ELF images
|
||||||
|
- trusted program launch with an agreed calling convention
|
||||||
|
- status return from launched programs back to the shell
|
||||||
|
|
||||||
|
Simple ELF binaries: compile tiny freestanding programs that use the kernel or
|
||||||
|
BIOS service table for `putchar`, `getchar`, and exit. The first binaries can be
|
||||||
|
as small as "print a line", "sum an array", and "echo typed characters". The
|
||||||
|
goal is not POSIX; the goal is a complete end-to-end loop: compile on your host,
|
||||||
|
send over UART, load as ELF, run on your CPU, return to your shell.
|
||||||
|
|
||||||
|
Why: This is the rewarding middle game between "bare-metal C works" and "Linux
|
||||||
|
boot hangs somewhere in early init." You get OS-shaped learning while the system
|
||||||
|
is still small enough to understand in one sitting.
|
||||||
|
|
||||||
|
Future role: Phase 12 turns `ecall`, exceptions, and trap vectors into real
|
||||||
|
architectural mechanisms. At that point, the ad hoc service table can evolve into
|
||||||
|
a syscall ABI and the kernel can start handling faults instead of trusting every
|
||||||
|
program.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 12 — CSRs + M-Mode Trap Handling [Hard stretch]
|
||||||
|
|
||||||
What: Add Control and Status Registers (mstatus, mtvec, mepc, mcause, mtval, mie,
|
What: Add Control and Status Registers (mstatus, mtvec, mepc, mcause, mtval, mie,
|
||||||
mip) and the CSR instructions (csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci). Add
|
mip) and the CSR instructions (csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci). Add
|
||||||
@@ -527,18 +642,18 @@ plus S-mode (supervisor mode), which you'll add later.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 10 — Timer [Hard stretch]
|
## Phase 13 — Timer [Hard stretch]
|
||||||
|
|
||||||
What: Implement mtime (a free-running 64-bit counter) and mtimecmp (comparison
|
What: Implement mtime (a free-running 64-bit counter) and mtimecmp (comparison
|
||||||
register). When mtime >= mtimecmp, a timer interrupt fires.
|
register). When mtime >= mtimecmp, a timer interrupt fires.
|
||||||
|
|
||||||
Why: Every operating system needs a timer tick for scheduling. Even bare-metal
|
Why: Every operating system needs a timer tick for scheduling. Even bare-metal
|
||||||
firmware needs delays and timeouts. This is your first interrupt source, which also
|
firmware needs delays and timeouts. This is your first interrupt source, which also
|
||||||
validates that the trap handling from Phase 9 actually works end-to-end.
|
validates that the trap handling from Phase 12 actually works end-to-end.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 11 — Interrupt Controller [Hard stretch]
|
## Phase 14 — Interrupt Controller [Hard stretch]
|
||||||
|
|
||||||
What: Build a minimal PLIC (Platform-Level Interrupt Controller) or a simplified
|
What: Build a minimal PLIC (Platform-Level Interrupt Controller) or a simplified
|
||||||
version. Connect UART RX as an interrupt source. Implement interrupt priority and
|
version. Connect UART RX as an interrupt source. Implement interrupt priority and
|
||||||
@@ -550,7 +665,7 @@ the PLIC manages them. This is required for Linux.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 12 — A Extension (Atomics) [Hard stretch]
|
## Phase 15 — A Extension (Atomics) [Hard stretch]
|
||||||
|
|
||||||
What: Implement the RV32A atomic instructions: `lr.w` / `sc.w` (load-reserved /
|
What: Implement the RV32A atomic instructions: `lr.w` / `sc.w` (load-reserved /
|
||||||
store-conditional) and the AMO ops (`amoswap.w`, `amoadd.w`, `amoand.w`,
|
store-conditional) and the AMO ops (`amoswap.w`, `amoadd.w`, `amoand.w`,
|
||||||
@@ -578,7 +693,7 @@ reservation set and bus become more involved.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 13 — Pipeline (Optional but educational) [Hard stretch]
|
## Phase 16 — Pipeline (Optional but educational) [Hard stretch]
|
||||||
|
|
||||||
What: Insert pipeline registers between your stages (fetch|decode|execute|
|
What: Insert pipeline registers between your stages (fetch|decode|execute|
|
||||||
memory|writeback). Handle data hazards (forwarding/stalling) and control
|
memory|writeback). Handle data hazards (forwarding/stalling) and control
|
||||||
@@ -592,11 +707,11 @@ and deeply educational.
|
|||||||
Why optional: A non-pipelined core can run at 50 MHz on the Artix-7. That's
|
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
|
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
|
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.
|
`riscv-tests` (Phase 8.5 + 15) after pipelining catches regressions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 14 — SPI Flash Boot + DRAM [Overkill/hard]
|
## Phase 17 — SPI Flash Boot + DRAM [Overkill/hard]
|
||||||
|
|
||||||
What: Add an SPI flash controller to boot from the on-board flash (instead
|
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
|
of BRAM initialization). The Arty A7-100T has a 16 MB Quad-SPI flash; map
|
||||||
@@ -619,7 +734,7 @@ keep.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 15 — S-Mode, U-Mode, Sv32 Virtual Memory [Overkill/hard]
|
## Phase 18 — S-Mode, U-Mode, Sv32 Virtual Memory [Overkill/hard]
|
||||||
|
|
||||||
What: Add supervisor and user privilege modes. Implement Sv32 page table
|
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
|
walking (two-level page tables, 4 KB pages). Add the `satp` CSR, page-fault
|
||||||
@@ -637,7 +752,7 @@ modes round-trip via `mret` / `sret`.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 16 — Linux Boot Contract (SBI / Device Tree / ABI) [Overkill/hard]
|
## Phase 19 — Linux Boot Contract (SBI / Device Tree / ABI) [Overkill/hard]
|
||||||
|
|
||||||
What: Lock down everything Linux expects at the moment of `kernel_entry`.
|
What: Lock down everything Linux expects at the moment of `kernel_entry`.
|
||||||
This is a real subproject, not a footnote inside "port Linux".
|
This is a real subproject, not a footnote inside "port Linux".
|
||||||
@@ -676,16 +791,16 @@ Required deliverables:
|
|||||||
surface area.
|
surface area.
|
||||||
|
|
||||||
Why: Skipping this phase means hitting all of these problems simultaneously
|
Why: Skipping this phase means hitting all of these problems simultaneously
|
||||||
during Phase 17 bring-up, where the failure mode is "kernel hangs silently
|
during Phase 20 bring-up, where the failure mode is "kernel hangs silently
|
||||||
in early boot" with no console. Doing it explicitly turns Phase 17 into a
|
in early boot" with no console. Doing it explicitly turns Phase 20 into a
|
||||||
debugging exercise on a known-good ABI surface.
|
debugging exercise on a known-good ABI surface.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 17 — Linux [Overkill/hard]
|
## Phase 20 — Linux [Overkill/hard]
|
||||||
|
|
||||||
What: Build a minimal RISC-V Linux kernel against the device tree and
|
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
|
boot path defined in Phase 19. Build an initramfs with BusyBox. Load
|
||||||
kernel + DTB + initramfs to flash. Boot to a shell prompt over UART.
|
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
|
Why: This is the summit. A Linux shell running on a CPU you built from
|
||||||
@@ -697,12 +812,12 @@ scratch.
|
|||||||
|
|
||||||
- Vivado 2025.2 or later (synthesis, simulation, ILA, VIO)
|
- Vivado 2025.2 or later (synthesis, simulation, ILA, VIO)
|
||||||
- RISC-V GCC toolchain (`riscv64-unknown-elf-gcc`, multilib build). March
|
- RISC-V GCC toolchain (`riscv64-unknown-elf-gcc`, multilib build). March
|
||||||
string evolves: `rv32im` (Phase 8) → `rv32im_zicsr_zifencei` (Phase 9) →
|
string evolves: `rv32im` (Phase 8) → `rv32im_zicsr_zifencei` (Phase 12) →
|
||||||
`rv32ima_zicsr_zifencei` (Phase 12).
|
`rv32ima_zicsr_zifencei` (Phase 15).
|
||||||
- `riscv-tests` repo cloned and buildable (Phase 8.5 onward; rv32ua added at
|
- `riscv-tests` repo cloned and buildable (Phase 8.5 onward; rv32ua added at
|
||||||
Phase 12)
|
Phase 15)
|
||||||
- Device-tree compiler `dtc` (Phase 16+)
|
- Device-tree compiler `dtc` (Phase 19+)
|
||||||
- OpenSBI source (Phase 16+, only if you choose the SBI boot path)
|
- OpenSBI source (Phase 19+, only if you choose the SBI boot path)
|
||||||
- Terminal program (minicom, picocom, or PuTTY) for UART, 115200 8N1
|
- Terminal program (minicom, picocom, or PuTTY) for UART, 115200 8N1
|
||||||
- Text editor you like for SystemVerilog
|
- Text editor you like for SystemVerilog
|
||||||
- The RISC-V ISA spec (Volume 1: Unprivileged, Volume 2: Privileged) — free PDFs
|
- The RISC-V ISA spec (Volume 1: Unprivileged, Volume 2: Privileged) — free PDFs
|
||||||
|
|||||||
+12
-10
@@ -27,13 +27,15 @@ is to explain what to learn, what to verify, and what mistakes to avoid.
|
|||||||
- [Phase 6 - Load/Store](phase-06-load-store/phase-06.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 7 - Memory-Mapped UART](phase-07-uart-mmio/phase-07.md)
|
||||||
- [Phase 8 - GCC Toolchain Integration](phase-08-gcc-toolchain/phase-08.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 9 - GCC-Built BIOS / Serial Monitor](phase-09-bios-monitor/phase-09.md)
|
||||||
- [Phase 10 - Timer](phase-10-timer/phase-10.md)
|
- [Phase 10 - Minimal ELF Loader](phase-10-elf-loader/phase-10.md)
|
||||||
- [Phase 11 - Interrupt Controller](phase-11-interrupt-controller/phase-11.md)
|
- [Phase 11 - Tiny Kernel + Command Shell](phase-11-tiny-kernel/phase-11.md)
|
||||||
- [Phase 12 - Atomics](phase-12-atomics/phase-12.md)
|
- [Phase 12 - CSRs + M-Mode Traps](phase-12-csrs-traps/phase-12.md)
|
||||||
- [Phase 13 - Pipeline](phase-13-pipeline/phase-13.md)
|
- [Phase 13 - Timer](phase-13-timer/phase-13.md)
|
||||||
- [Phase 14 - SPI Flash + DRAM](phase-14-flash-dram/phase-14.md)
|
- [Phase 14 - Interrupt Controller](phase-14-interrupt-controller/phase-14.md)
|
||||||
- [Phase 15 - M/S/U + Sv32](phase-15-privilege-sv32/phase-15.md)
|
- [Phase 15 - Atomics](phase-15-atomics/phase-15.md)
|
||||||
- [Phase 16 - Linux Boot Contract](phase-16-linux-boot-contract/phase-16.md)
|
- [Phase 16 - Pipeline](phase-16-pipeline/phase-16.md)
|
||||||
- [Phase 17 - Linux](phase-17-linux/phase-17.md)
|
- [Phase 17 - SPI Flash + DRAM](phase-17-flash-dram/phase-17.md)
|
||||||
|
- [Phase 18 - M/S/U + Sv32](phase-18-privilege-sv32/phase-18.md)
|
||||||
|
- [Phase 19 - Linux Boot Contract](phase-19-linux-boot-contract/phase-19.md)
|
||||||
|
- [Phase 20 - Linux](phase-20-linux/phase-20.md)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ more than minimizing cycles.
|
|||||||
|
|
||||||
- Write a cycle-by-cycle timeline for a simple `addi` instruction.
|
- Write a cycle-by-cycle timeline for a simple `addi` instruction.
|
||||||
- Write a cycle-by-cycle timeline for a multiply/divide instruction.
|
- Write a cycle-by-cycle timeline for a multiply/divide instruction.
|
||||||
- Confirm the pre-Phase 9 illegal-instruction behavior: halt the core and expose
|
- Confirm the pre-Phase 12 illegal-instruction behavior: halt the core and expose
|
||||||
the offending PC and instruction word to the testbench.
|
the offending PC and instruction word to the testbench.
|
||||||
|
|
||||||
## Pitfalls
|
## Pitfalls
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ every emitted instruction is implemented or fails loudly.
|
|||||||
|
|
||||||
- Compare C source to generated assembly.
|
- Compare C source to generated assembly.
|
||||||
- Identify every load/store used for UART access.
|
- Identify every load/store used for UART access.
|
||||||
- Confirm no CSR or `fence.i` instructions appear before Phase 9.
|
- Confirm no CSR or `fence.i` instructions appear before Phase 12.
|
||||||
|
|
||||||
## Pitfalls
|
## Pitfalls
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Phase 9 - GCC-Built BIOS / Serial Monitor
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This phase turns the GCC bring-up work into a persistent interactive firmware. The
|
||||||
|
CPU boots into a small monitor instead of a one-off test program.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Build a freestanding C/assembly BIOS with its own linker script and startup code.
|
||||||
|
- Provide a UART command prompt.
|
||||||
|
- Add simple commands for memory inspection, loading, and jumping to test payloads.
|
||||||
|
|
||||||
|
## New Concepts
|
||||||
|
|
||||||
|
- Monitor: small firmware that lets you inspect and control a machine interactively.
|
||||||
|
- Command parser: text interface that maps typed commands to firmware functions.
|
||||||
|
- Firmware ABI: the calling convention and data contract between loaded code and BIOS.
|
||||||
|
- Executable RAM window: memory that software can write and the fetch path can execute.
|
||||||
|
|
||||||
|
## How To Think About It
|
||||||
|
|
||||||
|
The BIOS is both a milestone and a tool. Keep it boring and reliable: UART in, UART out,
|
||||||
|
explicit commands, clear error messages, and no hidden dependencies on host tooling.
|
||||||
|
|
||||||
|
## Learning Tasks
|
||||||
|
|
||||||
|
- Decide where UART-loaded code can live and how the I-bus fetches it.
|
||||||
|
- Define a tiny BIOS call table for console I/O and returning to the monitor.
|
||||||
|
- Write down the exact register state expected by `run <addr>`.
|
||||||
|
- Add commands one at a time and test each on hardware.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Loading code into data memory that the instruction fetch path cannot see.
|
||||||
|
- Letting a failed command corrupt the monitor's own stack or globals.
|
||||||
|
- Building a clever shell before the load/run/debug basics work.
|
||||||
|
|
||||||
|
## Tooling And Testing
|
||||||
|
|
||||||
|
- Test the monitor in simulation with scripted UART input where practical.
|
||||||
|
- Use a terminal program that can send raw files without changing line endings.
|
||||||
|
- Keep a known-good tiny payload that prints one line and returns to the monitor.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
- OSDev bare bones background: https://wiki.osdev.org/Bare_Bones
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Phase 10 - Minimal ELF Loader
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This phase lets the BIOS receive GCC-built programs as ELF files and run them without
|
||||||
|
rebuilding the FPGA bitstream.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Parse enough ELF32 to recognize RISC-V executable files.
|
||||||
|
- Load `PT_LOAD` segments into memory and zero BSS.
|
||||||
|
- Jump to `e_entry` with a fixed tiny-program ABI.
|
||||||
|
|
||||||
|
## New Concepts
|
||||||
|
|
||||||
|
- ELF header: file-level metadata describing architecture, entry point, and tables.
|
||||||
|
- Program header: runtime load description used by loaders.
|
||||||
|
- `PT_LOAD`: segment that must be copied into memory before execution.
|
||||||
|
- BSS: zero-initialized memory represented by `memsz > filesz`.
|
||||||
|
|
||||||
|
## How To Think About It
|
||||||
|
|
||||||
|
Do not build a general-purpose dynamic loader. Build a strict loader for the exact
|
||||||
|
firmware shape you produce: static, little-endian, non-relocatable RV32 ELF files with
|
||||||
|
fixed physical addresses.
|
||||||
|
|
||||||
|
## Learning Tasks
|
||||||
|
|
||||||
|
- Read the ELF header fields needed to reject unsupported files.
|
||||||
|
- Map `p_paddr` or `p_vaddr` to your executable memory window.
|
||||||
|
- Define the stack pointer and argument registers at program entry.
|
||||||
|
- Decide how a loaded program returns a status code to the BIOS.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Trusting malformed ELF offsets or sizes.
|
||||||
|
- Forgetting to zero the BSS tail of a loadable segment.
|
||||||
|
- Accidentally accepting relocatable or dynamically linked binaries.
|
||||||
|
- Loading a segment over the BIOS itself.
|
||||||
|
|
||||||
|
## Tooling And Testing
|
||||||
|
|
||||||
|
- Use `readelf -h -l` and `objdump -d` on every early payload.
|
||||||
|
- Start with one fixed link address and one loadable segment.
|
||||||
|
- Add loud rejection messages for unsupported ELF class, endianness, machine, or type.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- ELF specification overview: https://refspecs.linuxfoundation.org/elf/elf.pdf
|
||||||
|
- RISC-V ELF psABI: https://github.com/riscv-non-isa/riscv-elf-psabi-doc
|
||||||
|
- GNU binutils `readelf` documentation: https://sourceware.org/binutils/docs/binutils/readelf.html
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Phase 11 - Tiny Kernel + Command Shell
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This phase builds an OS-shaped firmware payload before the project moves into traps,
|
||||||
|
interrupts, privilege, DRAM, and Linux.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Load a separate GCC-built kernel ELF from the BIOS.
|
||||||
|
- Provide a kernel-owned command shell.
|
||||||
|
- Run tiny trusted ELF programs through a simple service table.
|
||||||
|
|
||||||
|
## New Concepts
|
||||||
|
|
||||||
|
- Kernel: central program that owns machine services. Before traps, this is still
|
||||||
|
trusted bare-machine firmware, not an isolated privileged OS.
|
||||||
|
- Service table: function-pointer ABI for console I/O, exit, and simple utilities.
|
||||||
|
- Bump allocator: simple allocator that hands out memory linearly.
|
||||||
|
- Program table: small kernel data structure tracking loaded payloads.
|
||||||
|
|
||||||
|
## How To Think About It
|
||||||
|
|
||||||
|
The goal is a complete loop, not a Unix clone: compile a tiny program on the host,
|
||||||
|
send it over UART, load it as ELF, run it on the CPU, and return to the shell.
|
||||||
|
|
||||||
|
## Learning Tasks
|
||||||
|
|
||||||
|
- Decide whether kernel console I/O calls the BIOS or drives UART directly.
|
||||||
|
- Define the tiny-program ABI: entry registers, stack, service table, and return path.
|
||||||
|
- Implement the smallest useful allocator.
|
||||||
|
- Add shell commands that expose real machine state, not decorative output.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Pretending trusted payloads are isolated before traps and privilege modes exist.
|
||||||
|
- Growing the kernel into a second large project too early.
|
||||||
|
- Hiding loader or ABI bugs behind ad hoc special cases.
|
||||||
|
|
||||||
|
## Tooling And Testing
|
||||||
|
|
||||||
|
- Keep kernel and user-payload linker scripts separate.
|
||||||
|
- Build a tiny test suite of loaded programs: hello, echo, memory copy, return status.
|
||||||
|
- Run the same payloads after Phase 12 trap handling lands to catch ABI regressions.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- OSDev kernel structure notes: https://wiki.osdev.org/Kernel
|
||||||
|
- RISC-V calling convention: https://github.com/riscv-non-isa/riscv-elf-psabi-doc
|
||||||
|
- Embedded Artistry first-fit allocator background: https://embeddedartistry.com/blog/2017/02/15/implementing-malloc-first-fit-free-list/
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Phase 9 - CSRs + M-Mode Trap Handling
|
# Phase 12 - CSRs + M-Mode Trap Handling
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Phase 10 - Timer
|
# Phase 13 - Timer
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# Phase 11 - Interrupt Controller
|
# Phase 14 - Interrupt Controller
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Phase 12 - A Extension
|
# Phase 15 - A Extension
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Phase 13 - Pipeline
|
# Phase 16 - Pipeline
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Phase 14 - SPI Flash Boot + DRAM
|
# Phase 17 - SPI Flash Boot + DRAM
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# Phase 15 - S-Mode, U-Mode, Sv32 Virtual Memory
|
# Phase 18 - S-Mode, U-Mode, Sv32 Virtual Memory
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# Phase 16 - Linux Boot Contract
|
# Phase 19 - Linux Boot Contract
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Phase 17 - Linux
|
# Phase 20 - Linux
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
Reference in New Issue
Block a user