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
版本注意: 我曾经在v4.210版本上遇到SystemVerilog接口解析bug,导致整个SoC总线仿真挂死。建议使用v5.008以上版本,对RISC-V相关特性支持更完善。

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 轻量,适合裸机调试
我的经验: 别追最新版。2023.2版本我遇到过AXI VIP的时序违例误报,折腾了两天才发现是工具bug。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)
注意: 源码编译需要约8GB内存和20GB磁盘。我曾在4GB内存的虚拟机上编译,结果swap撑爆了,系统直接卡死。建议至少8GB内存。

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做成独立目标。这样白天写代码跑仿真,晚上挂机综合,互不干扰。
避坑指南: 我曾经在Makefile里用$(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 本章知识体系总览

下面这张图,把整个开发环境搭建的脉络画出来了。你可以把它贴在工位旁边,每次配环境时对照着看。

RISC-V软核SoC开发环境搭建总览 RISC-V软核SoC开发环境搭建总览 Verilator仿真环境 Vivado/Vitis工具链 RISC-V GCC交叉编译链 Makefile构建系统 SoC开发工作流 RTL仿真验证 FPGA综合实现 固件编译下载 波形.vcd 比特流.bit 固件.bin Makefile 串联所有步骤:make sim → make synth → make prog 一次配置,全流程自动化

这张图里,四个工具链是并列关系,但最终通过Makefile串联成完整工作流。你不需要一次性全部配好——先配Verilator和GCC,能跑仿真了,再装Vivado做综合。一步步来,稳一点。

本章核心要点:

  • Verilator用源码编译,版本不低于v5.008
  • Vivado推荐2022.2,稳定第一
  • GCC交叉编译链优先用预编译包,省时间
  • Makefile把仿真、综合、下载串成一条命令