2. 开发环境搭建:Verilator仿真器安装、GTKWave波形查看器配置、RISC-V GCC交叉编译工具链部署

做RISC-V内核验证,第一步就是把家伙事儿备齐。我记得刚入行那会儿,光装环境就折腾了两天——版本不对、路径冲突、缺库报错,搞得人想砸电脑。后来摸清了门道,其实就三样东西:仿真器、波形工具、交叉编译器。咱们一个一个来。

2.1 Verilator仿真器安装

Verilator是目前最流行的开源HDL仿真器之一。它能把Verilog/SystemVerilog代码转成C++,然后编译成可执行文件。说白了,它不直接“跑”仿真,而是把你的RTL代码“翻译”成高性能的C++模型。速度比传统事件驱动仿真器快一个数量级。

核心优势: 速度快、开源免费、与SystemVerilog断言兼容性好、支持Lint检查。

2.1.1 安装依赖

我用的是Ubuntu 22.04 LTS。其他发行版大同小异,包名可能略有不同。

# 更新包列表
sudo apt update

# 安装编译工具和依赖库
sudo apt install -y git make autoconf g++ flex bison \
    libfl-dev libgoogle-perftools-dev numactl \
    perl-doc libgz libfl2 libfl-dev zlib1g zlib1g-dev

小提示: 如果你在CentOS/RHEL上,把apt换成yum,包名基本一致。libgoogle-perftools-dev对应的是google-perftools-devel。

2.1.2 从源码编译安装

我个人习惯从GitHub拉最新稳定版。别用apt直接装——版本太老,很多新特性不支持。

# 克隆仓库
git clone https://github.com/verilator/verilator.git
cd verilator

# 切到最新稳定tag(建议查一下GitHub Releases)
git checkout v5.008

# 自动生成配置脚本
autoconf

# 配置、编译、安装
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install

验证安装是否成功:

verilator --version
# 输出类似:Verilator 5.008 2024-01-15 rev v5.008

注意: 如果报错“verilator: command not found”,检查/usr/local/bin是否在PATH里。加一行到~/.bashrc:export PATH=/usr/local/bin:$PATH。

2.1.3 快速测试

写个最简单的计数器模块,验证环境通不通:

// counter.v
module counter (
    input  clk,
    input  rst_n,
    output reg [7:0] count
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            count <= 8'h00;
        else
            count <= count + 1'b1;
    end
endmodule
// sim_main.cpp
#include <verilated.h>
#include <Vcounter.h>

int main(int argc, char** argv) {
    Verilated::commandArgs(argc, argv);
    Vcounter* top = new Vcounter;

    for (int i = 0; i < 20; i++) {
        top->clk = i % 2;
        top->rst_n = (i < 4) ? 0 : 1;
        top->eval();
        printf("cycle %2d: count = %02d\n", i, top->count);
    }

    delete top;
    return 0;
}
# 编译并运行
verilator --cc --exe --build -j counter.v sim_main.cpp
./obj_dir/Vcounter

看到count从0递增到19,环境就通了。嗯,这里要注意:--cc表示生成C++输出,--exe表示生成可执行文件,--build自动调用make。

2.2 GTKWave波形查看器配置

仿真跑完了,怎么看波形?GTKWave就是干这个的。轻量、免费、支持VCD/FST格式。我见过有人用ModelSim看波形,但GTKWave配合Verilator才是开源验证的黄金搭档。

2.2.1 安装GTKWave

sudo apt install -y gtkwave

装完直接命令行启动:

gtkwave dump.vcd

2.2.2 让Verilator输出VCD波形

修改上面的sim_main.cpp,加上波形转储:

#include <verilated.h>
#include <verilated_vcd_c.h>  // 新增
#include <Vcounter.h>

int main(int argc, char** argv) {
    Verilated::commandArgs(argc, argv);
    Vcounter* top = new Vcounter;

    // 初始化波形
    VerilatedVcdC* tfp = new VerilatedVcdC;
    Verilated::traceEverOn(true);
    top->trace(tfp, 99);  // 99是跟踪深度
    tfp->open("dump.vcd");

    for (int i = 0; i < 20; i++) {
        top->clk = i % 2;
        top->rst_n = (i < 4) ? 0 : 1;
        top->eval();
        tfp->dump(i * 10);  // 每个时间步dump一次
    }

    tfp->close();
    delete top;
    return 0;
}

重新编译运行,生成dump.vcd。然后用GTKWave打开:

gtkwave dump.vcd

个人经验: 我习惯在仿真脚本里加个环境变量控制波形开关。调试时开波形,回归测试时关掉,能省不少磁盘空间和仿真时间。

2.2.3 GTKWave常用操作

操作 快捷键/方法 说明
添加信号 左侧列表双击 / 拖拽 把信号从SST窗口拖到波形窗口
放大/缩小 Ctrl+滚轮 / 右键拖拽 缩放时间轴
测量时间 Shift+左键拖拽 显示两个光标之间的时间差
信号分组 选中信号 → Edit → Group 把总线信号合并显示
保存配置 File → Write Save File 保存.gtkw文件,下次直接加载

2.3 RISC-V GCC交叉编译工具链部署

仿真环境有了,但RISC-V内核跑的是RISC-V指令。你得把C代码编译成RISC-V的二进制,才能喂给仿真器。这就是交叉编译工具链的活儿。

2.3.1 选择工具链

目前主流的有两个分支:

  • riscv-gnu-toolchain:官方维护,功能最全,支持RV32/RV64、各种扩展。
  • riscv-none-elf-gcc:嵌入式裸机版,轻量,适合内核验证。

我个人推荐用riscv-none-elf-gcc。为什么?因为内核验证不需要操作系统支持,裸机程序就够了。而且编译快,体积小。

2.3.2 安装预编译版本

如果你不想从源码编译(说实话,编译工具链太耗时间),可以直接下载预编译包:

# 下载RISC-V GNU工具链(Linux x86_64)
wget https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.01.01/riscv64-unknown-elf-gcc-13.2.0-2024.01.tar.gz

# 解压到/opt
sudo tar -xzf riscv64-unknown-elf-gcc-13.2.0-2024.01.tar.gz -C /opt

# 添加环境变量
echo 'export PATH=/opt/riscv64-unknown-elf-gcc-13.2.0-2024.01/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

验证:

riscv64-unknown-elf-gcc --version
# 输出类似:riscv64-unknown-elf-gcc (GCC) 13.2.0

避坑指南: 我曾经在Ubuntu 20.04上装过某个版本的预编译工具链,结果glibc版本不兼容,一运行就段错误。后来发现是系统libc太老。解决办法:要么升级系统,要么用Docker跑工具链。

2.3.3 从源码编译(可选)

如果你有特殊需求(比如自定义扩展指令),那就得自己编译了:

git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git
cd riscv-gnu-toolchain

# 配置为裸机版本
./configure --prefix=/opt/riscv64-elf --with-arch=rv64imafdc --with-abi=lp64d
make -j$(nproc)

编译时间取决于机器性能,我上次编译用了大概40分钟(8核16线程)。

2.3.4 编译一个测试程序

写个简单的C程序,验证工具链是否正常工作:

// test.c
int main() {
    int a = 1, b = 2;
    int c = a + b;
    return c;
}
# 编译成RISC-V可执行文件
riscv64-unknown-elf-gcc -O2 -march=rv64imafdc -o test.elf test.c

# 反汇编查看生成的指令
riscv64-unknown-elf-objdump -d test.elf

看到反汇编里有addi、add等RISC-V指令,就说明工具链部署成功了。

2.4 整体环境关系图

下面这张图展示了三个工具之间的关系:

开发环境工具链关系图 RISC-V内核验证开发环境 C / 汇编源码 test.c / startup.S RISC-V GCC 交叉编译工具链 RTL设计 counter.v / core.v Verilator 仿真器 GTKWave 波形查看器 工作流程说明: 1. 用RISC-V GCC将C代码编译成RISC-V二进制(.elf或.hex) 2. Verilator将RTL设计编译成C++仿真模型 3. 仿真模型加载二进制文件,执行指令并输出VCD波形 4. GTKWave打开VCD文件,分析信号时序

2.5 环境验证清单

装完所有工具后,我建议你跑一遍这个验证清单,确保每个环节都正常:

序号 验证项 验证命令 预期结果
1 Verilator版本 verilator --version 显示版本号 ≥ 5.0
2 Verilator编译RTL verilator --cc counter.v --build 生成obj_dir目录,无报错
3 GTKWave启动 gtkwave --version 显示版本号
4 VCD波形生成 运行带波形输出的仿真程序 生成dump.vcd文件
5 RISC-V GCC版本 riscv64-unknown-elf-gcc --version 显示版本号 ≥ 12.0
6 交叉编译 riscv64-unknown-elf-gcc -o test.elf test.c 生成test.elf,无报错
7 反汇编验证 riscv64-unknown-elf-objdump -d test.elf 显示RISC-V指令

我的习惯: 把上面7个验证项写成一个shell脚本,每次换机器或重装系统后跑一遍。5分钟就能确认环境是否就绪,省得后面仿真出问题了再回头排查工具链。