2. RISC-V 工具链搭建:让编译器认识你的蜂鸟 E203
好,咱们正式开始动手。这一章要解决一个最基础的问题:怎么让电脑能编译、烧录、调试 RISC-V 程序。说白了,就是给你的开发环境装上“翻译官”和“快递员”。
我个人习惯先把工具链装好再写代码,不然写完了发现跑不起来,那才叫抓狂。你想想看,一个裸机程序从写完到在板子上跑起来,中间要经过编译、链接、烧录、调试四个步骤,每一步都需要对应的工具。
本章核心目标:在 Ubuntu 系统上搭建完整的 RISC-V 开发工具链,包括 GCC 编译器、OpenOCD 调试器,并用 Makefile 管理工程。最后跑一个 Hello World 验证环境是否正常。
2.1 安装 RISC-V GCC 交叉编译工具链
蜂鸟 E203 用的是 RV32IMAC 指令集,所以我们需要 riscv32-unknown-elf-gcc 这个交叉编译器。注意,不是 riscv64-linux-gnu,那个是给 Linux 系统用的,咱们裸机程序用不上。
安装方式有两种:直接下载预编译包 或者 从源码编译。我建议你直接用预编译的,省时间。从源码编译的话,我当年第一次搞的时候等了整整一个下午,中间还报了两个错,折腾到半夜才搞定。
我的建议:去 SiFive 官网下载 Freedom Studio 工具包,里面自带完整的 RISC-V 工具链。或者用芯来科技官方提供的预编译包,跟蜂鸟 E203 的兼容性最好。
安装步骤很简单,就三步:
- 下载工具链:从芯来科技 GitHub 仓库下载 riscv32-unknown-elf-gcc 预编译包
- 解压到指定目录:我习惯放在
/opt/riscv32下 - 配置环境变量:把工具链的 bin 目录加到 PATH 里
具体命令如下:
# 解压工具链
sudo tar -xzf riscv32-unknown-elf-gcc-xxx.tar.gz -C /opt/riscv32
# 配置环境变量(加到 ~/.bashrc 里)
export PATH=/opt/riscv32/bin:$PATH
export RISCV=/opt/riscv32
# 使环境变量生效
source ~/.bashrc
# 验证安装
riscv32-unknown-elf-gcc --version
看到版本号输出,就说明装好了。如果提示“command not found”,检查一下路径对不对,或者重新 source 一下。
注意:环境变量一定要加到 ~/.bashrc 里,不要只写在终端里临时执行。否则每次开新终端都要重新 export,很烦人。我曾经因为这个浪费了半小时排查“为什么刚才还能编译现在不行了”。
2.2 配置 OpenOCD 调试器
OpenOCD 是咱们和 FPGA 板子之间的桥梁。它通过 JTAG 接口连接开发板,负责烧录程序和在线调试。蜂鸟 E203 的 FPGA 开发板通常用 FT2232 芯片做 USB-JTAG 转换。
安装 OpenOCD 也有两种方式:
| 方式 | 命令/步骤 | 适用场景 |
|---|---|---|
| apt 安装 | sudo apt install openocd |
快速体验,版本可能较旧 |
| 源码编译 | 下载源码 → configure → make → make install | 需要最新特性或特定补丁 |
我个人推荐用 源码编译,因为 apt 里的版本通常比较老,对 RISC-V 的支持可能不完整。而且编译 OpenOCD 很快,几分钟就好。
源码编译的步骤:
# 安装依赖
sudo apt install libtool automake autoconf texinfo libusb-1.0-0-dev
# 下载 OpenOCD 源码(建议用 riscv-openocd 分支)
git clone https://github.com/riscv/riscv-openocd.git
cd riscv-openocd
# 配置(启用 FTDI 和 JTAG 支持)
./bootstrap
./configure --enable-ftdi --enable-jtag
# 编译安装
make -j4
sudo make install
# 验证
openocd --version
装好之后,还需要一个 配置文件,告诉 OpenOCD 怎么连接你的开发板。蜂鸟 E203 的配置文件长这样:
# hummingbird-e203.cfg
source [find interface/ftdi/hummingbird-e203.cfg]
source [find target/riscv.cfg]
# 设置 RISC-V 调试参数
riscv set_reset_timeout_sec 10
riscv set_command_timeout_sec 10
# 初始化
init
targets
halt
小技巧:芯来科技官方仓库里已经提供了现成的 cfg 文件,直接拿来用就行。别自己从头写,容易踩坑。我第一次自己写 cfg 文件时,忘了设置时钟频率,结果 OpenOCD 一直报连接超时。
2.3 Makefile 工程管理基础
裸机程序不像 Linux 应用那样有 IDE 帮你管理编译。你得自己写 Makefile,告诉编译器怎么编译、链接、生成最终的可执行文件。
一个最简单的 Makefile 包含三个要素:变量、规则、目标。咱们来看一个蜂鸟 E203 裸机程序的 Makefile 示例:
# 工具链定义
CROSS_COMPILE = riscv32-unknown-elf-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
SIZE = $(CROSS_COMPILE)size
# 编译选项
ARCH = rv32imac
ABI = ilp32
CFLAGS = -march=$(ARCH) -mabi=$(ABI) -O2 -g -ffreestanding -nostdlib
LDFLAGS = -T link.ld -nostartfiles
# 源文件
SRCS = main.c startup.c uart.c
OBJS = $(SRCS:.c=.o)
TARGET = hello.elf
# 默认目标
all: $(TARGET)
# 链接
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^
$(OBJCOPY) -O binary $@ $(TARGET:.elf=.bin)
$(SIZE) $@
# 编译
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
# 烧录
flash: $(TARGET)
openocd -f hummingbird-e203.cfg -c "program $(TARGET) verify reset exit"
# 调试
debug: $(TARGET)
openocd -f hummingbird-e203.cfg -c "program $(TARGET) verify reset" &
riscv32-unknown-elf-gdb $(TARGET) -x gdbinit
# 清理
clean:
rm -f $(OBJS) $(TARGET) $(TARGET:.elf=.bin)
.PHONY: all flash debug clean
这个 Makefile 里,我重点解释几个地方:
- -ffreestanding:告诉编译器这是一个裸机程序,没有操作系统支持
- -nostdlib:不使用标准 C 库,因为咱们没有 glibc
- -T link.ld:指定链接脚本,告诉链接器代码段、数据段放在内存的什么位置
- flash 目标:一键烧录,省得每次敲一长串 OpenOCD 命令
核心概念:Makefile 的本质是“依赖关系 + 执行命令”。你只要把源文件、编译选项、链接脚本写对,剩下的交给 make 自动处理。写一次,用整个项目周期。
2.4 验证工具链是否工作
工具链装好了,配置文件也有了,Makefile 也写好了。接下来就是验证环节。咱们写一个最简单的程序,编译、烧录、运行,看看能不能正常工作。
先写一个 main.c:
#include "uart.h"
void main(void)
{
uart_init(115200);
uart_puts("Hello, Hummingbird E203!\r\n");
uart_puts("RISC-V toolchain is working!\r\n");
while(1);
}
然后执行编译:
make clean
make all
如果一切正常,你会看到类似这样的输出:
riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -g -ffreestanding -nostdlib -c -o main.o main.c
riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -g -ffreestanding -nostdlib -c -o startup.o startup.c
riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -g -ffreestanding -nostdlib -c -o uart.o uart.c
riscv32-unknown-elf-gcc -T link.ld -nostartfiles -o hello.elf main.o startup.o uart.o
riscv32-unknown-elf-objcopy -O binary hello.elf hello.bin
text data bss dec hex filename
1234 56 12 1302 516 hello.elf
看到 text、data、bss 这些段的大小,说明编译成功了。接下来烧录到板子上:
make flash
OpenOCD 会连接开发板,把程序烧进去,然后自动复位运行。如果串口上打印出 "Hello, Hummingbird E203!",恭喜你,工具链搭建完成!
常见问题排查:
- OpenOCD 找不到设备:检查 USB 驱动,试试
lsusb看有没有 FTDI 设备 - 编译报错“unknown architecture”:确认工具链版本,用
riscv32-unknown-elf-gcc -v查看支持的架构 - 烧录后没反应:检查串口波特率是否匹配,复位引脚是否正常
嗯,到这里工具链就全部搞定了。你想想看,从零开始装编译器、配调试器、写 Makefile,再到跑通第一个程序,这个过程虽然琐碎,但每一步都很关键。后面咱们写再复杂的程序,都是在这个基础上搭建的。