2. 开发环境搭建:QEMU、Spike 与 RISC-V GNU 工具链
做 RISC-V 向量开发,第一件事就是把环境搭好。我见过太多人卡在工具链编译上,一卡就是一整天。其实没那么玄乎,今天咱们就把 QEMU、Spike 和 GNU 工具链这三个核心组件捋清楚。
2.1 整体架构:你需要哪几样东西?
说白了,你要跑 RISC-V 向量程序,需要三样东西:
- 编译器:把 C/C++ 代码编译成 RISC-V 机器码
- 模拟器:在没有真实芯片时,模拟执行这些机器码
- 代理内核(PK):提供操作系统级别的服务,比如文件读写、内存管理
它们的关系是这样的:
2.2 安装 RISC-V GNU 工具链
工具链是基础。我个人习惯从源码编译,虽然慢一点,但能保证版本可控。你也可以用发行版自带的包管理器,但版本可能比较老。
2.2.1 从源码编译
# 克隆仓库
git clone --recursive https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain
# 配置(支持向量扩展)
./configure --prefix=/opt/riscv --with-arch=rv64gcv
# 编译(多核加速)
make -j$(nproc)
# 安装到 /opt/riscv
make install
这里 --with-arch=rv64gcv 是关键。g 代表通用扩展,c 是压缩指令,v 就是咱们要的向量扩展。少了 v,编译器不会生成向量指令。
2.2.2 验证安装
装完后,检查一下版本:
/opt/riscv/bin/riscv64-unknown-elf-gcc --version
/opt/riscv/bin/riscv64-unknown-elf-objdump -d # 反汇编用
如果能正常输出版本号,说明工具链装好了。接下来写个简单的向量程序测试一下:
// test_v.c
#include <riscv_vector.h>
#include <stdio.h>
int main() {
float a[4] = {1.0, 2.0, 3.0, 4.0};
float b[4] = {5.0, 6.0, 7.0, 8.0};
float c[4];
size_t vl = vsetvl_e32m1(4);
vfloat32m1_t va = vle32_v_f32m1(a, vl);
vfloat32m1_t vb = vle32_v_f32m1(b, vl);
vfloat32m1_t vc = vfadd_vv_f32m1(va, vb, vl);
vse32_v_f32m1(c, vc, vl);
for (int i = 0; i < 4; i++)
printf("c[%d] = %f\n", i, c[i]);
return 0;
}
# 编译
/opt/riscv/bin/riscv64-unknown-elf-gcc -O2 -march=rv64gcv -o test_v test_v.c
# 用 Spike 运行
spike pk test_v
输出应该是 c[0]=6.0, c[1]=8.0, c[2]=10.0, c[3]=12.0。嗯,向量加法跑通了。
2.3 配置 Spike 模拟器与 PK 代理内核
Spike 是 RISC-V 官方的指令集模拟器,轻量、精确。PK(Proxy Kernel)是个迷你操作系统内核,负责处理系统调用。
2.3.1 编译 Spike
git clone https://github.com/riscv-software-src/riscv-isa-sim
cd riscv-isa-sim
mkdir build && cd build
../configure --prefix=/opt/riscv
make -j$(nproc)
make install
2.3.2 编译 PK
git clone https://github.com/riscv-software-src/riscv-pk
cd riscv-pk
mkdir build && cd build
../configure --prefix=/opt/riscv --host=riscv64-unknown-elf
make -j$(nproc)
make install
--host 参数必须和你工具链的前缀一致。我见过有人用 riscv64-linux-gnu 编译 PK,但工具链是 riscv64-unknown-elf,结果链接时报错,折腾了半天。
2.3.3 Spike 的常用参数
| 参数 | 说明 | 示例 |
|---|---|---|
-p<n> |
模拟 n 个处理器核 | spike -p4 pk a.out |
-l |
记录指令日志 | spike -l pk a.out |
--isa=rv64gcv |
指定 ISA 扩展 | spike --isa=rv64gcv pk a.out |
-d |
进入交互式调试模式 | spike -d pk a.out |
我个人最常用 -l 来调试向量指令。它会打印每条指令的 PC、指令编码和寄存器状态,对理解向量指令的执行流程特别有帮助。
2.4 配置 QEMU 系统模拟
QEMU 能模拟完整的 RISC-V 系统,包括外设、中断控制器、定时器等。适合跑 Linux 内核和复杂的多进程程序。
2.4.1 编译 QEMU
git clone https://github.com/qemu/qemu
cd qemu
mkdir build && cd build
../configure --target-list=riscv64-softmmu --prefix=/opt/qemu
make -j$(nproc)
make install
--target-list=riscv64-softmmu 只编译 RISC-V 64 位的系统模拟器,省时间。如果你还要用户态模拟,可以加上 riscv64-linux-user。
2.4.2 启动 QEMU 运行 Linux
# 下载 OpenSBI 和 Linux 内核
wget https://github.com/riscv-software-src/opensbi/releases/...
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/...
# 启动
/opt/qemu/bin/qemu-system-riscv64 \
-machine virt \
-cpu rv64,v=true \
-m 2G \
-kernel opensbi.bin \
-append "root=/dev/vda ro" \
-drive file=rootfs.img,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-nographic
这里 -cpu rv64,v=true 是开启向量扩展的关键。不加这个,QEMU 默认的 CPU 模型不支持向量指令。
2.5 两种方案的对比与选择
| 对比项 | Spike + PK | QEMU 系统模拟 |
|---|---|---|
| 启动速度 | 毫秒级 | 秒级(加载内核) |
| 指令精确度 | 完全精确 | 动态翻译,略有偏差 |
| 向量支持 | 完整(v1.0) | 完整(v1.0) |
| 调试能力 | 内置调试器 | 需配合 GDB |
| 适用场景 | 指令验证、性能分析 | 系统级测试、驱动开发 |
我的建议是:前期学向量指令用 Spike,后期做项目集成测试用 QEMU。两个都装,不冲突。
2.6 环境验证清单
装完所有东西后,跑一遍这个清单,确保环境没问题:
- 工具链:
riscv64-unknown-elf-gcc -v能输出版本 - Spike:
spike -v能输出版本 - PK:
spike pk /bin/true不报错 - 向量编译:用
-march=rv64gcv编译向量程序能通过 - QEMU:
qemu-system-riscv64 --version输出版本
/opt/riscv/bin 加到 PATH 里,省得每次敲全路径。我在 .bashrc 里加了 export PATH=/opt/riscv/bin:$PATH,用起来方便多了。
环境搭好了,接下来就可以真正开始写向量代码了。记住,环境问题是最容易卡住人的,但也是最不值得花时间的——一次配好,后面就顺畅了。