libriscv 1.0.0 copy "libriscv: ^1.0.0" to clipboard
libriscv: ^1.0.0 copied to clipboard

A RISC-V emulator in pure Dart supporting RV32/RV64 with M, A, F, D, C extensions, virtual memory, SBI runtime, and Linux/xv6 boot.

LibRISCV #

A RISC-V emulator written in pure Dart. Supports RV32 and RV64 with M, A, F, D, C, Zicsr, Zifencei, and Sstc extensions, SV32/SV39 virtual memory, a built-in SBI runtime, and a memory-mapped device model. Boots both 32-bit and 64-bit Linux to a BusyBox shell, and runs xv6.

Features #

Architecture #

  • RV32I / RV64I base integer ISA
  • M integer multiply/divide
  • A atomics (LR/SC, AMO)
  • F / D single- and double-precision IEEE 754 floating point
  • C compressed instructions
  • Zicsr CSR instructions
  • Zifencei instruction-fetch fence
  • Sstc supervisor timer (RV64)

System #

  • Machine, Supervisor, and User privilege levels
  • SV32 (RV32) and SV39 (RV64) page-based virtual memory with TLB
  • Built-in SBI runtime — v0.1 legacy and v0.3 extensions
  • Memory-mapped device model with IRQ delivery through the PLIC
  • ELF binary loading via dart_elf

Devices #

  • UART 16550A — serial console with pluggable input/output callbacks
  • CLINT — core-local interruptor (mtime, mtimecmp, MSIP)
  • PLIC — platform-level interrupt controller
  • VirtIO block — disk storage with legacy and modern MMIO layouts
  • HTIF — Host–Target Interface for test harnesses

Conformance #

All 217 tests from the official RISC-V test suite pass (94 RV32 + 123 RV64), covering base ISA, M, A, F, D, C, supervisor, and machine-mode scenarios.

Install #

dependencies:
  libriscv: ^1.0.0

Then:

dart pub get

Quick start #

A minimal RV32 run — allocate memory, load two instructions, step the CPU:

import 'dart:typed_data';
import 'package:libriscv/libriscv.dart';

void main() {
  final emulator = EmulatorBuilder()
      .xlen(XLEN.rv32)
      .memorySize(4 * 1024 * 1024)
      .enableRV32M(true)
      .enableRV32A(true)
      .enableZicsr(true)
      .build();

  final ram = Uint8List(4 * 1024 * 1024);
  emulator.cpu.memory.add(
    BasicMemoryDevice.from(ByteData.view(ram.buffer), MemoryProtections.rwx),
    0x80000000,
  );

  final view = ByteData.view(ram.buffer);
  view.setUint32(0, 0x02a00093, Endian.little); // addi x1, x0, 42
  view.setUint32(4, 0x00100073, Endian.little); // ebreak

  emulator.cpu.pc = 0x80000000;
  emulator.step(2);
  print('x1 = ${emulator.cpu.x[1]}'); // x1 = 42
}

The same example lives in example/example.dart.

Booting Linux #

LibRISCV ships with working direct-boot scripts for both RV32 and RV64 Linux kernels. They use the built-in SBI v0.1 runtime — no OpenSBI or U-Boot needed.

RV64 #

dart run example/linux/boot_rv64_direct.dart \
  --kernel example/linux/Image-rv64 \
  --initrd example/linux/rootfs-rv64-busybox.cpio \
  --memory 512

Boots Linux 6.6 to a BusyBox shell in roughly 1.2 s of emulated time.

RV32 #

dart run example/linux/boot_rv32_direct.dart \
  --kernel example/linux/Image-rv32 \
  --initrd example/linux/rootfs-rv32-busybox.cpio \
  --memory 256

Boots Linux 6.6 to a BusyBox shell in roughly 3.3 s of emulated time. BusyBox's mount userspace issues one harmless SIGSEGV; the shell remains usable.

Building your own kernel and rootfs is covered in example/linux/README.md.

Running xv6 #

The MIT teaching OS runs on RV64. You need to build xv6 yourself; the boot script expects example/xv6/kernel and example/xv6/fs.img:

git clone https://github.com/mit-pdos/xv6-riscv
cd xv6-riscv && make
cp kernel fs.img ../example/xv6/

cd ..
dart run example/xv6/boot_xv6.dart

Other examples #

Script Description
example/example.dart Minimal in-process RV32 run
example/linux/boot_rv64_direct.dart Direct-boot Linux on RV64
example/linux/boot_rv32_direct.dart Direct-boot Linux on RV32
example/rv32/boot_rv32.dart Bare-metal RV32 kernel with UART + timer
example/xv6/boot_xv6.dart xv6 on RV64 with VirtIO disk

Architecture #

Emulator
├── HART (hardware thread)
│   ├── x / f register files
│   ├── CSR system
│   ├── MMU + TLB
│   └── per-extension emulators
├── Memory map
│   ├── Physical RAM
│   └── Memory-mapped devices
└── Instruction pipeline
    ├── Decoder (per extension)
    ├── Dispatcher
    └── Exception handler + SBI runtime

Privilege invariants #

Two spec-critical rules are centralised in PrivilegePolicy (lib/src/riscv/emulator/privilege_policy.dart) so that every exception handler and every MMU shares one implementation:

  • MPRV on xRET — MRET and SRET clear MSTATUS.MPRV when the target privilege is below M.
  • S-mode access to U pages — S-mode may never execute a U=1 page; SUM only gates S-mode data loads/stores.

Regression tests live in test/privilege_semantics_test.dart.

Development #

dart pub get            # install deps
dart analyze            # static analysis
dart format lib test    # format
dart test               # unit + compliance tests (217 RISC-V tests)

Adding a RISC-V extension #

  1. Write a decoder in lib/src/riscv/decoder/.
  2. Write the executor in lib/src/riscv/emulator/.
  3. Wire it into Emulator / EmulatorBuilder.
  4. Add compliance tests under test/data/riscv-tests/.

Adding a device #

  1. Extend MemoryMappedDevice.
  2. Implement register read/write handlers.
  3. For IRQs, call into the PLIC.
  4. Attach to the emulator via emulator.cpu.memory.add(device, address) or one of the fluent withX(...) helpers.

Extension matrix #

Extension RV32 RV64
I (base)
M (mul/div)
A (atomic)
F (float)
D (double)
C (compressed)
Zicsr
Zifencei
Sstc (timer)

License #

MIT — see LICENSE.

References #

1
likes
140
points
14
downloads

Documentation

API reference

Publisher

verified publisherlibdbm.com

Weekly Downloads

A RISC-V emulator in pure Dart supporting RV32/RV64 with M, A, F, D, C extensions, virtual memory, SBI runtime, and Linux/xv6 boot.

Repository (GitHub)
View/report issues

Topics

#risc-v #emulator #virtual-machine #rv32 #rv64

License

MIT (license)

Dependencies

args, dart_elf, sprintf

More

Packages that depend on libriscv