第二章:开发环境搭建

好,咱们正式开始动手了。这一章我带你把整个开发环境搭起来。说实话,环境搭建这事儿看着琐碎,但要是没弄对,后面调试自定义指令时会让你抓狂。我自己就吃过这个亏——第一次做扩展指令时,工具链版本不对,折腾了两天才发现是编译器不支持新操作码。

这一章我们搞定三样东西:RISC-V GNU工具链QEMU模拟器、还有Verilator仿真环境。这三样就是你后续开发自定义指令的“三驾马车”。

2.1 RISC-V GNU工具链编译

工具链是什么?说白了就是你把C代码变成RISC-V机器码的那套工具。包括编译器、汇编器、链接器、调试器等等。我们做自定义指令,必须自己编译工具链,因为要告诉编译器:嘿,我新加了一条指令,你得认识它。

2.1.1 获取源码

我建议直接从官方仓库拉代码。别去网上找什么打包好的二进制,版本对不上很麻烦。

git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git
cd riscv-gnu-toolchain
git submodule update --init --recursive

嗯,这里要注意:submodule update这一步会下载很多子模块,包括binutils、gcc、newlib这些。网络不好的话可能会断,我建议用代理或者找个网络好的时间段搞。

2.1.2 配置与编译

配置时有两个选择:Newlib(轻量级C库,适合裸机)和glibc(完整版,适合Linux)。我们做自定义指令开发,用Newlib就够了,编译快很多。

mkdir build && cd build
../configure --prefix=/opt/riscv --with-arch=rv64gc --with-abi=lp64d
make -j$(nproc)

这里--prefix指定安装路径,我习惯装到/opt/riscv--with-arch=rv64gc表示生成64位、带通用扩展(G)和压缩指令(C)的工具链。--with-abi=lp64d是LP64数据模型加双精度浮点ABI。

注意:编译时间很长,我当年第一次编译等了快两个小时。建议用-j$(nproc)开启多核并行编译。另外,确保你的系统装了必要的依赖:flex bison autoconf automake libmpc-dev等。

2.1.3 验证安装

编译完成后,把工具链加入PATH:

export PATH=/opt/riscv/bin:$PATH
riscv64-unknown-elf-gcc --version

如果看到版本信息,恭喜你,工具链搞定了。写个简单的测试程序试试:

// test.c
int main() {
    int a = 1, b = 2;
    return a + b;
}
riscv64-unknown-elf-gcc -o test test.c
riscv64-unknown-elf-objdump -d test

看到RISC-V的汇编指令了吗?嗯,这就是我们后面要扩展的基础。

2.2 QEMU模拟器安装

QEMU是个好东西。你手头没有RISC-V开发板?没关系,QEMU能模拟一个完整的RISC-V系统。我在做自定义指令原型验证时,90%的调试都是在QEMU上完成的。

2.2.1 安装QEMU

我推荐从源码编译,这样能确保支持最新的RISC-V特性。Ubuntu系统上也可以直接apt安装,但版本可能比较老。

git clone https://github.com/qemu/qemu.git
cd qemu
mkdir build && cd build
../configure --target-list=riscv64-softmmu,riscv64-linux-user
make -j$(nproc)
sudo make install

这里--target-list指定了两个目标:riscv64-softmmu(系统模式,模拟完整机器)和riscv64-linux-user(用户模式,直接运行RISC-V程序)。

小技巧:如果你只是测试裸机程序,用户模式就够了,启动快。系统模式适合跑Linux内核,调试复杂场景。

2.2.2 运行第一个RISC-V程序

用用户模式运行刚才编译的test程序:

qemu-riscv64-static ./test
echo $?

输出应该是3(1+2的结果)。看,没有硬件也能跑RISC-V代码了。

2.3 Verilator仿真环境配置

Verilator是什么?它能把Verilog代码转换成C++,然后编译成可执行文件进行仿真。速度比传统的ModelSim快很多,适合做大规模数字电路的验证。我们后面要设计自定义指令的硬件实现,Verilator是主力工具。

2.3.1 安装Verilator

Verilator依赖一些库,先装好:

sudo apt-get install git make autoconf g++ flex bison
sudo apt-get install libfl-dev libgoogle-perftools-dev

然后从源码编译:

git clone https://github.com/verilator/verilator.git
cd verilator
autoconf
./configure
make -j$(nproc)
sudo make install

验证安装:

verilator --version

看到版本号就对了。我目前用的是5.x版本,功能很全。

2.3.2 第一个Verilator仿真

写个简单的Verilog模块测试一下:

// 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'b0;
        else
            count <= count + 1;
    end
endmodule

然后写个C++测试文件:

// 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 < 10; i++) {
        top->clk = 0;
        top->eval();
        top->clk = 1;
        top->eval();
        printf("count = %d\n", top->count);
    }
    
    delete top;
    return 0;
}

编译运行:

verilator --cc counter.v --exe sim_main.cpp
make -j -C obj_dir -f Vcounter.mk Vcounter
./obj_dir/Vcounter

看到计数器从0递增到9了吗?嗯,Verilator环境就绪了。

核心要点:Verilator仿真速度快,但它是“两态”仿真(只有0和1,没有X态)。如果你需要X态传播检查,得用其他工具。不过对于自定义指令的功能验证,Verilator完全够用。

2.4 环境验证与常见问题

三样工具都装好了,我们来个综合测试。写一个简单的RISC-V汇编程序,用工具链编译,QEMU运行,同时用Verilator仿真一个对应的硬件模块。这样就能确认整个链路是通的。

我曾经遇到过一个坑:工具链编译时用了--with-arch=rv64imafdc,但QEMU模拟的CPU不支持D扩展(双精度浮点),结果程序一跑就崩。后来我统一用rv64gc才解决。所以,工具链和模拟器的架构参数一定要匹配

工具 用途 验证命令
GNU工具链 编译RISC-V程序 riscv64-unknown-elf-gcc --version
QEMU 运行RISC-V程序 qemu-riscv64-static --version
Verilator 仿真Verilog设计 verilator --version

好了,环境搭建就到这里。下一章我们开始真正进入自定义指令的设计——从指令编码到硬件实现,一步步来。到时候你会发现,今天搭的环境就是你的“试验田”,所有想法都能在这里快速验证。