2. RISC-V指令集与软核基础
说实话,搭建开发环境这件事,很多工程师觉得「装个软件而已,有什么好讲的」。但我见过太多项目,卡在环境配置上浪费一两天。尤其是RISC-V软核SoC这种涉及仿真、综合、编译、调试多个环节的工程,工具链稍微没对齐,后面全是坑。
这一章,我就把每个环节的配置要点、版本选择、避坑经验,一次性说清楚。
2.1 Verilator仿真环境配置
Verilator是目前最快的开源Verilog仿真器。它把HDL代码转成C++,然后编译成可执行文件。跑大型SoC仿真时,速度比商业仿真器快不少。我个人习惯在Linux下用Verilator做日常功能验证,只在需要波形调试时才切到Vivado Simulator或VCS。
2.1.1 安装Verilator
我建议直接从源码编译安装,这样能拿到最新特性。Ubuntu 20.04/22.04下的步骤:
# 安装依赖
sudo apt-get install git perl python3 make autoconf g++ flex bison
sudo apt-get install libgoogle-perftools-dev numactl perl-doc
sudo apt-get install libfl2 # Ubuntu 22.04需要
# 克隆源码
git clone https://github.com/verilator/verilator
cd verilator
# 编译安装
autoconf
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install
# 验证
verilator --version
2.1.2 配置仿真环境变量
为了让Verilator找到你的RTL和C++仿真文件,建议在.bashrc里加几个环境变量:
export VERILATOR_ROOT=/usr/local/share/verilator
export PATH=$PATH:$VERILATOR_ROOT/bin
export VERILATOR_INC=$VERILATOR_ROOT/include
嗯,这里要注意:VERILATOR_ROOT这个变量,很多教程不设,但如果你用Verilator的C++接口写testbench,编译时会找不到verilated.h。我踩过这个坑,折腾了半小时。
2.1.3 快速验证:跑一个最小SoC仿真
装好之后,我习惯跑一个最小RISC-V核的仿真来验证环境。比如用picorv32:
git clone https://github.com/YosysHQ/picorv32
cd picorv32
verilator --cc --exe --build -j 0 \
--top-module picorv32 \
sim_main.cpp picorv32.v
./obj_dir/Vpicorv32
如果终端打印出PASS或类似信息,说明环境通了。
2.2 Vivado/Vitis工具链安装
Vivado是Xilinx的FPGA综合实现工具,Vitis是嵌入式软件开发环境。对于RISC-V软核SoC,我们通常用Vivado做硬件综合,用Vitis(或Vitis Classic)写裸机程序。
2.2.1 版本选择建议
| 用途 | 推荐版本 | 说明 |
|---|---|---|
| 日常开发 | Vivado 2022.2 | 稳定,对RISC-V软核支持好 |
| 新项目 | Vivado 2023.1/2024.1 | 支持新器件,但注意IP兼容性 |
| 调试用 | Vitis Classic 2022.2 | 轻量,适合裸机调试 |
2.2.2 安装要点
安装Vivado时,有几点容易忽略:
- 磁盘空间: 完整安装需要约100GB。我建议只选「Vivado」和「Vitis」,不装DocNav和PetaLinux,能省30GB。
- License: 如果只是教学或个人学习,用WebPACK版就够了。商业项目需要Node Locked或Floating License。
- JTAG驱动: Linux下安装后,记得运行
sudo /opt/Xilinx/Vivado/2022.2/data/xicom/cable_drivers/lin64/install_script/install_drivers/install_drivers,否则连不上开发板。
2.2.3 环境变量配置
安装完成后,在.bashrc里添加:
source /opt/Xilinx/Vivado/2022.2/settings64.sh
source /opt/Xilinx/Vitis/2022.2/settings64.sh
这样每次打开终端,Vivado和Vitis的命令就自动可用了。
2.3 RISC-V GCC交叉编译链搭建
RISC-V GCC交叉编译链,说白了就是把你的C代码编译成RISC-V指令集的机器码。没有它,你写的软件就跑不到软核上。
2.3.1 两种搭建方式
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 源码编译 | 可定制,支持最新ISA扩展 | 编译耗时(约1小时) | 需要自定义指令集 |
| 预编译包 | 即装即用 | 版本固定,扩展支持有限 | 快速上手、教学 |
我个人推荐初学者用预编译包,省时间。等后面需要调优或加自定义指令时,再源码编译。
2.3.2 预编译包安装(推荐)
从SiFive官网或GitHub Releases下载:
# 下载 riscv64-unknown-elf-gcc
wget https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.11.20/riscv64-unknown-elf-toolchain-2023.11.20-x86_64-linux-ubuntu14-14.tar.gz
# 解压到 /opt
sudo tar -xzf riscv64-unknown-elf-toolchain-*.tar.gz -C /opt
# 添加环境变量
export PATH=$PATH:/opt/riscv64-unknown-elf/bin
# 验证
riscv64-unknown-elf-gcc --version
2.3.3 源码编译安装
如果你需要定制,比如只编译RV32IMC架构(不带浮点和64位),可以这样:
git clone https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain
# 配置为裸机工具链,只编译RV32IMC
./configure --prefix=/opt/riscv32 \
--with-arch=rv32imc \
--with-abi=ilp32
make -j$(nproc)
2.3.4 验证交叉编译链
写一个简单的C程序测试:
// test.c
int main() {
volatile int a = 1, b = 2;
int c = a + b;
return c;
}
// 编译
riscv64-unknown-elf-gcc -march=rv32imc -mabi=ilp32 -o test.elf test.c
// 查看反汇编
riscv64-unknown-elf-objdump -d test.elf
如果能看到RISC-V指令(如add、lui、sw),说明工具链正常工作。
2.4 Makefile构建系统设计
一个SoC项目,涉及RTL仿真、C代码编译、FPGA综合、固件下载等多个步骤。用手动敲命令的方式,迟早会出错。Makefile就是把这些步骤串起来的「自动化脚本」。
2.4.1 一个最小可用的Makefile
我设计Makefile的习惯是:把变量定义放在顶部,把目标依赖写清楚,每个目标只做一件事。
# 变量定义
CROSS_COMPILE = riscv64-unknown-elf-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
ARCH = rv32imc
ABI = ilp32
# 源文件
SRC_C = main.c uart.c gpio.c
SRC_S = startup.S
OBJS = $(SRC_C:.c=.o) $(SRC_S:.S=.o)
# 目标
TARGET = firmware.elf
BIN = firmware.bin
# 编译规则
%.o: %.c
$(CC) -march=$(ARCH) -mabi=$(ABI) -c -o $@ $<
%.o: %.S
$(CC) -march=$(ARCH) -mabi=$(ABI) -c -o $@ $<
# 链接
$(TARGET): $(OBJS) link.ld
$(CC) -march=$(ARCH) -mabi=$(ABI) -T link.ld -o $@ $(OBJS)
# 生成二进制文件
$(BIN): $(TARGET)
$(OBJCOPY) -O binary $< $@
# 仿真
sim: $(BIN)
verilator --cc --exe --build \
--top-module soc_top \
sim_main.cpp rtl/*.v
./obj_dir/Vsoc_top +firmware=$(BIN)
# 综合
synth:
vivado -mode batch -source synth.tcl
# 下载
prog:
vivado -mode batch -source prog.tcl
# 清理
clean:
rm -rf *.o *.elf *.bin obj_dir
2.4.2 设计要点
- 变量集中管理: 交叉编译链前缀、架构参数、源文件列表,都放在顶部。换一个核或换一个架构,改一行就行。
- 依赖链清晰:
sim依赖$(BIN),$(BIN)依赖$(TARGET),$(TARGET)依赖$(OBJS)。改一个C文件,只会重新编译那个文件,然后重新链接。 - 仿真和综合分离: 我习惯把
sim和synth做成独立目标。这样白天写代码跑仿真,晚上挂机综合,互不干扰。
$(shell find ...)自动收集源文件,结果某次不小心把仿真目录下的临时文件也加进来了,链接时报重复定义。后来我改成手动列出源文件,虽然麻烦点,但可控。
2.4.3 进阶:多配置支持
同一个SoC,可能要在不同FPGA板上跑,或者要切换不同的RISC-V核。我习惯用Makefile变量来切换配置:
# 默认配置
BOARD ?= nexys_a7
CORE ?= picorv32
# 根据配置选择不同的文件
ifeq ($(BOARD), nexys_a7)
XDC = constraints/nexys_a7.xdc
PART = xc7a100tcsg324-1
else ifeq ($(BOARD), arty_a7)
XDC = constraints/arty_a7.xdc
PART = xc7a35ticsg324-1L
endif
synth:
vivado -mode batch -source synth.tcl \
-tclargs $(PART) $(XDC)
使用时:make synth BOARD=arty_a7 CORE=vexriscv,一条命令切换整个项目配置。
2.5 本章知识体系总览
下面这张图,把整个开发环境搭建的脉络画出来了。你可以把它贴在工位旁边,每次配环境时对照着看。
这张图里,四个工具链是并列关系,但最终通过Makefile串联成完整工作流。你不需要一次性全部配好——先配Verilator和GCC,能跑仿真了,再装Vivado做综合。一步步来,稳一点。
本章核心要点:
- Verilator用源码编译,版本不低于v5.008
- Vivado推荐2022.2,稳定第一
- GCC交叉编译链优先用预编译包,省时间
- Makefile把仿真、综合、下载串成一条命令