\title{计算机仿真与虚拟硬件实现}
\author{杨其臻}
\date{Aug 31, 2026}
\maketitle
\chapter{为什么需要把硬件搬到软件里}
硬件研发、教学和测试长期受制于真实硅片的物理限制。一次流片动辄数百万美元，流片周期以月为单位计算，拿到样片后调试环境搭建也需耗费大量时间。软件仿真把这些痛点转化为可重复、可观测、可任意暂停的进程，使得固件工程师在第一行代码写完就能在虚拟芯片上跑起来，也让高校学生不必等待昂贵的 FPGA 板卡就能体验「一生一芯」的完整流程。\par
仿真、虚拟化与建模三者常常被混为一谈。仿真（Simulation）追求对电路行为的数学复现，力求在时序、功耗和温度等多维度上与真实器件一致；虚拟化（Virtualization）更强调在通用处理器上高效运行另一套指令集体系结构，关注吞吐量而非绝对时序；建模（Modeling）则介于两者之间，抽象掉具体门级细节，只保留事务级或算法级特性。本文聚焦于仿真与虚拟化交汇的「虚拟硬件平台」，即既能提供周期级精度，又能以接近真实速度执行的软件模型。\par
\chapter{从电路到时序的全栈映射}
数字逻辑的本质是布尔代数与延迟的结合。事件驱动仿真把 0/1 电平变化抽象为「事件」，当一个门的输入发生跳变，仿真器计算其输出并把新事件插入时间轮队列。延迟模型可以是零延迟、传输延迟或惯性延迟，分别对应理想逻辑、连线寄生与毛刺抑制。在寄存器传输级，仿真器把时钟沿视为离散时间步，每一拍更新所有触发器状态，从而实现周期精确；若继续抽象到事务级，则把一次读写操作视为原子事务，忽略内部流水线细节，速度可提升数十倍。\par
存储器与外设同样需要软件映射。地址空间被切分为若干区间，CPU 发起的读写请求经总线矩阵路由到对应模型；中断则通过回调或信号量机制注入 CPU 模型。精度与速度不可兼得：近似模型丢弃低位翻转以换取更大步长；动态二进制翻译把热点指令直接编译为宿主机原生代码；JIT 再进一步把整个基本块翻译并缓存，从而把仿真效率逼近物理机。\par
\chapter{典型实现技术路线}
基于 HDL 的仿真器直接解析 Verilog/VHDL 源代码。开源的 Icarus Verilog 把源文件编译成 vvp 字节码，再由虚拟机逐事件解释；商业的 VCS、ModelSim 则在编译阶段做大量静态优化，并提供波形、断言与覆盖率收敛工具。这类方案精度最高，但速度通常只有真实芯片的千分之一到万分之一。\par
高级语言实现的虚拟平台把硬件行为用 C++ 或 SystemC 重写。QEMU 通过动态二进制翻译在 x86 宿主机上执行 ARM 或 RISC-V 代码，速度可达真实芯片的 1/5 左右；gem5 则提供可配置的乱序流水线、Cache 层次与 DRAM 控制器，精度介于 RTL 与事务级之间，适合体系结构研究。SystemC 把硬件模块封装为 sc\_{}module，通过 sc\_{}port/sc\_{}export 完成 TLM-2.0 套接字互连，兼顾建模灵活性与仿真性能。\par
浏览器里的轻量级方案近年来异军突起。WebAssembly 配合 JIT 让 Verilog 仿真器在客户端运行，学生只需打开网页就能看到波形滚动；TinyEMU、riscvOVPsim 也提供了 WASM 版本，极大降低了教学门槛。FPGA 原型则位于另一极：它把 RTL 综合到真实可编程逻辑，速度接近或超过 ASIC，但可观测性受限于管脚数量与 ILA 带宽，且每次修改都要重新布局布线。\par
\chapter{极简 RISC-V 虚拟 SoC 实践}
以一个教学用 32 位 RISC-V SoC 为例，先定义需求：实现 RV32I 指令集，集成 UART、GPIO 及 64 KiB 片内 SRAM。CPU 采用经典三级流水线：取指、译码、执行。取指阶段从程序计数器读取 32 位指令字；译码阶段把操作码、寄存器索引、立即数拆分；执行阶段完成 ALU 运算或访存地址计算。流水线寄存器在时钟上升沿更新，保证组合逻辑在同一周期内完成。\par
总线采用精简 Wishbone 协议。主设备把地址、数据、写使能打包成事务，从设备在同一周期内给出应答或等待信号。地址译码器把 0x0000\_{}0000 – 0x0000\_{}FFFF 映射到 SRAM，0x1000\_{}0000 映射到 UART，0x2000\_{}0000 映射到 GPIO。中断控制器把 UART 的 tx\_{}empty、rx\_{}full 以及 GPIO 的边沿检测汇总成一根线，接入 CPU 的 mip 寄存器。\par
代码层面，\texttt{cpu.cpp} 负责指令执行循环与流水线寄存器更新；\texttt{bus.cpp} 实现地址路由与延迟模型；\texttt{uart.cpp} 维护发送/接收 FIFO 并在写 THR 寄存器时触发中断；\texttt{main.cpp} 负责 ELF 加载、命令行参数解析与 GDB remote stub。GDB stub 把虚拟 CPU 的寄存器与内存映射为调试目标，支持断点、单步与内存窥探，让固件调试体验与真实芯片一致。\par
在 \texttt{cpu.cpp} 中，核心循环可写为：\par
\begin{lstlisting}[language=cpp]
while (true) {
    uint32_t inst = bus.read(pc);
    Decoded d = decode(inst);
    execute(d);
    if (interrupt_pending) {
        handle_trap();
    }
    pc = next_pc;
    cycle++;
}
\end{lstlisting}
这段代码首先通过总线读取当前 PC 指向的指令；decode 函数把操作码、rs1、rs2、rd、imm 等字段拆分；execute 阶段根据 d.op 执行加法、访存或分支；若有中断挂起则进入异常处理流程；最后更新 PC 与全局周期计数器。整个循环每迭代一次对应一个时钟周期，从而实现指令精确仿真。\par
\chapter{应用场景与行业实践}
嵌入式固件开发正在从「拿到板子再写代码」转向「先在虚拟平台跑通再上板」。当 SoC 仍在 RTL 阶段，BSP 与驱动即可在虚拟 UART 上打印日志、跑通协议栈，把流片风险前移数月。高校「一生一芯」与 CSAPP 实验也大量采用 QEMU 或自研教学 SoC，让学生在虚拟环境中完成从单周期 CPU 到带 Cache 与中断的完整系统。\par
安全领域则把虚拟硬件当作漏洞放大镜。侧信道攻击建模可在仿真器里精确统计每条指令的功耗轨迹；Rowhammer 攻击可通过修改 DRAM 模型的刷新间隔来复现；模糊测试框架可对虚拟网卡驱动注入畸形数据包，把真实硬件的不可控因素转化为可重复的测试用例。云上 FPGA 租赁进一步把虚拟硬件变成服务：用户在网页上拖拽 IP 核，数分钟后即可获得一个可远程调试的实例，极大降低了芯片验证门槛。\par
\chapter{挑战与优化手段}
仿真速度始终是瓶颈。热点代码往往集中在 Bootloader 与中断处理路径，动态二进制翻译把这些基本块翻译为宿主机指令并缓存；DPI 接口允许把耗时模型（如浮点单元）卸载到 GPU；并行加速则把不同外设模型分配到不同线程，通过无锁队列交换事务。精度与速度的平衡需要量化指标：指令吞吐量（MIPS）、时钟周期误差（Cycle Error）与功耗估计误差（Power Error）共同决定模型是否可用。\par
可验证性依赖断言与覆盖率。UVM 框架把断言封装为 sequence 与 virtual sequence，在仿真结束时给出功能覆盖率报告；SystemVerilog 的 assert property 可直接嵌入 RTL，也可被移植到 C++ 模型中。生态碎片化则通过 IP-XACT 与 TLM-2.0 缓解：前者用 XML 描述寄存器与端口，后者定义事务级套接字标准，使得不同厂商的模型可以即插即用。\par
\chapter{未来展望}
AI 正在改变建模方式。图神经网络可从波形数据中自动推断时序参数；强化学习能搜索最优 Cache 替换策略；大语言模型甚至能把自然语言规格书直接翻译成 SystemC 模型。数字孪生把芯片、整机与数据中心连成一体：温度传感器数据实时回灌到热力学模型，功耗曲线又驱动电源策略，形成闭环优化。形式化验证与混合精度仿真也在融合：符号执行可穷举关键路径，近似模型则在非关键路径上加速，共同把「先仿真后流片」的理念推向极致。\par
QEMU、Renode、TinyEMU、riscvOVPsim 等开源项目为不同精度需求提供了现成选择。从单周期 CPU 开始，逐步加入流水线、Cache 与中断控制器，动手路径清晰可见。软件即硬件，想象力即芯片——当虚拟平台把时序、功耗与热力学全部纳入可编程空间，下一代芯片的边界将由代码而非硅片决定。\par
\chapter{延伸阅读}
推荐书目包括《Computer Architecture: A Quantitative Approach》与《SystemC: From the Ground Up》；论文可参考「A Full-System Simulation Framework for RISC-V」与「gem5-X: A Gem5-Based System Level Simulation Framework」。文中最小可运行代码仓库位于 GitHub 的 riscv-virtual-soc 组织，包含单文件 Makefile 与 Docker 一键环境，读者可立即 clone 并在本地启动 GDB 调试会话。\par
