FRISC OS: First Boot
The moment FRISC OS first booted on real RISC-V hardware — a journey from QEMU to physical silicon. After months of emulator testing, we finally lit up real LEDs.
◉ Target Board
HiFive Unmatched with SiFive U74 cores. Four 64-bit RISC-V cores at 1.2 GHz, 16GB DDR4, PCIe slot, NVMe support. Finally, Lateralus running on native RISC-V silicon.
The U74 is a "Linux-capable" core, meaning it has:
- Full supervisor mode with S-mode page tables
- Physical Memory Protection (PMP)
- IMAFDCSU extension set
- Sv48 virtual memory (48-bit virtual addresses)
◉ Boot Sequence
Getting from power-on to shell takes several stages:
ZSBL → FSBL → OpenSBI → FRISC Bootloader → Kernel → Init → Shell
↓ ↓ ↓ ↓ ↓ ↓
ROM SPI Flash FW_JUMP stage1.ltl kernel.ltl /init
ZSBL (Zero Stage Boot Loader): Hardwired in ROM. Loads FSBL from SPI flash.
FSBL (First Stage Boot Loader): Initializes DDR, loads OpenSBI from SD/NVMe.
OpenSBI: RISC-V Supervisor Binary Interface. Provides M-mode services to S-mode OS. We use FW_JUMP mode — it jumps directly to our bootloader.
FRISC Bootloader: Written in Lateralus, compiled to C. Loads kernel, sets up initial page tables, jumps to kernel_main.
◉ The Magic Moment
January 20, 2026, 2:47 AM. Serial console output:
OpenSBI v1.2
____ _____ ____ ___
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |
\____/| .__/ \___|_| |_|_____/|____/___|
| |
|_|
Platform Name : SiFive HiFive Unmatched A00
...
[FRISC] Bootloader v0.1.0 starting
[FRISC] Found 16 GB RAM @ 0x80000000
[FRISC] Loading kernel from /boot/kernel.bin
[FRISC] Kernel loaded at 0x80200000 (487 KB)
[FRISC] Jumping to kernel_main...
_____ ____ ___ ____ ____ ___ ____
| ___| _ \|_ _/ ___| / ___| / _ \/ ___|
| |_ | |_) || |\___ \| | | | | \___ \
| _| | _ < | | ___) | |___ | |_| |___) |
|_| |_| \_\___|____/ \____| \___/|____/
FRISC OS v0.1.0 on HiFive Unmatched
4 cores detected (U74 @ 1200 MHz)
Memory: 16384 MB
Initializing kernel heap... OK
Initializing interrupts... OK
Starting scheduler... OK
Mounting root filesystem... OK
Welcome to FRISC OS
frisc:/ $
We stared at that blinking cursor for a solid minute before anyone typed anything.
◉ What Broke First
Of course, it wasn't smooth. The journey to that boot included:
Timer interrupt frequency: QEMU's timer runs at 10 MHz. The U74's runs at 1 MHz. Our scheduler ran 10x too slow until we fixed the frequency detection.
Cache coherency: The U74 has non-coherent DMA. Forgot fence.i after self-modifying code? Enjoy stale instruction cache.
UART initialization: The SiFive UART needs explicit clock configuration that QEMU doesn't care about. Spent two days getting serial output working.
◉ Pipeline-Driven Boot
The kernel boot sequence is a Lateralus pipeline:
fn kernel_main() {
serial_puts("[FRISC] Starting kernel subsystems")
let boot_sequence = [
("memory", memory_init),
("interrupts", interrupts_init),
("scheduler", scheduler_init),
("filesystem", fs_init),
("network", network_init),
("devices", device_init),
]
boot_sequence
|> each(fn((name, init_fn)) {
serial_puts(" Starting " + name + "...")
let result = init_fn()
match result {
Ok(_) => serial_puts(" OK"),
Err(e) => panic("Boot failed: " + name + ": " + e),
}
})
serial_puts("Boot complete. Starting init.")
spawn_process("/init")
}
Error handling flows naturally through the pipeline. If any subsystem fails, boot halts with a clear message.
◉ Performance Numbers
First measurements on real hardware:
| Metric | QEMU | HiFive Unmatched |
|---|---|---|
| Cold boot to shell | 4.2s | 1.8s |
| Kernel image size | 487 KB | 487 KB |
| Process spawn | 12ms | 3ms |
| Context switch | ~1ms | ~50μs |
Real hardware is fast. QEMU's full-system emulation adds substantial overhead.
◉ What's Next
With first boot working, we're focused on:
- SMP support (currently single-core only)
- PCIe driver framework
- NVMe storage driver
- Network stack (TCP/IP)
- GUI framebuffer support
See the OS page for build instructions and latest progress.