Simmerv
Simmerv 是一个用 Rust 编写的 RISC-V SoC 模拟器, 可编译为 WebAssembly。 它最初是 Takahiro 的 riscv-rust 模拟器 的一个分支,但如今已 被大量重写,使其更加完整且 速度更快。 最终,我们预计它将变得显著更快, 但这项工作被推迟,直到我们能够运行标准基准测试 和现成的 Linux 发行版。
在线演示
您可以在浏览器中在模拟器上运行 Linux:在线演示 在此
在 Simmerv 上启动 Ubuntu(加速 50 倍)

特性
- 模拟 RISC-V
RV64GC_Zba_Zbb_Zbc_Zbs_Zicond_Zfhmin_Svinval_Svade_Svpbmt_Sstc_Zicbom_Zicbop_Zicboz_Zihpm(RVA22) 处理器和外围设备 (CLINT, PLIC, NS16550A UART, virtio 块设备, 以及 VirtIO 以太网) - 可选的 RVA23 模式,通过
--rva23启用:V向量扩展 (RVV 1.0,ELEN=64,VLEN=128 或 256,通过--vlen实现) 以及 Zcb, Zimop, Zcmop, Zfa, Zawrs, Zacas, Zabha, Zvbb, Zvkt 和 Zihintntl。 启动 Ubuntu 26.04 的 RVA23 移植版本。 - 支持原生和 WASM 目标
- 快照
- 速度计
指令/特性支持状态
RVA22 配置文件(完整)
- RV64IMAC
- RV64FD
- RV64Zifencei
- RV64Zicsr
- Zba, Zbb, Zbc, Zbs ("B" 扩展)
- Zicond
- Zfhmin (半精度浮点转换)
- Zihpm (硬件性能计数器)
- Zicbom, Zicbop, Zicboz (缓存块操作)
- Svinval (细粒度 TLB 无效化)
- Svade (硬件 A/D 访问故障)
- Sstc (stimecmp/menvcfg 定时器比较)
- Sv39, Sv48, Sv57 (较宽的两个为可选启用:参见
--satp-mode) - Svpbmt (基于页面的内存类型;接受 PTE,无需模拟缓存)
- 特权规范 1.12 (mcounteren/scounteren, senvcfg, 0 个条目的 PMP 存根)
- Svnapot
- [-] PMP 强制 (实现了 0 个条目;允许所有访问)
该模拟器支持上述列出的所有指令。
- 通过所有 RV64IMC 的 riscof (RISC-V 架构测试)
- 启动 Buildroot, Debian Trixie, Ubuntu
- 支持 Linux OpenSBI 和传统 BBL 启动
RVA23 配置文件(进行中)
- V — 向量扩展,RVV 1.0。 包含
Zve64d以及完整的V指令集:配置、单位步长 / 跨步 / 索引 / 分段 / 整寄存器 / 仅首元素故障的加载和存储, 整数、定点、掩码、置换和浮点运算, 以及带有mstatus.VS的vstart/vl/vtype/vxrm/vxsat/vcsr/vlenbCSR。VLEN默认为 128(Zvl128b,即配置文件 的最小值);--vlen 256则选择Zvl256b。 - Zvfh, Zvfhmin — 向量半精度(完整的算术集,它是
配置文件所要求的
Zvfhmin的超集) - Zvkt, Zkt — 恒定时间执行;通过没有数据依赖时序的功能 模型即可轻松满足
- Zicond, Zfhmin, Zicbom/z/p, Svinval, Svnapot, Svpbmt, Sstc, Svade (继承自 RVA22)
- Zcb — 较新的压缩编码。 每一条都缩写了一个 模拟器已经拥有的指令,因此所有十二条都直接解码为 该 uop,而不是并行执行路径。
- Zimop, Zcmop — 可能是操作。
mop.r.n/mop.rr.n向rd写入零;c.mop.n不改变任何架构状态。 - Zfa —
fli(两个常量表),fminm/fmaxm,静默比较fleq/fltq,fround/froundnx以及fcvtmod.w.d。 半精度成员被省略:它们需要完整的Zfh,而 RVA23 并未强制要求,且此模拟器也未实现。 - Zvbb — 向量基本位操作(
vandn,vbrev,vbrev8,vrev8,vclz,vctz,vcpop,vrol,vror,vwsll),以及与之相关的Zvkb,它是一个严格子集 - Zawrs — wait-on-reservation-set。 两种形式都可能立即终止等待 且原因任意,因此两者均作为空操作(no-op)退休。
- Zacas —
amocas.b/h/w/d/q,包括针对偶数/奇数 寄存器对的 quadword 形式 - Zabha — 所有 AMO 的字节和半字形式
- Zihintntl — 非临时局部性提示。 四个提示是
ADD x0, x0, x2..x5(以及C.ADD x0, x2..x5压缩形式),因此 任何符合规范的 hart 已经将它们作为空操作退休;功能模型 没有缓存层次结构可供提示,这也是Zkt和Zvkt免费满足的原因。 已验证其不会修改x0和源 寄存器。 -
satp中的 Sv48, Sv57。 通过--satp-mode sv48/sv57选择加入,因为 Linux 保持satp接受的最宽模式:默认使用 Sv57 会将 所有现有客户机迁移到更深的页表。 由 Tenstorrent 套件的paging_sv48和paging_sv57组进行验证。 - Supm / Ssnpm / Smnpm — 指针掩码。 已推迟。 没有任何东西依赖
它:掩码是按进程选择加入的(
prctl(PR_SET_TAGGED_ADDR_CTRL)), 因此未找到它的软件只是不标记指针,而 唯一的实际受益者 -- HWASAN,这也是 Android 依赖 Arm 的 等效 TBI 的原因 -- 在 RISC-V 上尚不存在。 模拟器也不会 获得该功能旨在提供的东西:重点在于硬件免费忽略 标签位,而在这里这仅仅是“不对这些 addresses". 若 LLVM 获得 RISC-V HWASAN 支持,或某个发行版提供 经过 HWASAN 插桩的 RISC-V 软件包,则重新评估。 - Sscofpmf — 计数溢出中断。 计数器从全 1 回绕时
在 0->1 边沿置位
OF并触发LCOFIP;该中断 以CounterOverflowInterrupt形式投递,且scountovf反映此状态。 在两个设备树中均有声明。 - Ssstateen / Smstateen — 状态使能 CSR。
mstateen0的SE0和ENVCFG位门控 M 模式以下的sstateen0和senvcfg;其余所有 位,以及mstateen1..3/sstateen0..3整体,读取值为零,因为 这些位所保护的状态(AIA、IMSIC、Zcmt的jvt、Zfinx的fcsr、 自定义)在此处不存在。mstateen0复位为允许状态而非 架构定义的零值:它所门控的唯一状态是senvcfg,因此 默认拒绝既无保护意义,又会破坏任何早于 Smstateen 且 从未开启该门控的固件。 - H — 虚拟化扩展,RVA23S64 所必需。 已推迟。 运行
Linux 无需此扩展,仅托管 Linux 时需要:若来宾系统未检测到 H,则
不提供 KVM。 它也是规模最大的一项——两级
地址转换、VS 模式 CSR 组、虚拟化加载/存储指令——且位于
MMU 快速路径上。 Tenstorrent 的测试套件包含一个
h_ext测试 树,目前被跳过,因此测试将在该扩展被实现时 等待执行。
因此 RVA23 是完整的,但 Supm 和 H 除外,两者均因上述相同原因被推迟:没有任何内容依赖于它们,它们也不阻塞 simmerv 所针对的任何内容。
覆盖率并非自我报告:用户态指令集由 Tenstorrent 的架构测试进行验证(参见 tests/tenstorrent/run.sh),向量实现则在两种
VLEN 宽度下与 QEMU 逐条指令进行差异比对(tests/vector/run.sh)。
设备树会通告已实现的内容,以便来宾可以发现。
这包括默认(非---rva23)机器,该机器过去仅命名
imafdc 以及少数几个 CSR 扩展,尽管它一直拥有
bitmanip 指令集、Zicond、Zfhmin 以及缓存块操作。
V、Zcb、Zimop、Zcmop、Zfa、Zvbb、Zawrs、Zacas 和 Zabha 默认关闭,
并通过 --rva23 一起启用,以便在没有该标志的运行中仍然模拟一个
会陷入所有这些编码的 hart。(从 RVA22 继承的条目
始终开启。)RVA23 强制要求 V,因此一个开关即可涵盖全部:它控制
指令解码、misa V 位以及向量 CSR,并切换到一个
其 cpu 节点通告该配置文件的设备树。
$ cargo r -r -- --rva23 -n my-vector-program.elf
要启动 Ubuntu 的 RVA23 移植版,来宾内核也必须使用 CONFIG_RISCV_ISA_V=y 进行构建——如果没有它,内核会从设备树解析出的 ISA 中丢弃 v,并且 RVA23 用户空间会在 ld.so 中的第一条向量指令处崩溃,因为该配置文件允许 glibc 发出没有标量回退的向量代码。启动日志中的 riscv: base ISA extensions 是需要检查的内容:它应显示为 acdfimv。
$ cargo r -r -- --rva23 -f ubuntu-26.04-preinstalled-server-riscv64.img \
fw_payload.bin,0x80000000
tests/vector/run.sh 构建了一个裸机测试器 —— 涵盖所有 SEW、LMUL、舍入模式和寻址形式的 1994 个指令用例,既包括 RVA23 的扩展,也包括基础 RVV —— 并将 simmerv 的转录与 QEMU 的 virt 机器进行差异比对,该机器共享 simmerv 的内存映射。 目前所有 1994 行转录完全匹配,两侧均触发三个陷阱:vwsll 在 SEW=64 时,其中扩展后的元素超出 ELEN 且编码必须为非法。
一个有意为之的差异:simmerv 强制执行 vill、寄存器组对齐、EMUL 范围以及“带掩码指令不得写入 v0”的规则,但不执行更细粒度的源/目的重叠约束(双宽度 vs2 组内的窄化 vs1,段加载目的与索引组重叠)。 QEMU 对这些情况会触发非法指令;simmerv 则执行它们。 没有符合规范的汇编器会生成这些指令。
如何使用 VirtIO 块设备 (/dev/vda) 运行 Linux
非常重要:镜像使用 git LFS 存储。 安装 LFS(别忘了 git lfs install 也要安装),如有需要请重新检出。 否则
镜像将是包含 LFS 指针的小文件。
$ cargo r -r -- linux/fw_payload.bin,0x80000000 -f linux/rootfs.img
或
$ cargo r -r -- -c linux/opensbi/fw_jump.elf,0x80000000 linux/vmlinux,0x80200000 -f linux/rootfs.img
如何使用 initramfs (/dev/ram) 运行 Linux
分配 2 GiB,使用设备树,将 initramfs 置于 0xa0000000, 并在该地址加载 initrd2+gdb.cpio 二进制文件。
$ (cd linux;cargo r -r -- -m 2048 -d with-initrd.dtb fw_payload.bin,0x80000000 initrd2+gdb.cpio,0xa0000000)
如何配置网络 (VirtIO-net)
Simmerv 在第二个 virtio-mmio 窗口上模拟了一个 VirtIO-net 设备(MAC 52:54:00:12:34:56)。内置的设备树已经声明了它,因此带有 CONFIG_VIRTIO_NET 的来宾内核会自动探测它——但在你附加主机后端之前,该设备处于非活动状态(数据包会被丢弃)。除了后端提供的功能外,没有内置的模拟器 NAT/DHCP。
Linux: TAP 接口 (-T)
在 Linux 上,Simmerv 将来宾的网卡连接到一个 TAP 设备——这是一个没有自身 DHCP 或 NAT 的原始二层链路,因此你需要配置主机端并为来宾分配静态地址(或者如果你愿意,可以将 tap0 桥接到真实网络)。
- 创建一个由你的用户拥有的持久化 TAP(这样 Simmerv 就不需要 root 权限):
$ sudo ip tuntap add dev tap0 mode tap user $USER
$ sudo ip link set tap0 up
$ sudo ip addr add 172.16.0.1/24 dev tap0
- 对于访客互联网访问,在主机上启用转发 + NAT。
MASQUERADE 规则必须指定您的面向互联网的接口——从默认路由自动检测,
而不是假设
eth0:
$ UPLINK=$(ip route show default | awk '{print $5; exit}') # e.g. enp9s0, wlan0
$ echo "NAT via uplink: $UPLINK" # sanity-check it
$ sudo sysctl -w net.ipv4.ip_forward=1
$ sudo iptables -t nat -A POSTROUTING -s 172.16.0.0/24 -o "$UPLINK" -j MASQUERADE
$ sudo iptables -A FORWARD -i tap0 -j ACCEPT
$ sudo iptables -A FORWARD -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT
如果来宾能够到达主机(172.16.0.1)但无法到达更远的地方,
此规则几乎总是罪魁祸首——使用
sudo iptables -t nat -L POSTROUTING -n -v 检查它(错误的 out 接口,或
当来宾生成流量时 pkts 计数卡在 0)。
- 运行附加到 TAP 的 Simmerv(
-T <ifname>):
$ cargo r -r -- -T tap0 linux/fw_payload.bin,0x80000000 -f linux/rootfs.img
- 在客户机内部配置接口(名称可能为
eth0、enp0s…等 — 请检查ip link):
# in the guest
$ ip addr add 172.16.0.2/24 dev eth0
$ ip link set eth0 up
$ ip route add default via 172.16.0.1 # only needed for step 2
$ echo 'nameserver 1.1.1.1' > /etc/resolv.conf
主机(172.16.0.1)和来宾(172.16.0.2)现在可以互相 ping 通;通过
步骤 2,来宾还可以访问互联网。之后使用
sudo ip tuntap del dev tap0 mode tap 移除 TAP。
macOS: vmnet 共享/NAT(--vmnet)
在 macOS 上,Simmerv 使用 Apple 的 vmnet.framework 以共享模式运行,该模式
会自动提供 DHCP 和 NAT —— 来宾只需请求一个
地址。vmnet 共享模式需要提升权限,因此请在
sudo 下运行。首先进行构建,以免 sudo 以 root 身份重新构建整个目录树:
$ cargo build -r
$ sudo ./target/release/simmerv_cli --vmnet linux/fw_payload.bin,0x80000000 -f linux/rootfs.img
客户机将获取 vmnet 子网(通常为 192.168.x.x)上的地址,
并通过 NAT 连接到宿主机的网络。如果客户机镜像未自动启用链路,
请在其中运行一个 DHCP 客户端:
# in the guest
$ udhcpc -i eth0 # busybox; or: dhclient eth0
(-T/--tap 仅限 Linux,--vmnet 仅限 macOS;在另一平台上均会报错。)
如何运行 riscv-tests
$ ./run-riscv-tests.sh
如何在 Web 浏览器中导入和使用 WebAssembly RISC-V 模拟器
参见 wasm/web
如何安装和使用 WebAssembly RISC-V 模拟器 npm 包
参见 wasm/npm