Total points: See CAPSTONE.md (Tier 1 + Tier 2 scoring)
Estimated time: Open-ended (recommended 8-12 hours over the final two weeks)
Prerequisites: Labs 1-13 complete (all six gates require specific prior labs)
Overview
Lab 14 is not a prescribed sequence of parts -- it is an integration sprint. You are assembling all CSA-201 modules into a single running system: Virtus OS v2 on DE10-Nano. The deliverable is the capstone package described in CAPSTONE.md.
Read CAPSTONE.md in full before starting this lab. The six Tier 1 gates are the primary pass/fail criteria. Tier 2 scoring depends on passing all six gates first.
This document is a pre-submission checklist and debugging reference. Work through it top to bottom before submitting.
Pre-flight: gate-by-gate prerequisite map
Before attempting a gate, confirm the listed lab artifacts are working on hardware (not just in Verilator):
| Gate | Gate requirement | Required lab |
|---|---|---|
| Gate 1: OS boots | Cold boot, OLED shows boot message within 10 s | Labs 11, 12 |
| Gate 2: U/S transition | ECALL flips priv bit; MRET restores it | Lab 2 |
| Gate 3: Page-fault handler | Unmapped access handled cleanly | Lab 7 (Part C) |
| Gate 4: PMP W^X | Write to code page faults; OS handles | Lab 8 (Part C) |
| Gate 5: Round-robin scheduler | Two tasks advance concurrently on OLED | Lab 11 (Part C) |
| Gate 6: SSD1306 live output | OS version + process names + live counter | Lab 12 (Part C) |
If any prerequisite lab is incomplete, finish it before attempting the corresponding gate.
Integration checklist
Work through these items in order. Check each off as you verify it on DE10-Nano hardware.
Boot and initialization order
-
clint_init()called beforescheduler_start(); mtimecmp set before MIE enabled -
mmu_init()called before any user process spawns; satp written before MRET to U-mode -
pmp_init()called before switching to U-mode; at minimum entry 0 (code R+X) and entry 1 (data R+W) set -
oled_init()called before first display write;sd_init()called before first FAT access - Heap initialized for both BRAM and DDR3 regions before
Memory.allocis called -
__stack_chk_guardinitialized before any user function runs
MMU + PMP interaction (common source of silent failures)
- After every page-table modification,
SFENCE.VMAexecuted before the next memory access to the modified VA - PMP entries cover exactly the physical ranges you intend; virtual addresses must be translated to physical before PMP checks them
- If using the Sv32 MMU, PMP checks physical addresses (post-translation). Test with a code page that has both a valid PTE and a PMP W=0 entry; a store should fault even if the PTE has W=1
Scheduler + GC interaction
-
Memory_gc()callsscheduler_stop()(disables timer interrupt) at entry andscheduler_resume()at exit - If a context switch fires during the mark phase (before stop-the-world), the GC state will be corrupted. Verify timer interrupt is disabled before gc_mark() executes
- After GC completes and scheduler resumes, the first timer interrupt fires normally; verify by counting interrupts in the first 100 ms after GC
Context-switch register file integrity
- All 32 registers are saved and restored (x0-x31); do not skip x0 (it must read as zero but the slot must be consistent)
-
mepcsaved and restored correctly; the interrupted instruction re-executes correctly after MRET -
satpsaved and restored per process (if processes have different address spaces -- even if they share the same page table, save satp in case you later add per-process tables) -
mstatus.MPPset to U (=00) before MRET to user process; set to M (=11) only when returning to kernel
Driver timing
- I2C SCL does not exceed the SSD1306's maximum clock rate (400 kHz fast mode, or 100 kHz standard mode)
- SD card SPI clock rate at or below 400 kHz during init sequence (CMD0 through ACMD41); can increase to 25 MHz after initialization
- No SPI transaction starts while CS is high; CS must be asserted (low) before the first clock edge and de-asserted after the last
Common last-minute failures and fixes
Gate 1: OLED shows nothing after boot.
- Most common cause:
oled_init()not called beforeoled_draw_string(). - Second most common: I2C address wrong (try 0x3C and 0x3D).
- Third: pull-up resistors missing on SDA/SCL lines (SSD1306 is open-drain; needs external 4.7k ohm to VCC).
Gate 3: Page fault handler fires but process does not resume.
- Check that MRET returns to the faulting address (mepc = faulting instruction VA, not mepc + 4).
- Check that
SFENCE.VMAis called after installing the new PTE. - Check that alloc_page() returns a non-zero physical address; if the free list is exhausted, the PTE install writes address 0, which is likely already mapped to kernel code.
Gate 4: Store to code page does not fault; "write succeeded" appears on OLED.
- Check that the PMP entry for the code region has W=0. The pmpcfg byte should have bit 1 (W) = 0.
- Check that the U-mode program is actually running in U-mode (priv = 00). If it is running in M-mode due to a MRET bug, PMP W=0 with L=0 does not apply to M-mode.
- Check priority: if a later PMP entry has W=1 and matches the same address, it will not override entry 0 (higher number = lower priority); but verify your CPU's priority cascade is correct.
Gate 5: One task advances but the other freezes.
- Most likely: the context switch is restoring registers to the wrong process. Check that the process table index used for save and restore is consistent.
- Second most likely: timer is not reprogrammed after context switch.
reprogram_timer(500000)must execute before MRET in the timer handler. - Third: the new process's sp was initialized to a valid stack address in
sys_spawn(); if sp is 0 or uninitialized, the process will fault on first push.
Gate 5: Context-switch cycle count is far above 150 cycles.
- Cause:
memcpyis not optimized; word-by-word copy is slower than expected. - Fix: implement an unrolled 32-register save using
sw x1, 4(a0); sw x2, 8(a0); ...(32 explicit stores); no loop overhead.
Gate 6: OLED shows garbled output.
- Check page/column cursor is set before each string write; successive writes without cursor reset will run off the end of the page.
- Check that oled_draw_string does not write past column 128; add a bounds check.
Deliverable package reminder
csa201-capstone-{your-name}.zip
├── bitstream/
│ └── virtus-os-v2-de10nano.sof
├── hdl/
├── firmware/
├── compiler/
├── measurements/
├── demo/
│ └── capstone-demo.mp4 (3-5 minutes)
└── writeup.pdf (6-8 pages, six sections per CAPSTONE.md)
SHA-256 all binaries and include checksums.txt.
Submit to the course LMS before the deadline. Late submissions lose 10% per day. Gate demonstrations must be live on hardware (not Verilator) to count for Tier 1.
Grading
See CAPSTONE.md for the complete rubric.
| Component | Points |
|---|---|
| Tier 1: Six gates (pass/fail; must pass all for Tier 2 credit) | Pass/Fail |
| Tier 2 / Mitigation depth | 40% of Tier 2 |
| Tier 2 / Measurement quality | 30% of Tier 2 |
| Tier 2 / Demo and write-up | 30% of Tier 2 |
B- minimum on Tier 2 (>= 70%) required for the VCA-CSA-201 Certificate of Completion.