搭建开发环境:安装QEMU、配置RISC-V交叉编译工具链、安装GDB调试器
做嵌入式开发,环境搭建是第一道坎。很多人觉得这事简单,照着文档敲命令就行。但我在实际项目中见过太多人卡在这一步——不是版本冲突,就是路径不对,折腾半天连个Hello World都跑不起来。
这一章,我带你一步步把环境搭好。咱们的目标很明确:装好QEMU、配好交叉编译工具链、装好GDB,最后跑一个程序验证全部正常。整个过程大概需要30分钟,请确保你的电脑能联网。
本章核心产出:一个能编译RISC-V程序、能在QEMU上运行、能用GDB调试的完整开发环境。
2.1 整体架构:这些工具怎么配合工作?
先别急着敲命令。咱们花两分钟搞清楚这些工具之间的关系,后面遇到问题你才知道怎么排查。
说白了,你的开发机是x86或ARM架构,但目标芯片是RISC-V。你不能直接在开发机上编译运行——得用交叉编译器生成RISC-V的二进制文件,然后扔给QEMU去模拟执行。GDB则通过远程调试协议连上QEMU,让你能单步调试。
2.2 安装QEMU
QEMU的安装方式取决于你的操作系统。我个人习惯用包管理器,干净省事。
2.2.1 Ubuntu/Debian 系
sudo apt update
sudo apt install qemu-system-misc
装完验证一下:
qemu-system-riscv64 --version
如果看到版本号,说明装好了。我遇到过有人装成了 qemu-system-riscv(少了个64),那是32位的,别搞混。
2.2.2 macOS 系
brew install qemu
2.2.3 从源码编译(可选)
如果你需要最新特性,或者想自己加patch,那就从源码编译:
git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
./configure --target-list=riscv64-softmmu
make -j$(nproc)
sudo make install
我的建议:初学者先用包管理器装。等后面学到设备模拟、自定义机器时,再考虑源码编译。我曾经为了调试一个RISC-V的MMU bug,不得不自己编译QEMU加调试日志——那是后话了。
2.3 配置RISC-V交叉编译工具链
交叉编译工具链,就是一套能在你的开发机上生成RISC-V代码的编译器、汇编器、链接器。最常用的是 riscv64-unknown-elf-gcc。
2.3.1 方案一:使用发行版包管理器(推荐)
Ubuntu 22.04+ 直接有现成的包:
sudo apt install gcc-riscv64-unknown-elf
装完检查:
riscv64-unknown-elf-gcc --version
2.3.2 方案二:从SiFive官网下载预编译包
如果包管理器版本太老,或者你用的不是Ubuntu,可以去SiFive的GitHub Releases页面下载预编译的工具链。下载后解压,把 bin 目录加到PATH里:
export PATH=$PATH:/path/to/riscv64-unknown-elf-toolchain/bin
记得把这个加到 ~/.bashrc 或 ~/.zshrc 里,不然每次开终端都要重新export。
2.3.3 方案三:自己编译(不推荐初学者)
从源码编译RISC-V工具链大概需要2小时,而且容易踩坑。除非你有特殊需求,否则别折腾。
注意:交叉编译工具链的前缀是 riscv64-unknown-elf-,不是 riscv64-linux-gnu-。前者是裸机工具链(没有操作系统),后者是Linux工具链(依赖内核)。咱们做固件开发,用裸机工具链。
2.4 安装GDB调试器
GDB用于调试RISC-V程序。同样需要交叉编译版本的GDB。
sudo apt install gdb-multiarch
或者安装RISC-V专用版本:
sudo apt install gdb-riscv64-unknown-elf
我个人更推荐 gdb-multiarch,因为它支持多种架构,后面调试ARM或其他芯片也能用。
验证安装:
gdb-multiarch --version
# 或者
riscv64-unknown-elf-gdb --version
2.5 验证环境:写一个Hello World跑起来
光装不验等于白装。咱们写个最简单的程序,编译、运行、调试,全套走一遍。
2.5.1 编写启动代码和链接脚本
RISC-V裸机程序需要一点启动代码。新建一个文件 start.S:
.section .text.init
.globl _start
_start:
# 设置栈指针
lui sp, 0x80000
# 跳转到main
jal main
# 死循环
loop:
j loop
再写个链接脚本 link.ld:
OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS
{
. = 0x80000000;
.text : { *(.text.init) *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
2.5.2 编写C程序
main.c:
volatile unsigned int *uart = (unsigned int *)0x10000000;
void putchar(char c) {
*uart = c;
}
void print(const char *s) {
while (*s) {
putchar(*s++);
}
}
int main() {
print("Hello, RISC-V World!\n");
return 0;
}
2.5.3 编译
riscv64-unknown-elf-gcc -march=rv64gc -mabi=lp64d \
-nostdlib -nostartfiles -ffreestanding \
-T link.ld start.S main.c -o hello.elf
解释一下关键参数:
| 参数 | 含义 |
|---|---|
-march=rv64gc | 64位RISC-V,带通用扩展(I、M、A、F、D、C) |
-mabi=lp64d | LP64数据模型,双精度浮点 |
-nostdlib | 不使用标准库(裸机环境没有libc) |
-ffreestanding | 独立环境编译 |
2.5.4 在QEMU上运行
qemu-system-riscv64 -machine virt -nographic \
-bios none -kernel hello.elf
如果看到输出 Hello, RISC-V World!,恭喜你,环境搭建成功!
常见问题:如果没输出,检查UART地址。virt机器的串口在0x10000000,其他机器可能不同。我曾经在sifive_u机器上折腾了半天,才发现它的UART地址是0x10010000。
2.5.5 用GDB调试
先启动QEMU,让它等待GDB连接:
qemu-system-riscv64 -machine virt -nographic \
-bios none -kernel hello.elf \
-s -S
-s 是 -gdb tcp::1234 的简写,-S 让CPU在启动时暂停。
另开一个终端,启动GDB:
gdb-multiarch hello.elf
(gdb) target remote :1234
(gdb) break main
(gdb) continue
(gdb) step
(gdb) info registers
你会看到程序停在 main 函数入口,可以单步执行、查看寄存器。嗯,这种感觉很爽。
2.6 环境验证清单
最后,给你一个检查清单。每完成一项就打勾:
| # | 检查项 | 验证命令 |
|---|---|---|
| 1 | QEMU安装成功 | qemu-system-riscv64 --version |
| 2 | 交叉编译器可用 | riscv64-unknown-elf-gcc --version |
| 3 | GDB可用 | gdb-multiarch --version |
| 4 | 编译生成ELF | file hello.elf 显示RISC-V架构 |
| 5 | QEMU运行输出 | 看到 "Hello, RISC-V World!" |
| 6 | GDB远程调试 | 能打断点、单步、看寄存器 |
我的习惯:我会把上面这些验证命令写成一个shell脚本,每次换电脑或重装系统后跑一遍,5分钟就能确认环境是否正常。你也试试。