在 WebAssembly 生态日益成熟的当下,浏览器已不再满足于简单的 JavaScript 逻辑执行,而是逐步成为能够承载复杂系统仿真的运行平台。Velxio 2.0 作为一个完全运行在浏览器中的开源多板卡仿真器,其核心挑战在于如何在同一界面内同时模拟来自五种不同 CPU 架构的微控制器,并保证它们之间的时序同步与互操作性。本文将从指令翻译层的角度,详细解析 Velxio 实现多架构支持的工程方案。

仿真引擎的技术选型分布

Velxio 2.0 对不同架构采取了差异化的仿真策略,这一决策并非随意为之,而是基于性能、兼容性以及开发成本的综合考量。从整体架构来看,五种 CPU 架构的仿真实现可以分为两大类:纯浏览器端实现与后端 QEMU 桥接实现。

AVR8 架构(ATmega328p、ATmega2560、ATtiny85 等)采用纯浏览器端仿真,依赖于 wokwi 社区维护的 avr8js 库。该库完全使用 TypeScript 编写,能够在无需后端支持的情况下实现 ATmega 系列芯片的完整指令集模拟,包括定时器、USART、SPI、ADC 等外设。仿真循环通过 requestAnimationFrame 驱动,在现代浏览器中可稳定达到约 60 FPS 的执行效率。这种纯前端方案的优势在于零网络延迟、离线可用,以及能够与前端 UI 组件进行紧耦合的时序交互。

ARM Cortex-M0+ 架构(RP2040)的仿真同样采用纯浏览器方案,由 rp2040js 库实现。该库模拟了树莓派官方 RP2040 芯片的完整功能,包括双核执行、GPIO 事件监听、UART 通信、I2C/SPI 总线以及 PWM 输出值得注意的是,rp2040js 实现了 WFI(Wait For Interrupt)指令的优化处理,当固件执行 delay() 函数时,仿真器会跳过实际的忙等待,直接推进仿真时间,从而大幅提升长时间延时的执行效率。此外,其示波器功能能够以约 8 纳秒的分辨率记录 GPIO 状态变化,为调试时序敏感型代码提供了有力支持。

RISC-V 架构(ESP32-C3 所用的 RV32IMC 以及 CH32V003 所用的 RV32EC)则采用了完全自研的 TypeScript 实现,命名为 RiscVCore.ts。这一选择背后的逻辑在于:ESP32-C3 作为近年来新兴的 RISC-V 架构芯片,社区尚缺乏成熟的浏览器端仿真库,而自研方案可以避免外部依赖的版本兼容问题。RiscVCore.ts 直接在浏览器中解析和执行 RISC-V 指令,实现了即开即用的启动速度(零冷启动延迟),并且能够离线运行。该仿真器通过 W1TS(Write 1 to Set)与 W1TC(Write 1 to Clear)两种内存映射 I/O 寄存器模式来处理 GPIO 操作,这与 ESP32-C3 硬件的 MMIO 设计完全一致。

Xtensa 与 ARM Cortex-A53 的后端桥接方案

对于 Xtensa LX6/LX7 架构(ESP32、ESP32-S3)以及 ARM Cortex-A53 架构(树莓派 3B),纯浏览器方案面临显著的指令翻译性能瓶颈。ESP32 的双核 Xtensa 处理器主频高达 240 MHz,要在浏览器中通过软件模拟实现这一性能级别几乎不可能。因此,Velxio 采用了 WebAssembly 后端桥接的混合架构。

Xtensa 仿真使用 lcgamboa 维护的 QEMU 分支版本,该方案将 QEMU 8.1.3 编译为 WebAssembly 模块运行在后端容器中。Web 前端通过 WebSocket 与后端 QEMU 进程通信,将 GPIO 状态变化、UART 数据传输等外设交互指令转发至仿真器。Velxio 团队对 QEMU 进行了针对性修改,使其能够正确模拟 ESP32 的外设行为,包括完整的 GPIO 控制(支持 40 个 GPIO 引脚的方向追踪与状态回调)、多 UART 支持(UART0/1/2)、ADC 读取(12 位分辨率,0-3300mV 电压注入)、I2C 同步总线、SPI 全双工通信、RMT 硬件解码器(支持 WS2812 RGB LED)以及 16 通道 LEDC PWM 输出。值得注意的是,WiFi 仿真是通过 SLIRP NAT 模拟实现的,固件可以正常调用 WiFi.begin() 并获得网络连接能力。

树莓派 3B 的仿真则更为复杂,因为它需要运行完整的 Raspberry Pi OS(Trixie 版本)。Velxio 使用官方的 qemu-system-aarch64raspi3b 机器类型进行仿真,模拟 BCM2837 芯片(ARM Cortex-A53 @ 1.2 GHz)。为实现与前端组件的交互,Velxio 提供了 RPi.GPIO 的 shim 层 —— 这是一个树莓派 GPIO 库的降级替代实现,能够将引脚事件通过文本协议发送至前端。为确保会话隔离,QEMU 使用 qcow2 格式的差分镜像,基础 SD 卡镜像永不修改,用户的所有更改都存储在独立的 overlay 层中。

同步多设备仿真的时序协调

Velxio 2.0 最具技术挑战性的特性之一,是能够在同一画布上同时运行多个来自不同架构的板卡,并通过串口、I2C、SPI 等总线进行互操作。这种同步仿真的实现依赖于全局仿真时间的统一调度。

在纯浏览器端方案中(AVR8、RP2040、RISC-V),所有仿真引擎共享同一个由 requestAnimationFrame 驱动的仿真循环。每次帧回调时,各个仿真引擎会根据配置的时间片执行相应数量的指令周期。对于 WFI 优化的固件,仿真器会计算下一次中断发生的时间点,并直接跳转至该时刻,从而避免无效的空循环消耗。

对于后端 QEMU 方案(Xtensa、ARM Cortex-A53),时序协调通过 WebSocket 消息队列实现。前端维护一个全局仿真时钟,当需要与后端 QEMU 同步时,会将当前时间戳附加在每条外设交互消息中。后端 QEMU 则根据接收到的目标时间进行等待或加速,确保各设备之间的时序一致性。例如,当用户在画布上拖动一个连接到 ESP32 的 HC-SR04 超声波传感器时,前端会实时计算传感器的触发与回波时序,并通过 WebSocket 同步至后端 QEMU,使固件能够获得准确的 GPIO 边沿时间戳。

多板卡之间的串口桥接是另一项关键功能。Velxio 实现了 Raspberry Pi 3 与 Arduino 之间的串口通信仿真 —— 当用户在画布上连接两设备的 TX/RX 引脚后,双方的 UART 外设数据会通过内部的虚拟总线进行转发。这一功能使得复杂的物联网场景仿真成为可能,例如在树莓派上运行 Python 脚本读取 Arduino 采集的传感器数据。

工程实践中的关键参数

基于 Velxio 2.0 的实现经验,以下参数可为类似的多架构浏览器仿真项目提供参考。AVR8 仿真推荐将时间片设置为每帧 16MHz / 60 ≈ 267000 个时钟周期,可根据实际固件复杂度动态调整。RP2040 的仿真频率固定为 133MHz,其 WFI 优化阈值建议设置为 1 毫秒以上,即当 delay() 参数超过 1ms 时启用时间跳转。RISC-V 仿真器的指令缓存模拟建议禁用,以降低内存占用并提升响应速度。QEMU 后端的 WebSocket 消息批处理窗口推荐设置为 16 毫秒,在延迟与吞吐量之间取得平衡。对于需要高精度时序的外设(如 WS2812 LED),建议在浏览器端实现软件定时器进行补偿,而非完全依赖 QEMU 的时间精度。

资料来源:Velxio GitHub 仓库(https://github.com/davidmonterocrespo24/velxio)