前阵子我需要一块 RISC-V AI 芯片的实验台。芯片工程样片还在工厂里软件栈又不能等于是我把目标转向了 QEMU在一台普通的 x86 主机上用纯软件模拟拉起一块 RISC-V 64 位开发板把 Linux、PyTorch、RVV 向量扩展环境全部跑通。和以前不一样的是这次环境搭建不是我一条命令一条命令手敲的整个过程由 AGENT 主导它负责拆任务、查资料、生成脚本、分析启动日志我只在最关键的架构选择上做最终确认。写这篇内容是想把整套“Agent QEMU RISC-V AI软件栈”的搭法记录下来给准备做 RISC-V 软件预研、内核驱动开发或者 AI 推理栈移植的朋友一个可直接复用的参考。我不会把每个细节都讲成教科书重点放在三个地方为什么用 QEMU 而不是等真板子、Agent 在这个过程里到底能承担什么、以及真正跑通一块 AI 实验台会遇到哪些坑。1. 先搞清楚这块实验台到底要模拟什么动手之前务必先想清楚一件事QEMU 到底是用来“替身”哪一块硬件的。如果你连这个都没想好后面整个环境搭出来也会四不像。1.1 RISC-V AI 芯片QEMU 能模拟到哪一层RISC-V 和 x86 不太一样它不是一个具体 CPU而是一套指令集架构下面还有一系列可选扩展。AI 芯片通常关注的是向量计算能力也就是 RISC-V 的 V 扩展RVV以及 AI 推理时常会用到的各种自定义指令。QEMU 作为一个指令级模拟器可以在-cpu参数里把这些扩展打开让 guest 里的代码真正执行到 RVV 指令比如vadd.vv、vfmacc.vv这样的向量运算。这是最接近“AI 芯片计算核心”的模拟层级。QEMU 也能承担平台级模拟。virt机器是一块虚拟开发板包含 UART、PCI 总线、virtio 外设等。你能在上面跑完整 Linux 内核也能通过-device挂载额外的模拟设备。如果你有一颗自研的 NPU想把它的寄存器模型做成 QEMU 设备再在 guest 里写驱动访问它这条路也是通的。还有一层是软件栈级模拟也就是不关心底层是不是真的仿了 NPU只要 guest 里能跑 PyTorch、ONNX Runtime 这类 AI 软件验证它们在 riscv64 上能否编译、能否运行就达到了目的。我这次选择的是“指令级 软件栈”组合一方面用-cpu rv64,vtrue验证 RVV 代码另一方面在 guest 里搭 Python AI 推理环境。至于具体 NPU 的寄存器模型后面再慢慢加。这样做的好处是周期短、可控性强坏处是没办法评估真实芯片的性能因为 QEMU 的 TCG 翻译执行比真实硬件慢很多跑出来的性能数据没有参考意义。1.2 为什么不等真板子非要折腾 QEMU很多人的第一反应是去买一块 RISC-V 开发板不就行了吗如果只是玩一玩确实可以。但放到项目里真板子有几个绕不开的麻烦。第一货期不可控。芯片厂商给的评估板数量有限经常要排队有时候软件团队要提前三个月开始干活。第二成本不可控。一块带 NPU 的 RISC-V 板子价格不低而 QEMU 只需要一台能跑 x86 的普通电脑零硬件成本。第三CI/CD 需求。我想在每次代码提交后自动跑一遍 RISC-V 的软件测试真板子只有一两块没法并行跑多实例QEMU 则可以同时开四五个虚拟机每个测试一个干净环境。QEMU 还有一点被很多人忽略它可以模拟“还没存在的芯片”。芯片流片之前软件开发必须先行这时候 QEMU 是唯一能让你提前接触指令集和平台模型的工具。你可以在 QEMU 上先把内核驱动写好、把 AI 推理框架移植好等芯片回来直接跑。当然也有代价。RISC-V guest 在 x86 宿主机上不能走 KVM 加速只能靠 TCG 动态翻译CPU 密集型任务会明显变慢。如果你要测真实推理延迟、吞吐量那还是得等真板子。我的建议是软件栈验证、功能调试、内核开发用 QEMU性能测试和功耗测试等硬件。2. AGENT 链路设计怎么写任务书怎么防止它乱来这次环境搭建最特别的一点是我把大量工作交给了 AGENT。很多人用 AI 工具只是问一句“怎么写命令”然后自己复制粘贴。真正像样的做法是把 AGENT 当成一个会自己动手的实习生给它明确任务、明确边界、明确验收标准。2.1 任务书不只是几句 Prompt我建了一个工作目录在里面放了一个TASK.md内容不是随便几句话而是带验收条件的任务书。# TASK.md 目标在宿主机上用 QEMU 拉起 RISC-V 64 位 Linux并在 guest 里安装可用的 AI 推理环境。 环境约束 - 操作系统宿主机为 Ubuntu 22.04 - 工作目录/home/user/riscv-lab - 不允许修改宿主机全局配置 - 所有产物输出到 /home/user/riscv-lab/build 执行步骤 1. 检查 qemu-system-riscv64 是否存在不存在则用 apt 安装 qemu-system-misc 2. 获取 Ubuntu riscv64 cloud image 或可替代 rootfs 3. 编写并执行启动脚本 qemu-run.sh 4. 确认 guest 能通过 SSH 访问 5. 在 guest 中安装 python3-venv给出 PyTorch CPU 版本的安装方案 6. 输出启动脚本、运行日志、每步执行记录 验收标准 - 启动脚本可以直接拉起到串口控制台 - SSH 端口映射可用 - guest 内能执行 python3 -V - 记录失败的步骤和原因这份任务书看起来简单但比普通 Prompt 多出了“约束”和“验收”两部分。约束是告诉 AGENT 什么不能碰验收是保证它不会只在表面上完成一半就交差。我在后面还会强调一点AGENT 每一步产生的日志都要落盘毕竟它跑完就忘了日志是我事后复盘和回滚的依据。很多人在用 AGENT 的时候会纠结该选哪种 agent 框架。其实没有标准答案。对我来说关键是这个 AGENT 有没有“执行命令”的能力而不是停留在聊天界面上。所谓 harness就是让 AGENT 调用工具时走权限检查的壳技能 skill 则是一些可复用的套路比如“QEMU 启动参数模板”。如果你用现成的 Agent 框架选支持终端操作的如果你是技术出身也可以自己写一个循环本质上就是“模型决定下一步动作 - 执行命令 - 把输出反馈给模型”。2.2 AGENT 自动完成的部分和我人工确认的关键点整个流程里AGENT 自动完成的事情包括检查 QEMU 是否安装、搜索 Ubuntu riscv64 镜像下载地址、生成启动脚本、分析启动日志并尝试修复、给出 guest 内 Python 环境的安装步骤。这些事如果我自己做大概要一到两个小时AGENT 十几分钟就跑完了。但我没有把所有决策都交给它。有几个点是我人工确认后才让它继续的。一个是机器选型。AGENT 一开始用的方案是-machine sifive_u因为网上很多旧教程都是拿这块板子举例。但我想用virt原因是virt 是纯虚拟平台virtio 设备支持更完整后续要挂自定义设备模型也更容易。这个选择直接影响了整个环境的基础。另一个是网络模式。AGENT 默认建议用-netdev user我认可。但如果未来想用固定 IP、多台虚拟机互相通信就需要改成tap0桥接模式这涉及宿主机网桥配置权限要求高不能随便让 AGENT 自动改。还有一个是 rootfs 选择。它最初想下载完整的 Ubuntu cloud image我确认了这个方向没问题但提醒它先检查镜像里是否自带内核和引导避免启动时还要自己去拼 OpenSBI、内核、initrd。事实证明这个检查很关键后面省了很多事。2.3 权限边界与回滚机制AGENT 能执行终端命令听起来很爽但也意味着它有破坏宿主机的风险。我的经验是尽量不让 AGENT 直接拿到 root 权限即使它说“需要 sudo”也要在任务书里写明必须停下来询问。如果一定要允许就在宿主机上创建一个低权限用户只给它操作工作目录的权限。另外工作目录本身也要设计成可丢弃的。我把所有下载的镜像、生成的 rootfs 都放在build/下万一搞坏了直接删掉重来。QEMU 的磁盘镜像我用的是 qcow2 格式它天然支持快照环境弄乱之后可以qemu-img snapshot -a一键回滚。这一点强烈建议你做到因为你让 AGENT 自由操作时它一定会踩坑能回滚比什么高级技巧都实在。3. QEMU 拉起 RISC-V 系统的关键实操现在进入物理层面。无论 AGENT 多聪明最终还是要落地成几个命令和文件。下面的操作我在 Ubuntu 22.04 上完整跑通过细节比较啰嗦但每一步都有原因。3.1 宿主机准备与镜像获取先把宿主机需要的软件装上。sudo apt update sudo apt install -y qemu-system-misc qemu-utils wget xz-utils检查 QEMU 是否装好qemu-system-riscv64 --version如果提示找不到命令可能是因为发行版里这个二进制被拆分了。Debian/Ubuntu 下qemu-system-misc包含 riscv64 模拟器装好后直接可用。下一步是获取镜像。我用的是 Ubuntu riscv64 cloud image这个镜像基于 qcow2 格式可以直接被 QEMU 作为磁盘使用不需要再转换。下载地址在 Ubuntu 官方 cdimage 站点选择release下对应版本的ubuntu-XX.XX-preinstalled-server-riscv64.img.xz。mkdir -p /home/user/riscv-lab/build cd /home/user/riscv-lab/build wget 镜像下载链接 xz -d ubuntu-*.img.xz解压后得到一个.img文件。注意这个文件的格式是 qcow2不要习惯性地把它当成 raw 镜像。用file命令确认一下file ubuntu-*.img输出里应该能看到QEMU QCOW2 Image (v3)字样。确认之后我建议先创建一块数据盘专门放模型权重和测试脚本。用 qcow2 的好处是宿主机上只占用实际写入的大小。qemu-img create -f qcow2 data.qcow2 20G3.2 启动命令逐行拆解下面这条命令是我最终跑通的先整体看一遍。qemu-system-riscv64 \ -machine virt \ -cpu rv64,vtrue \ -smp 4 \ -m 8G \ -drive fileubuntu-riscv64.img,formatqcow2,idhd0 \ -device virtio-blk-device,drivehd0 \ -drive filedata.qcow2,formatqcow2,idhd1 \ -device virtio-blk-device,drivehd1 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-device,netdevnet0 \ -nographic逐个解释参数不然 AGENT 很可能随机调参而你根本不知道哪里出了问题。-machine virt指定平台模型。RISC-V 下常见的是virt和sifive_u。我选 virt 的理由前面已经说过virtio 支持完整设备树规范社区生态都围绕它。-cpu rv64,vtrue开启 RVV 向量扩展。如果你后续想跑 RVV 向量代码这个参数必须有。QEMU 7.2 之后对 V 扩展的支持已经很成熟可以放心开。-smp 4开 4 个虚拟 CPU。-m 8G给 guest 8G 内存。因为 AI 推理有时会吃内存2G 太紧8G 比较宽松。如果你只是验证启动可以用-m 2G减少宿主机负载。-drive和-device virtio-blk-device是配套的。第一块盘装系统第二块盘是数据盘。这里有个容易踩的坑很多人只写-drive filexxx,formatqcow2忘了-device结果 guest 里根本看不到硬盘。在 RISC-V virt 机器上不能假设盘会自动接到某条总线上必须显式挂一个 virtio-blk-device。-netdev user,idnet0,hostfwdtcp::2222-:22是用户态网络。这种模式最简单guest 通过 QEMU 内置的 NAT 上网宿主机无需 root也不需要网桥。hostfwd把宿主机的 2222 端口转发到 guest 的 22 端口这样后面可以用 SSH 连接。如果你想要 guest 里的固定 IP或者希望 guest 能被局域网内其他机器直接访问就要换成tap0桥接但那种模式需要创建tap设备和网桥权限和网络拓扑都要规划我这次没选。-device virtio-net-device,netdevnet0是网络设备。在 virt 机器上一定要用virtio-net-device不要用 e1000因为 virt 平台默认没有 e1000 这种 PCI 网卡。很多 Agent 生成的脚本会照抄 x86 的参数拿到这里就起不来。-nographic把串口作为控制台直接在当前终端显示 guest 的输出。加上这个参数你就能在纯文本终端里控制整个虚拟机不需要图形桌面。3.3 从启动到 SSH 登录执行启动脚本后第一次会看到一个比较慢的启动过程因为 QEMU 正在用 TCG 翻译 guest 指令。几秒钟后应该能看到 OpenSBI 的 logo然后是内核启动日志最后进入登录提示。Ubuntu cloud image 默认用户名一般是ubuntu密码在镜像说明里会有通常安装时有提示。登录后建议马上改密码然后检查网络。ip addr show如果没问题会看到一个eth0或者enp0s1网卡IP 是 QEMU 用户态网络分配的 10.0.2.15。这个 IP 只有 guest 内部可见想要从宿主机进来还得走端口转发。从宿主机连ssh ubuntulocalhost -p 2222第一次连接会提示 host key 确认正常。如果连不上十有八九是 guest 里没启用 SSH 服务或者 cloud-init 还没完成初始化。等两分钟再试。为了方便传文件我习惯加一块 9p 共享目录。在启动命令里插入-virtfs local,path/home/user/riscv-lab/shared,mount_taghost0,security_modelnone,idhost0在 guest 里挂载sudo mkdir -p /mnt/shared sudo mount -t 9p -o transvirtio,version9p2000.L host0 /mnt/shared这块共享目录最初让我很惊讶因为 RISC-V 的 guest 内核必须编译进 9p 支持才能挂载。Ubuntu 官方内核默认带了所以能用。如果之后你自己编译内核记得把CONFIG_NET_9P_VIRTIO和CONFIG_9P_FS选上否则会报mount: /mnt/shared: unknown filesystem type 9p。这个 9p 目录是我传模型权重、测试脚本和日志的主要通道比 SSH 传文件方便得多尤其是大文件。4. Guest 里搭建 AI 运行环境系统起来了网络通了只是万里长征第一步。既然是“AI 芯片实验台”还得把 AI 运行环境搭出来。这里有很多和 x86 完全不同的坑AGENT 可以帮你生成步骤但几个现实问题必须提前知道。4.1 Python 与 PyTorch 在 RISC-V 上的安装现实进入 guest 后先确保 Python 环境可用。sudo apt update sudo apt install -y python3-venv python3-pip build-essential git mkdir -p /home/ubuntu/ai-lab cd /home/ubuntu/ai-lab python3 -m venv venv source venv/bin/activate pip install --upgrade pip接下来是重头戏装 PyTorch。坏消息是PyTorch 官方并不提供riscv64的预编译 wheel。你在pip install torch时pip 会在 PyPI 上找不到合适的包然后尝试下载源码包现场编译。CPU 版 PyTorch 在 RISC-V 上源码编译是可行的但耗时非常长我试过一次8G 内存 4 核的虚拟机里编了两三个小时还没编完而且很容易因为内存不足被 kill。如果你只是想验证推理逻辑而不是真的拿到一个可用的 torch 包我更推荐一条折中的路先用 NumPy 实现一个小型神经网络推理把整条数据通路跑通。等确认了真正的目标芯片和工具链之后再去编译 PyTorch。另外ONNX Runtime 对 RISC-V 的支持比 PyTorch 好一些有社区构建的包虽然不一定官方发布但成功率更高。下表是我的选择建议需求推荐方案原因快速验证推理流程NumPy 手动实现零编译几分钟内跑通跑标准 ONNX 模型ONNX Runtime 源码/社区包编译量比 PyTorch 小依赖轻完整 PyTorch 环境源码编译时间长、吃内存但要拿真实结果只能走这条验证 RVV 指令gcc 交叉编译汇编/C最贴近芯片真实计算核心如果你决定源码编译 PyTorch建议不要直接在 QEMU guest 里编。先在 x86 宿主机上把交叉编译工具链准备好生成 riscv64 的交叉编译环境再尝试交叉编译。这是很多人的经验能省掉 guest 里内存不足的问题。不过交叉编译 PyTorch 依赖项非常复杂涉及 OpenBLAS、protobuf、eigen 等坑比想象的深新手容易劝退。4.2 用一个小型推理程序验证实验台我最后在实验台上跑通的第一个正经 AI 程序是一个两层的 MLP用来做简单的二分类。为了不依赖 PyTorch我用 NumPy 手写前向传播。代码如下import numpy as np def sigmoid(x): return 1.0 / (1.0 np.exp(-x)) class MLP: def __init__(self): self.w1 np.random.randn(4, 16).astype(np.float32) self.b1 np.zeros((1, 16), dtypenp.float32) self.w2 np.random.randn(16, 2).astype(np.float32) self.b2 np.zeros((1, 2), dtypenp.float32) def forward(self, x): h sigmoid(x self.w1 self.b1) out sigmoid(h self.w2 self.b2) return out model MLP() x np.random.randn(1, 4).astype(np.float32) print(model.forward(x))在 RISC-V guest 里执行python3 inference.py能看到输出一个形状为(1, 2)的数组说明 Python、NumPy、CPU 浮点运算链路是通的。这个测试虽然简单但对实验台来说意义重大它证明了你可以在 RISC-V 虚拟硬件上跑数据流后续把 NumPy 换成 PyTorch 或者 ONNX Runtime只是换一个算子库的事数据通路不用改。如果你想更贴近“AI 芯片”的计算核心可以用 RISC-V 工具链写一个使用向量扩展的程序。在 guest 里安装 gccsudo apt install -y gcc写一个最简单的向量加法#include riscv_vector.h #include stdio.h int main() { int n 16; float a[16], b[16], c[16]; for (int i 0; i n; i) { a[i] i; b[i] i * 2; } for (int i 0; i n; i) c[i] a[i] b[i]; for (int i 0; i n; i) printf(%f , c[i]); return 0; }用-marchrv64gcv编译gcc -O2 -marchrv64gcv -o vadd vadd.c ./vadd如果程序正常输出说明 QEMU 的 V 扩展真的在工作。你可以在这基础上逐步加向量化矩阵乘、卷积等算子这就是一个最原始的 AI 芯片计算单元实验台。4.3 与真实 AI 芯片相关的驱动和虚拟设备模拟如果想更进一步模拟一颗具体的 AI 芯片QEMU 也支持你自定义设备模型把 NPU 想象成一个挂在 PCIe 上的加速卡guest 里写驱动通过 MMIO 访问寄存器。QEMU 提供了比较完整的设备建模接口你可以注册一个 PCIe 设备static void riscv_npu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc DEVICE_CLASS(klass); dc-desc RISC-V NPU accelerator; dc-realize riscv_npu_realize; dc-reset riscv_npu_reset; }这只是设备侧的骨架实际还要实现 PCIe 配置空间读写、BAR 空间映射、中断、DMA 等逻辑。guest 侧再写一个 Linux 内核模块注册一个platform_driver或者pci_driver通过ioremap访问寄存器。这套流程就是一个完整的“驱动先行”开发闭环芯片还没有驱动和用户态栈已经在 QEMU 上联调完了。不过我要提醒一句自定义 QEMU 设备模型涉及 QOM 和 PCIe 模拟的不少概念不要指望 AGENT 一次生成对。我建议先跑通最小 GPIO/UART 设备模型再往 AI 方向扩展。AGENT 在这里的价值是帮你生成代码骨架、查 QEMU 源码里现成设备模型的实现方法而不是凭空替你设计硬件逻辑。5. 常见问题与排查手记Agent 是主力排查员最后这部分是我最想分享的。环境搭建过程中遇到了一堆问题AGENT 作为主力排查员确实帮我省了很多时间。下面这些问题几乎都会遇到建议收藏。5.1 启动卡在 OpenSBI或者串口完全无输出现象执行启动命令后终端里只有 OpenSBI 的几行字然后一直卡住或者干脆什么都没显示。排查顺序第一确认你用了-nographic。如果没有串口输出不会直接打到终端上可能会去图形窗口而你如果在一个纯 SSH 会话里根本看不到。第二确认有没有给内核传正确的 console 参数。如果是 Ubuntu cloud image它通常会自己处理但如果你是自己拼内核和 rootfs需要在-append里加consolettyS0否则内核日志不会出现在串口。第三看分区。root/dev/vda是常见写法但如果你少加了-device virtio-blk-deviceguest 里没有/dev/vda内核就会在挂根文件系统时崩溃日志最后会停在VFS: Cannot open root device vda。第四如果卡在 OpenSBI 阶段多半是固件问题。Ubuntu 官方镜像自带引导不需要单独指定-bios。如果从网上找了旧教程加了奇怪的-bios参数反而可能冲突。遇到这种问题我一般直接把完整启动日志存到文件里丢给 AGENT 让它帮我定位是在哪个阶段卡住的。它比人眼看着更快的点在于能迅速搜索日志关键词比如Kernel panic、No working init found、Failed to mount。5.2 SSH 连不上guest 里能看到网络但宿主机过不去现象guest 里ip addr能看到10.0.2.15ping 外网也能通但宿主机上ssh ubuntulocalhost -p 2222就是连不上。原因大多是两个。一个是 guest 里的 SSH 服务没起来Ubuntu cloud image 默认会装 openssh-server但如果你用的是其他 rootfs可能需要手动安装并启动sudo systemctl enable ssh --now另一个是端口转发写错了。hostfwdtcp::2222-:22的语法是宿主机端口在前guest 端口在后。如果你写反成hostfwdtcp::22-:2222那宿主机的 22 端口会转发到 guest 的 2222 端口而 guest 的 SSH 在 22 上当然连不上。还有一种情况是 cloud-init 还没有完成。镜像第一次启动时会自动扩展根分区、配置网络、生成 SSH host key这个过程可能要一两分钟。如果你在启动后几十秒就去连可能服务还没就绪。耐心等一分钟后重试。5.3 GDB、快照与性能调试的实际经验QEMU 自带 GDB stub内核开发时可以在启动命令里加-s -S-S表示启动后暂停 CPU等待调试器连接-s表示在宿主机 1234 端口开 GDB 服务。然后用交叉版的 gdb 连上去gdb-multiarch vmlinux target remote :1234不过如果你只是想调试用户态程序没必要用 QEMU 的 GDB 接口直接在 guest 里gdb ./a.out就完了。QEMU 的 GDB 更多是给内核、引导代码用的。另外一个经验是QEMU 多核模拟有时候反而会比单核慢。TCG 模式下多核会有同步开销如果你跑的任务不是并行密集用-smp 1可能启动更快。如果你确实需要多核可以尝试-accel tcg,threadmulti这会启用多线程 TCG在宿主机是多核时能明显改善性能。但这属于锦上添花我在实验台上跑 NumPy 推理时单核也能接受。磁盘快照在这里是保命工具。qcow2 天然支持内部快照在 guest 里装好了一轮环境之后回到宿主机执行qemu-img snapshot -c healthy ubuntu-riscv64.img下次如果想恢复到干净状态qemu-img snapshot -a healthy ubuntu-riscv64.img这个方法比重新下载镜像快很多也避免 AGENT 自动操作时把环境改坏后无法回滚的痛苦。5.4 Agent 排查问题的一个通用套路如果让 AGENT 帮你排查我的做法是把现象、启动日志、配置文件粘贴给它然后明确告诉它“不要给我解释原理先给我下一步操作”。很多人让 AI 排查失败是因为问题描述太模糊只说了“连不上”没有任何客观信息。一份合格的排查信息长这样QEMU 启动后 guest 能登录宿主机 ssh localhost -p 2222 连不上。 日志显示 sshd 已启动宿主机的端口监听正常。 hostfwd 配置是 tcp::2222-:22。 请给我下一步的排查命令。AGENT 拿到这种输入会优先检查监听端口、防火墙、SSH 配置而不是漫无目的地展开长篇大论。最终它定位到的问题是guest 的 sshd 只监听了 IPv6而localhost解析成了127.0.0.1。这种问题人工看半天也未必能马上反应过来让 AGENT 过滤日志反而更快。写在最后这套实验台搭建下来我最深的体会是AGENT 不是用来替代你的判断力的它是帮你把脏活累活干掉的。环境搭建里大量工作其实是机械搜索、试错、翻日志这些 AGENT 做得又快又好。但哪些参数不能乱动、哪条路线会浪费时间、要在什么层级上模拟 AI 芯片这些决策最后还是得你自己想清楚。如果让我重来一次我不会让 AGENT 一上来就拉 Ubuntu 完整镜像而是会先让它用最小的 BusyBox rootfs 把 QEMU RISC-V 启动链路跑通再一步步加上网络、共享目录、Python。小系统更轻、问题更少排查起来也不容易被一堆 cloud-init 日志干扰。还有一个小技巧想分享现在这套环境里我会把每次修改 QEMU 启动参数的命令记录到一个history.md文件里顺手让 AGENT 在每次做完事之后追加一段变更说明。时间一长这个文件就是最好的项目文档比什么环境搭建笔记都管用。下一块真正带 NPU 设备模型的模拟器我就是在这个文件的基础上继续扩展的。