2. RISC-V与蜂鸟E203基础

好,咱们正式开始动手。这一章要解决一个最基础的问题:怎么让电脑能编译、烧录、调试 RISC-V 程序。说白了,就是给你的开发环境装上“翻译官”和“快递员”。

我个人习惯先把工具链装好再写代码,不然写完了发现跑不起来,那才叫抓狂。你想想看,一个裸机程序从写完到在板子上跑起来,中间要经过编译、链接、烧录、调试四个步骤,每一步都需要对应的工具。

本章核心目标:在 Ubuntu 系统上搭建完整的 RISC-V 开发工具链,包括 GCC 编译器、OpenOCD 调试器,并用 Makefile 管理工程。最后跑一个 Hello World 验证环境是否正常。

RISC-V 工具链搭建知识体系 RISC-V 工具链 GCC 编译器 OpenOCD 调试器 Makefile 工程管理 验证测试 riscv64-unknown-elf-gcc newlib 库 FTDI/JTAG 驱动 cfg 配置文件 变量/规则/函数 自动依赖 编译测试 烧录验证 💡 四个组件缺一不可:GCC 负责编译,OpenOCD 负责烧录调试,Makefile 负责自动化,验证确保一切正常 蜂鸟 E203 使用 RV32IMAC 指令集,必须选择对应的交叉编译工具链

2.1 安装 RISC-V GCC 交叉编译工具链

蜂鸟 E203 用的是 RV32IMAC 指令集,所以我们需要 riscv32-unknown-elf-gcc 这个交叉编译器。注意,不是 riscv64-linux-gnu,那个是给 Linux 系统用的,咱们裸机程序用不上。

安装方式有两种:直接下载预编译包 或者 从源码编译。我建议你直接用预编译的,省时间。从源码编译的话,我当年第一次搞的时候等了整整一个下午,中间还报了两个错,折腾到半夜才搞定。

我的建议:去 SiFive 官网下载 Freedom Studio 工具包,里面自带完整的 RISC-V 工具链。或者用芯来科技官方提供的预编译包,跟蜂鸟 E203 的兼容性最好。

安装步骤很简单,就三步:

  1. 下载工具链:从芯来科技 GitHub 仓库下载 riscv32-unknown-elf-gcc 预编译包
  2. 解压到指定目录:我习惯放在 /opt/riscv32 下
  3. 配置环境变量:把工具链的 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,再到跑通第一个程序,这个过程虽然琐碎,但每一步都很关键。后面咱们写再复杂的程序,都是在这个基础上搭建的。