- Understand the RISC-V ISA.
- Implement a clean, correct RV32 system step-by-step (ISA → tests → peripherals → privileged modes).
- Boot the Linux Kernel.
- Learn how CPUs really work.
This project is an independent personal emulator and is not affiliated with, endorsed by, or sponsored by RISC-V International. “RISC-V” is a trademark of RISC-V International.
This repository is built as a hands-on way to learn computer architecture by implementing the machine “for real”: decoding instructions, executing them, handling memory/MMIO, and validating behavior against official test suites.
The project is developed step-by-step with a correctness-first mindset:
- Implement a feature.
- Run official tests from riscv-tests and RISC-V Architecture Test SIG.
- The project uses the RISCOF Architectural Test Framework.
- Fix edge cases until it matches the expected behavior.
- Then move forward to the next layer (peripherals → privileged → boot flow).
The emulator has already reached major milestones:
- Boots the Linux Kernel.
- Executes code across privilege levels: M-mode / S-mode / U-mode.
- Can run real RTOS workloads like FreeRTOS and Zephyr
From here, the focus is on improving correctness and stability (more official tests, trap/CSR edge cases, peripheral accuracy, and a smoother OpenSBI/DTB/UART console experience). This codebase aims to stay portable (Linux + Windows), readable, and easy to debug. Performance is not the priority yet, clarity and spec alignment are.
- ISA / Extensions
- Base ISA: RV32I
- Supported extensions: M, Zicsr, Zifencei, Zalrsc, Zaamo
- Compressed instructions (C) are intentionally not supported (compile without
-march=...c).
- RV32 emulator in portable C (C17)
- Designed to build on Linux and Windows (MSVC) with a focus on clarity and debugability.
- Privilege modes: M / S / U
- Implements the core privileged flow so software can run in the correct mode and transition via traps/returns.
- Linux-capable boot path
- Able to boot the Linux kernel, using a realistic platform-style flow (OpenSBI/DTB/UART depending on your setup).
- RTOS support
- Capable of running FreeRTOS and Zephyr workloads.
- Official ISA test-driven development
- Running the RISC-V architectural tests (e.g.
rv32ui-*) to validate correctness feature-by-feature.
- Running the RISC-V architectural tests (e.g.
- Full instruction decode for base formats
- Decoder supports R / I / S / B / U / J formats, built for correctness and easy inspection.
- Memory bus + MMIO model
- A simple bus that routes RAM vs MMIO, enabling platform peripherals to be modeled cleanly.
- UART console output via MMIO
- UART-style MMIO (e.g. at
0x10000000) to capture guest output (useful for kernel/RTOS logs).
- UART-style MMIO (e.g. at
- CLINT-style timer
mtime/mtimecmp-style timing to support basic timer behavior and consistent logging.
- Instruction tracing and logging
- Optional trace output to console or file, useful for debugging tests, traps, and boot issues.
- Build the emulator
- You only need Python 3+:
python3 build.pyThe build script prints the output path/name of the emulator executable.
- Install a RISC-V toolchain (RV32, no compressed instructions)
- To build and run your own programs you’ll need a RISC-V GCC toolchain.
- On Windows, the easiest options are WSL or the project’s Docker environment.
- Toolchain: riscv-gnu-toolchain releases
- Windows setup:
- Build the Hello World demo
- Source:
docs/demo/hello_world/hello.S
- Source:
riscv32-unknown-linux-gnu-gcc -march=rv32i -mabi=ilp32 \
-ffreestanding -nostdlib -nostartfiles \
-Wl,-T,link.ld \
-o ./docs/demo/hello_world/hello.elf ./docs/demo/hello_world/hello.S
- If your compiler prefix is different (e.g.
riscv64-unknown-elf-gcc), replace the command name, but keep-march=rv32...and-mabi=ilp32.- Important: compile with
-march=rv32...without thecextension (example:rv32i,rv32im,rv32ima, etc). If you compile withrv32ic/rv32imac, your binary will contain compressed instructions and the emulator may fail (by design).
- Run it (on project root)
./bin/exnihilo --kernel ./docs/demo/hello_world/hello.elf