基于QEMU的RISC-V AI芯片虚拟实验台搭建与Agent自动化
发布时间:2026/10/29 10:13:45 锦皓数字建站

做嵌入式或者CPU方向的朋友应该都有过这种经历芯片还在设计验证阶段软件却已经要动工了。最近我在折腾手头一个AI算法往RISC-V架构上迁移的活儿手边没有对应开发板也不可能每一代芯片都买一块回来等着最后落地的方案就是用QEMU拉起一块RISC-V AI芯片的“虚拟实验台”再配合现在很流行的AI Agent来加速环境搭建和问题排查。这篇就把我整个搭建过程、踩过的坑、还有哪些地方值得自动化完整梳理一遍。不管你是做编译器移植、内核验证还是想在RISC-V上跑PyTorch算子验证这套流程都能帮你省掉不少弯路。1. 为什么要把RISC-V AI芯片实验台搬进虚拟机1.1 QEMU到底在模拟什么先明确一个概念QEMU是一个纯软件模拟器它模拟的不是某个“操作系统”而是完整的一台机器——CPU指令集、内存控制器、中断控制器、UART串口、virtio磁盘和网络设备全部用软件翻译的方式跑在宿主机上。你可以把它理解成一台“没有实体电路但能通电的虚拟开发板”照着芯片的指令集手册和数据手册在软件里实现了一个行为等价的硬件平台。对于AI芯片实验来说这一点格外重要。很多AI加速器芯片内部其实是“RISC-V核心专用NPU/向量单元”的组合NPU部分QEMU通常不会精确模拟但RISC-V主控CPU部分、内存子系统、外设总线、中断控制器这些都是标准化的QEMU的virt机器类型支持得很完整。我这次的目标是把AI算法里的向量算子、矩阵运算库、调度逻辑在RISC-V指令集上先验证跑通而不是去做性能摸底——性能摸底那是以后真芯片回来之后的事。1.2 为什么偏选RISC-V做AI芯片实验AI芯片选RISC-V作为主控或者协处理核心在行业里已经很常见了。原因不难理解RISC-V指令集开放、模块化厂商可以根据AI负载自由加减指令扩展。尤其是向量扩展RVV一套寄存器可以同时操作多条数据做矩阵乘法、卷积这类算子时代码密度和理论上限都很合适。但开放指令集带来的另一个问题就是生态碎片明显。不同厂商的SoC可能选了不同的扩展组合有的带V扩展有的只带M扩展缺了某个扩展代码怎么优雅降级这些都需要提前验证。QEMU的好处是它允许我在命令行里直接指定CPU特性比如打开vtrue启用向量扩展然后用一套软件栈测试不同配置下的兼容性。这在真实板卡上反而很难做到因为板卡CPU特性出厂就定死了。你可以把QEMU当成一枚“可以随时改芯片配置的软芯片”这一特性在做AI算子移植时特别顺手。1.3 AGENT在这里扮演什么角色环境搭建这个事表面看只是几条命令实际做起来要反复处理依赖缺失、启动参数写错、内核挂载不了根文件系统之类的问题。每报一次错就得去查文档、看日志、改参数、重跑。这些操作重复度高、规律性强非常适合交给AI Agent去辅助。我这里的Agent指的是现在常见的命令行智能体、编程助手或者自动化任务执行器它能理解自然语言描述、生成命令、执行命令、读取回显再根据错误信息自我修正。你可以把它想象成一个“能读日志、能改脚本、能反复执行命令的实习生”有明确任务边界的时候比人工手动试错快得多。但注意它不是万能的关键决策还是要人来把控——毕竟它不懂你的项目背景也不理解哪些内核参数动了会产生潜在风险。所以我给它设置的工作范围就限定在“为这个RISC-V虚拟机生成启动脚本、解析启动日志、检查依赖包”这几件事上。2. 搭建前先盘点宿主机与组件选型2.1 宿主机准备与依赖安装QEMU本身是跨平台的Linux、macOS、Windows都有对应的包。我这次用的是Ubuntu系的Linux宿主机原因比较实在RISC-V相关工具链和交叉编译环境在APT源里基本都有省去很多手动编译时间。先确认一下宿主机有没有装QEMU的RISC-V系统模拟器。不同发行版包名有差异Ubuntu/Debian下包含qemu-system-riscv64的包是qemu-system-misc另外顺手装一个qemu-user-binfmt这个包用于用户态模拟后面跑RISC-V交叉编译的可执行文件时会用到。sudo apt update sudo apt install qemu-system-misc qemu-user-binfmt qemu-system-riscv64 --version版本信息能正常打印出来就可以继续了。这里多说一句很多人分不清qemu-system-riscv64和qemu-riscv64的区别前者模拟完整机器可以启动内核后者只模拟用户态只能跑单个可执行文件。这次搭建实验台我们需要的是前者。2.2 三种镜像路线怎么选启动一台RISC-V虚拟机本质上需要三样东西固件、内核、根文件系统。固件通常用OpenSBI负责机器模式M-mode的初始化内核用编译好的Linux RISC-V内核镜像根文件系统用Buildroot生成的rootfs.ext4或者发行版预制的rootfs.img。新手第一反应可能是“我自己用Buildroot编一个”但我建议第一次先别急着自己编。Buildroot全量编译RISC-V工具链加内核在普通机器上要跑一两个小时期间还会遇到各种网络和版本问题。更稳妥的路线是先下载别人打包好的预制镜像把虚拟机跑起来熟悉启动流程之后再回头自己编内核和rootfs。我实际用的就是Fedora/RISC-V的预制镜像加OpenSBI固件两个文件加起来也就几百MB下载路径很容易找到。第二个选择是Ubuntu的RISC-V版起步更顺包管理器成熟后期装Python、PyTorch都比Buildroot的busybox环境方便得多。2.3 搞清楚各组件版本的匹配关系环境搭建里最隐蔽的坑其实是版本匹配。QEMU、OpenSBI、Linux内核这三个项目都在快速迭代不是随便拿一个OpenSBI配一个内核就能跑起来。我遇到过一次内核启动到一半卡在init阶段最后换了一个更新版本的OpenSBI固件就正常了。原因是旧固件没有正确配置好某个CSR控制状态寄存器导致内核访问到预期之外的值。所以建议你在搭建的时候记录三样东西QEMU版本号、OpenSBI版本、内核版本。低于2023年版本的QEMU对部分RISC-V扩展支持不完整如果你的目标是AI向量算子实验尽量用较新的QEMU版本确保支持RVV 1.0规范。这里可以用一张小表把方案定下来组件我的选型说明宿主机Ubuntu 22.04 / 24.04包齐全社区资料多QEMU8.0以上对RVV和virt设备支持较好固件OpenSBI较新release避免旧固件启动内核失败内核6.x内核RISC-V支持成熟根文件系统Fedora/RISC-V预制镜像方便包管理器顺手这个组合本身不是唯一解但它是兼容性风险最低、参考资料最多的组合。3. 从零拉起RISC-V虚拟机的完整实操3.1 最小启动命令逐段拆解先把最小可用的启动命令写出来这条命令我会一直用后面所有排错都从它开始。命令看起来很绕实际每个参数都有明确作用qemu-system-riscv64 \ -machine virt \ -cpu rv64,vtrue \ -smp 4 \ -m 8G \ -bios default \ -kernel Image \ -drive filerootfs.img,formatraw,idhd0 \ -device virtio-blk-device,drivehd0 \ -append root/dev/vda rw consolettyS0 \ -netdev user,idnet0,hostfwdtcp::2222-:22 \ -device virtio-net-device,netdevnet0 \ -nographic逐段解释一下-machine virt指定QEMU模拟的机器类型。RISC-V QEMU里最常用的是virt它统一定义了中断控制器、UART、PCIe总线类似ARM平台的virt机型兼容性最好。-cpu rv64,vtrue指定CPU架构为RV64并开启向量扩展。这个vtrue对AI实验是关键参数后面跑向量算子就靠它。-smp 4 -m 8G4核CPU8G内存。RISC-V TCG模式是纯软件模拟核数和内存不要盲目开高否则宿主机容易卡死8G内存对大多数算子验证已经够用。-bios default使用QEMU内置的OpenSBI固件自动完成M-mode初始化。-kernel Image指定Linux内核镜像路径。-drive filerootfs.img,formatraw,idhd0挂载rootfs作为磁盘formatraw直接标注为裸镜像避免QEMU去探测格式导致歧义。-device virtio-blk-device,drivehd0把磁盘挂到virtio-blk总线上速度快驱动支持好。-append root/dev/vda rw consolettyS0把内核启动参数传过去告诉内核根设备是/dev/vda控制台用串口。-netdev user,idnet0,hostfwdtcp::2222-:22配置用户模式网络并把宿主机的2222端口转发到虚拟机里的22端口后续SSH登录用。-nographic不用图形窗口串口直接映射到当前终端。3.2 启动日志应该长什么样执行上面命令后终端会陆续打印两类日志。先是OpenSBI的banner包含版本号和平台信息然后进入Linux内核启动日志出现[ 0.000000] Linux version 6.x.0 ...这样的内核版本行再往下可以看到内存布局、设备树、SMP CPU核数等初始化信息。启动到最后的标志性输出是Welcome to Fedora RISC-V ... [ OK ] Reached target Multi-User System.看到login提示符说明整个虚拟开发板已经正常起来了。如果卡在前面某一步后面会让你头疼但大部分问题都能从日志里定位。3.3 网络和SSH登录配置虚拟机起来了之后直接面对黑黢黢的串口终端操作并不舒服。我的建议是顺手把SSH配好之后就可以像连普通服务器一样操作。虚拟机里面先做这几步# 查看网卡是否已经获取到地址 ip addr # 如果没有地址使用DHCP客户端获取 sudo dhclient eth0 # 安装并启动SSH服务 sudo dnf install -y openssh-server sudo systemctl enable --now sshd随后在宿主机上因为启动命令里已经有hostfwdtcp::2222-:22直接ssh -p 2222 riscvlocalhost这就是QEMU用户模式网络最方便的地方不需要配置复杂的网络桥接就能从宿主机访问虚拟机内部服务。网络不通时优先检查-netdev参数有没有写错以及在虚拟机里ip addr是否看到eth0。3.4 为AI实验额外预留的几个QEMU参数上面那条是最小启动配置。一旦你确定AI实验方向还可以加上这些参数提升体验-rtc baseutc \ -no-reboot \ -fsdev local,security_modenone,idfsdev0,path/host_share \ -device virtio-9p-device,fsdevfsdev0,mount_taghostshare-no-reboot是排查问题时特别好用的参数内核panic后QEMU不会自动重启而是停在崩溃现场等你观察。-fsdev配置9p共享目录把宿主机的一个目录直接共享进虚拟机做代码同步和日志提取非常方便。挂载方式是在虚拟机内部执行sudo mount -t 9p -o transvirtio hostshare /mnt这样你在宿主上编辑代码虚拟机里直接从/mnt读取不需要来回scp。我后续所有算子实验都是在这种共享目录下完成的。4. 用AGENT把环境搭建串成自动化流程4.1 环境搭建为什么适合交给Agent环境搭建过程里最多的时间不是花在敲命令上而是花在“试错-看日志-改参数-再试”这个循环里。每一次报错人工处理需要理解错误信息、定位组件、查资料、修正命令但这类问题的解决模式高度重复AI Agent恰恰擅长这种模式。举个实际例子。我最初写启动命令时把内核启动参数写成root/dev/sda结果启动后一直报VFS: Cannot open root device sda。我让Agent基于日志排查它很快指出virtio-blk设备在RISC-V virt平台上通常注册为/dev/vda改掉之后系统直接起来了。这个判断本身不复杂但省掉了读文档和试错的时间。更复杂的场景是内核模块依赖。如果你之后要加一个特定的virtio驱动模块Agent可以从内核源码和Kconfig里把依赖关系梳理出来生成一份补丁式的配置说明。这些都属于“信息检索命令执行”的强项。4.2 一个可复用的Agent工作流设计为了不把Agent用成“一次性问答”我有意把整个环境搭建过程拆成了几个固定阶段每个阶段都有明确的输入输出目标定义告诉Agent最终要得到什么。比如“在QEMU virt平台上启动Fedora RISC-V内核带RVV支持”。环境探测Agent先在宿主机执行uname -m、qemu-system-riscv64 --version等命令确认环境基础。脚本生成Agent根据探测结果生成启动脚本脚本里带参数检查和日志输出重定向避免跑完命令不知道发生了什么。执行与验证Agent执行启动脚本比对输出中的关键标志Booting Linux、Reached target。错误迭代如果失败Agent抓取日志最后100行提炼可能的根因修改参数后重试最多尝试5次就停下来请示。这个流程你可以直接用各种命令行智能体工具的主流程来套核心思想是每步都有可验证的输出物而不是黑盒跑一条命令就结束。4.3 幂等性反复跑脚本不搞出脏状态环境搭建脚本最忌讳的是“跑一次能用跑第二次就坏”。我在设计启动脚本时引入了状态管理逻辑用一个STATE_DIR目录记录下载是否完成用锁文件避免并发冲突。简化版如下#!/usr/bin/env bash STATE_DIR${STATE_DIR:-/srv/riscv_lab/state} mkdir -p $STATE_DIR if [ ! -f $STATE_DIR/Image.done ]; then echo 下载内核镜像... # 下载完成后创建标记文件 touch $STATE_DIR/Image.done fi if [ ! -f $STATE_DIR/rootfs.done ]; then echo 下载rootfs... touch $STATE_DIR/rootfs.done fi exec qemu-system-riscv64 \ -machine virt \ -cpu rv64,vtrue \ -smp 4 \ -m 8G \ -bios default \ -kernel $STATE_DIR/Image \ ...这里的细节是用“标记文件”而不用“文件是否存在”来判断因为镜像文件可能下载了一半。标记文件的存在意味着这个文件已经完整落盘不会每次重跑都重新下载。这个习惯是从构建系统里学来的用在环境搭建脚本里同样顺手。4.4 Agent操作的安全边界不能省让Agent主动执行命令安全边界一定要提前设好。我的习惯是三条硬性约束只允许Agent在当前项目目录和STATE_DIR内写文件不允许读取或修改系统级目录。所有需要sudo的命令必须经过我确认。不运行任何从不明来源自动下载的二进制脚本下载动作只允许通过命令行包管理器或明确的官方链接。这些约束看起来繁琐但在自动化流程中能避免很多意外。毕竟Agent的“意图理解”能力再强也无法替你判断一个未知脚本的副作用。你自己都不确定的东西别让它替你做决定。5. 进入系统后的AI环境配置从Python到PyTorch5.1 在RISC-V用户空间里装Python和PyTorch虚拟机起来之后最关心的就是AI软件栈能不能跑。在Fedora RISC-V镜像里Python 3环境是预装好的python3 --version pip3 --version如果还没装用包管理器安装即可。这里要注意一点RISC-V架构的pip源里不是所有包都有预编译wheel安装PyTorch时最稳妥的方式是检查是否提供了riscv64的wheel包或者用pip直接安装源码包让它在本地编译。实测下来源码编译PyTorch在QEMU环境下耗时非常长8G内存都显得紧张所以我更推荐先用现成的rpm包把环境跑通再考虑针对特定算子做源码级编译优化。一个比较轻量的落地做法先在虚拟机里创建一个虚拟环境然后用PyTorch的CPU版本做矩阵运算验证。因为QEMU模拟的是纯CPU环境GPU相关的CUDA逻辑用不上所以CPU版是更合适的选择。5.2 跑一个最小算子验证进入Python环境后我通常会跑一个最朴素的矩阵乘法脚本先验证PyTorch的RISC-V后端是否工作正常import torch x torch.randn(32, 32, dtypetorch.float32) y torch.randn(32, 32, dtypetorch.float32) z torch.mm(x, y) print(z.shape) print(z[0, :3])输出能打印出torch.Size([32, 32])和数值说明PyTorch在RISC-V虚拟机上已经完成了最基本的张量运算链路。接着可以试试更接近AI负载的操作比如对某个张量做卷积、softmax、归一化import torch.nn.functional as F input_t torch.randn(1, 8, 16, 16) weight_t torch.randn(4, 8, 3, 3) out F.conv2d(input_t, weight_t, padding1) print(out.shape)这类算子跑起来很慢但能验证算子实现的正确性对算法迁移前排查逻辑问题已经足够。就像用模拟器练手你关心的是能不能跑对不指望它飞快。5.3 向量扩展能给AI算子带来什么前面启动时给CPU加了vtrue这是为后续做向量算子实验预留的。RISC-V的V扩展允许一条指令同时处理多个数据元素类似SIMDAI算子里的逐元素运算activation、normalization和矩阵分块运算都可以受益。验证向量扩展是否生效可以从/proc/cpuinfo看到标志位cat /proc/cpuinfo | grep isa # 如果看到 rv64imafdcv 这样的字符串说明V扩展已经在当前核上开启了字符串末尾的v就是向量扩展标记。有了它后续无论是自己写汇编测试向量指令还是编译带auto-vectorization的C代码都有基础支撑。不过要说明的是QEMU的向量性能不能代表真实芯片它的意义在于让你提前适配指令行为和编译选项。5.4 与真实开发板的差异要心里有数用QEMU做AI实验本质上是在“指令行为一致时序性忽略不计”的前提下运作。以下几点差异建议早点认清性能QEMU TCG模式是动态二进制翻译比真实硬件慢几个数量级矩阵比较大的训练任务不要指望现场出结果。时序外设的中断时序、DMA行为都只是近似涉及实时性的代码不适合在这里验证。NPUQEMU一般不模拟专用NPU指令AI加速器厂商的私有指令集需要真实芯片或厂商自研模拟器。功耗没有物理芯片功耗评估为零。但它也有真实开发板比不了的价值虚拟硬件配置可以在命令行里随意改动同一个测试可以把CPU核数、内存大小、扩展开关都切换一遍这对软件栈的健壮性验证比收几块固定配置的开发板高效得多。CI系统里也可以天天跑RISC-V回归测试成本几乎为零。6. 常见问题与排查技巧实录6.1 高频问题速查表这一路踩过不少坑整理成表格方便你对症处理现象可能原因解决办法启动无任何输出缺少-nographic或consolettyS0加上-nographic内核参数里加consolettyS0卡在Starting kernel ...OpenSBI与内核版本不匹配升级OpenSBI或换新版本QEMU报错VFS: Cannot open root device根设备名写错核实virtio磁盘注册名一般是/dev/vdarootfs挂载成功但init崩溃rootfs架构不对或格式错误确认rootfs是riscv64架构格式为rawSSH连接不通hostfwd端口冲突或虚拟机没装sshd改宿主端口确认虚拟机内sshd状态网络通了但DNS解析失败user模式网络的DNS配置问题在虚拟机里手动配/etc/resolv.conf编译Python/PyTorch时OOM内存分配过小提升-m或者关闭部分并行编译任务启动脚本第二次跑异常有残留镜像或端口占用清理STATE_DIR检查2222端口占用6.2 帮你省时间的几个排查技巧第一个技巧是日志分级。启动失败时别直接看完整串口输出先抓最后100行里面通常藏着真正的报错。系统起来之后journalctl -b能看内核和服务的详细日志定位问题比盲目改参数快得多。第二个技巧是给QEMU加-no-reboot。内核一旦panic默认行为是重启然后继续panic信息一闪而过。关掉自动重启后崩溃现场就停在终端上可以慢慢分析。第三个技巧是用9p共享目录做日志中转。虚拟机的日志写到共享目录宿主机直接查看两边都不用搭额外服务。调试SSH这类涉及网络的问题尤其好用。第四个技巧是不要一次开满资源。如果你用-m 16G -smp 8启动TCG模式下CPU占用会直接拉满宿主机其他工作别想做了。先开-smp 4 -m 4G把流程跑通再根据实际需要加资源。6.3 关于Agent辅助排错的边界Agent在处理这些常见问题时确实高效但我发现它在两类场景会翻车一是需要结合项目历史的特殊取舍时它可能给出标准答案但不适配你的环境二是它容易“自信地”给出一个看似合理但实际没人验证过的参数组合。所以凡是Agent给出的关键参数特别是内核启动参数、设备树相关配置我都会自己查一遍手册再采用。这也回到前面说的Agent做助理核心决策还是自己做。把它定位成一个随叫随到、检索能力强的技术助手环境搭建效率能提升不少但别把整台虚拟机的命运完全交给它做主。6.4 一个小技巧把启动参数收拢成配置文件最后分享一个我实际用得很舒服的习惯。不要每次手动敲一长串QEMU参数而是写一个lab.env文件把CPU核数、内存大小、端口转发、内核路径这些全部做成变量启动脚本只负责读取变量并拼装命令。以后想调整配置改一行文件即可不用动脚本逻辑也不会在多次实验之间留下“上一次乱改的参数”。这样一来整套环境就是一个可以随时重建、记录清晰、自动化友好的实验台。后续不管是接CI回归还是换一套rootfs测试不同发行版改动成本都非常小。AI实验台本来就是用来反复折腾的底子打顺了后面每一步都省心。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。