IC开发——Verilator

1. 简介

Verilator 是一个开源的 Verilog 和 SystemVerilog 硬件描述语言 (HDL) 仿真器。它是一个高性能的仿真器,可以将 Verilog 和 SystemVerilog 代码转换为 C++/SystemC 代码,并生成可执行的仿真模型。

Verilator 的主要特点包括:

  1. 高性能:Verilator 生成的仿真模型具有非常高的性能,可以与商业级仿真器媲美。
  2. 开源:Verilator 是一个开源项目,可以免费使用和修改。
  3. 跨平台:Verilator 可以在 Linux、macOS 和 Windows 等多种操作系统上运行。
  4. 支持多种 HDL:Verilator 不仅支持 Verilog,还支持 SystemVerilog 语言。
  5. 可扩展性:Verilator 提供了丰富的 API,可以与其他工具和环境集成。

Verilator 的主要应用场景包括:

  1. 硬件设计和验证:Verilator 可以用于设计和验证各种硬件电路,包括 FPGA、ASIC 和嵌入式系统等。
  2. 软件开发:Verilator 生成的仿真模型可以用于软件开发和测试,如驱动程序和固件等。
  3. 教学和研究:Verilator 可以用于教学和研究,帮助学生和研究人员了解和学习 HDL 语言。

    2. 安装

    Linux下可以通过apt安装Verilator,但是安装的版本较旧,新版本功能更强大,Bug更少,推荐通过代码编译安装最新版本。此处安装gcc-9是为了后续编译gcc-5.2做准备,所以未安装最新版本gcc。Linux使用22.04版本。

    sudo apt install -y gcc-9
    sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 10
    sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 10
    
    sudo apt-get install -y git help2man perl python3 make cmake autoconf flex bison ccache
    sudo apt-get install -y libgoogle-perftools-dev numactl perl-doc
    sudo apt-get install -y libfl2  
    sudo apt-get install -y libfl-dev  
    sudo apt-get install -y zlibc zlib1g zlib1g-dev  
    
    git clone https://github.com/verilator/verilator
    
    cd verilator
    unset VERILATOR_ROOT
    git checkout -b v5.024 v5.024
    autoconf         
    ./configure
    make -j `nproc`
    sudo make install
    
    sudo apt install -y gtkwave
    
    # SystemC 2.3.3
    curl -O https://www.accellera.org/images/downloads/standards/systemc/systemc-2.3.3.tar.gz
    tar -xzf systemc-2.3.3.tar.gz 
    cd systemc-2.3.3
    mkdir build
    cd build
    ../configure --prefix=/usr/local/systemc-2.3.3
    make -j8
    sudo make install

    配置SystemC:

    在~/.bashrc最后面添加:

    export SYSTEMC_INCLUDE=/usr/local/systemc-2.3.3/include

    export SYSTEMC_LIBDIR=/usr/local/systemc-2.3.3/lib-linux64

    export LD_LIBRARY_PATH=/usr/local/systemc-2.3.3/lib-linux64

    在控制台输入:

    source ~/.bashrc

3. 测试

3.1. 版本

m@vm-ubuntu22:~$ verilator --version
Verilator 5.024 2024-04-05 rev v5.024

3.2. 示例

Verilator目录下的example目录下有一些示例,其中有_c表示是cpp版本示例,_sc表示是SystemC版本示例。

3.2.1. CPP示例

Verilator将Verilog转为标准C++来进行编译仿真,其仿真速度会更快。这种方式生成的仿真模块也可以更方便地接入QEMU等模块中进行使用。

cd make_tracing_c
make

make会完成Verilog转C++,编译SystemC代码,仿真运行。

3.2.1.1. 编译

verilator -cc --exe --build -j top.v sim_main.cpp

  • verilator -cc 表示将top.v生成cpp文件,并保存到OBJ_DIR目录。
  • -exe表示生成可执行文件。
  • sim_main.cpp表示此为启动文件,也即testbench文件。
  • --build指示通过make编译,make中g++的编译过程其实现的过程如下:
    ccache g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -Os -c -o sim_main.o ../sim_main.cpp
    ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -c -o verilated.o /usr/local/share/verilator/include/verilated.cpp
    ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -c -o verilated_threads.o /usr/local/share/verilator/include/verilated_threads.cpp
    /usr/bin/python3 /usr/local/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vtop.cpp Vtop___024root__DepSet_h84412442__0.cpp Vtop___024root__DepSet_heccd7ead__0.cpp Vtop___024root__Slow.cpp Vtop___024root__DepSet_heccd7ead__0__Slow.cpp Vtop__Syms.cpp > Vtop__ALL.cpp
    ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=0 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=0 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -c -o Vtop__ALL.o Vtop__ALL.cpp
    echo "" > Vtop__ALL.verilator_deplist.tmp
    g++    sim_main.o verilated.o verilated_threads.o Vtop__ALL.a    -pthread -lpthread -latomic   -o Vtop

  • ccache是为了缓存加速编译的,可以不用管。
  • verilator的main入口函数在sim_main.cpp中。
  • verilated.cpp和verilated_threads.cpp是两个verilator文件。
  • Python脚本是将top.v生成的C++代码全部合并到Vtop__ALL.cpp,方便编译。
  • 最后编译链接生成可执行文件Vtop。
3.2.1.2. 仿真运行

./Vtop

3.2.1.3. testbench代码 

int main(int argc, char** argv) {
    // This is a more complicated example, please also see the simpler examples/make_hello_c.

    // Create logs/ directory in case we have traces to put under it
    Verilated::mkdir("logs");

    // Construct a VerilatedContext to hold simulation time, etc.
    // Multiple modules (made later below with Vtop) may share the same
    // context to share time, or modules may have different contexts if
    // they should be independent from each other.

    // Using unique_ptr is similar to
    // "VerilatedContext* contextp = new VerilatedContext" then deleting at end.
    const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
    // Do not instead make Vtop as a file-scope static variable, as the
    // "C++ static initialization order fiasco" may cause a crash

    // Set debug level, 0 is off, 9 is highest presently used
    // May be overridden by commandArgs argument parsing
    contextp->debug(0);

    // Randomization reset policy
    // May be overridden by commandArgs argument parsing
    contextp->randReset(2);

    // Verilator must compute traced signals
    contextp->traceEverOn(true);

    // Pass arguments so Verilated code can see them, e.g. $value$plusargs
    // This needs to be called before you create any model
    contextp->commandArgs(argc, argv);

    // Construct the Verilated model, from Vtop.h generated from Verilating "top.v".
    // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end.
    // "TOP" will be the hierarchical name of the module.
    const std::unique_ptr<Vtop> top{new Vtop{contextp.get(), "TOP"}};

    // Set Vtop's input signals
    top->reset_l = !0;
    top->clk = 0;
    top->in_small = 1;
    top->in_quad = 0x1234;
    top->in_wide[0] = 0x11111111;
    top->in_wide[1] = 0x22222222;
    top->in_wide[2] = 0x3;

    // Simulate until $finish
    while (!contextp->gotFinish()) {
        // Historical note, before Verilator 4.200 Verilated::gotFinish()
        // was used above in place of contextp->gotFinish().
        // Most of the contextp-> calls can use Verilated:: calls instead;
        // the Verilated:: versions just assume there's a single context
        // being used (per thread).  It's faster and clearer to use the
        // newer contextp-> versions.

        contextp->timeInc(1);  // 1 timeprecision period passes...
        // Historical note, before Verilator 4.200 a sc_time_stamp()
        // function was required instead of using timeInc.  Once timeInc()
        // is called (with non-zero), the Verilated libraries assume the
        // new API, and sc_time_stamp() will no longer work.

        // Toggle a fast (time/2 period) clock
        top->clk = !top->clk;

        // Toggle control signals on an edge that doesn't correspond
        // to where the controls are sampled; in this example we do
        // this only on a negedge of clk, because we know
        // reset is not sampled there.
        if (!top->clk) {
            if (contextp->time() > 1 && contextp->time() < 10) {
                top->reset_l = !1;  // Assert reset
            } else {
                top->reset_l = !0;  // Deassert reset
            }
            // Assign some other inputs
            top->in_quad += 0x12;
        }

        // Evaluate model
        // (If you have multiple models being simulated in the same
        // timestep then instead of eval(), call eval_step() on each, then
        // eval_end_step() on each. See the manual.)
        top->eval();

        // Read outputs
        VL_PRINTF("[%" PRId64 "] clk=%x rstl=%x iquad=%" PRIx64 " -> oquad=%" PRIx64
                  " owide=%x_%08x_%08x\n",
                  contextp->time(), top->clk, top->reset_l, top->in_quad, top->out_quad,
                  top->out_wide[2], top->out_wide[1], top->out_wide[0]);
    }

    // Final model cleanup
    top->final();

    // Final simulation summary
    contextp->statsPrintSummary();

    // Return good completion status
    // Don't use exit() or destructor won't get called
    return 0;
}
  1. VerilatedContext模块用来控制控制时钟的,包括运行时间、时间步进、结束等。
  2. Vtop即对应Verilog中的Top模块,通过Vtop对象可以直接操作Top模块中的信号来完成对Top模块的验证。

3.2.2. SystemC示例

SystemC库提供了更友好的接口,并且可以直接接入SystemC建模的代码来进行仿真,通用性更好。其缺点是仿真速度不如纯C++版本。

cd make_tracing_sc

make

make会完成Verilog转SystemC,编译SystemC代码,仿真运行。

3.2.2.1. 编译

make的主要功能是:

verilator -sc --exe --build -j top.v sc_main.cpp

  • -sc指示将top.v转换为SystemC代码。
  • --exe指定生成可执行文件。
  • sc_main.cpp表示这是一个SystemC的启动文件。

make -j -C obj_dir -f ../Makefile_obj

ccache g++  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=1 -DVM_SC=1 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -I/usr/local/systemc-2.3.2/include  -MMD -MP -DVL_DEBUG=1 -Wno-deprecated -Os -fstrict-aliasing -c -o sc_main.o ../sc_main.cpp
ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=1 -DVM_SC=1 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -I/usr/local/systemc-2.3.2/include  -MMD -MP -DVL_DEBUG=1 -Wno-deprecated -c -o verilated.o /usr/local/share/verilator/include/verilated.cpp
ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=1 -DVM_SC=1 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -I/usr/local/systemc-2.3.2/include  -MMD -MP -DVL_DEBUG=1 -Wno-deprecated -c -o verilated_cov.o /usr/local/share/verilator/include/verilated_cov.cpp
ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=1 -DVM_SC=1 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -I/usr/local/systemc-2.3.2/include  -MMD -MP -DVL_DEBUG=1 -Wno-deprecated -c -o verilated_vcd_c.o /usr/local/share/verilator/include/verilated_vcd_c.cpp
ccache g++ -Os  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=1 -DVM_SC=1 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -I/usr/local/systemc-2.3.2/include  -MMD -MP -DVL_DEBUG=1 -Wno-deprecated -c -o verilated_threads.o /usr/local/share/verilator/include/verilated_threads.cpp
/usr/bin/python3 /usr/local/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vtop.cpp Vtop___024root__DepSet_h84412442__0.cpp Vtop___024root__DepSet_heccd7ead__0.cpp Vtop__Trace__0.cpp Vtop___024root__Slow.cpp Vtop___024root__DepSet_h84412442__0__Slow.cpp Vtop___024root__DepSet_heccd7ead__0__Slow.cpp Vtop__Syms.cpp Vtop__Trace__0__Slow.cpp Vtop__TraceDecls__0__Slow.cpp > Vtop__ALL.cpp
echo "" > Vtop__ALL.verilator_deplist.tmp
ccache g++ -Os -fstrict-aliasing  -I.  -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=1 -DVM_SC=1 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fcf-protection=none -Wno-bool-operation -Wno-shadow -Wno-sign-compare -Wno-tautological-compare -Wno-uninitialized -Wno-unused-but-set-parameter -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable      -I/usr/local/systemc-2.3.2/include  -MMD -MP -DVL_DEBUG=1 -Wno-deprecated -c -o Vtop__ALL.o Vtop__ALL.cpp
g++     -L/usr/local/systemc-2.3.2/lib-linux64 sc_main.o verilated.o verilated_cov.o verilated_vcd_c.o verilated_threads.o Vtop__ALL.a    -pthread -lpthread -latomic  -lsystemc -o Vtop

  • sc_main.cpp是SystemC的testbench文件。
  • verilated.cpp,verilated_cov.cpp,verilated_vcd_c.cpp,verilated_threads.cpp是Verilator的源码,主要提供时钟模块、覆盖率模块、vcd波形生成模块,以及线程并行模块。
  1. Python脚本是将top.v生成的C++代码全部合并到Vtop__ALL.cpp,方便编译。
  2. 最后编译链接生成可执行文件Vtop。
3.2.2.2. 仿真运行

仿真运行会打印SystemC的版本信息。

通过GDB,我们可以看到真实的main入口函数是在SystemC模块中的。

3.2.2.3. testbench代码

int sc_main(int argc, char* argv[]) {
    // This is a more complicated example, please also see the simpler examples/make_hello_c.

    // Create logs/ directory in case we have traces to put under it
    Verilated::mkdir("logs");

    // Set debug level, 0 is off, 9 is highest presently used
    // May be overridden by commandArgs argument parsing
    Verilated::debug(0);

    // Randomization reset policy
    // May be overridden by commandArgs argument parsing
    Verilated::randReset(2);

#if VM_TRACE
    // Before any evaluation, need to know to calculate those signals only used for tracing
    Verilated::traceEverOn(true);
#endif

    // Pass arguments so Verilated code can see them, e.g. $value$plusargs
    // This needs to be called before you create any model
    Verilated::commandArgs(argc, argv);

    // General logfile
    std::ios::sync_with_stdio();

    // Define clocks
    sc_clock clk{"clk", 10, SC_NS, 0.5, 3, SC_NS, true};
    sc_clock fastclk{"fastclk", 2, SC_NS, 0.5, 2, SC_NS, true};

    // Define interconnect
    sc_signal<bool> reset_l;
    sc_signal<uint32_t> in_small;
    sc_signal<uint64_t> in_quad;
    sc_signal<sc_bv<70>> in_wide;
    sc_signal<uint32_t> out_small;
    sc_signal<uint64_t> out_quad;
    sc_signal<sc_bv<70>> out_wide;

    // Construct the Verilated model, from inside Vtop.h
    // Using unique_ptr is similar to "Vtop* top = new Vtop" then deleting at end
    const std::unique_ptr<Vtop> top{new Vtop{"top"}};

    // Attach Vtop's signals to this upper model
    top->clk(clk);
    top->fastclk(fastclk);
    top->reset_l(reset_l);
    top->in_small(in_small);
    top->in_quad(in_quad);
    top->in_wide(in_wide);
    top->out_small(out_small);
    top->out_quad(out_quad);
    top->out_wide(out_wide);

    // You must do one evaluation before enabling waves, in order to allow
    // SystemC to interconnect everything for testing.
    sc_start(SC_ZERO_TIME);

#if VM_TRACE
    // If verilator was invoked with --trace argument,
    // and if at run time passed the +trace argument, turn on tracing
    VerilatedVcdSc* tfp = nullptr;
    const char* flag = Verilated::commandArgsPlusMatch("trace");
    if (flag && 0 == std::strcmp(flag, "+trace")) {
        std::cout << "Enabling waves into logs/vlt_dump.vcd...\n";
        tfp = new VerilatedVcdSc;
        top->trace(tfp, 99);  // Trace 99 levels of hierarchy
        Verilated::mkdir("logs");
        tfp->open("logs/vlt_dump.vcd");
    }
#endif

    // Simulate until $finish
    while (!Verilated::gotFinish()) {
#if VM_TRACE
        // Flush the wave files each cycle so we can immediately see the output
        // Don't do this in "real" programs, do it in an abort() handler instead
        if (tfp) tfp->flush();
#endif

        // Apply inputs
        if (sc_time_stamp() > sc_time(1, SC_NS) && sc_time_stamp() < sc_time(10, SC_NS)) {
            reset_l = !1;  // Assert reset
        } else {
            reset_l = !0;  // Deassert reset
        }

        // Simulate 1ns
        sc_start(1, SC_NS);
    }

    // Final model cleanup
    top->final();

    // Close trace if opened
#if VM_TRACE
    if (tfp) {
        tfp->close();
        tfp = nullptr;
    }
#endif

    // Coverage analysis (calling write only after the test is known to pass)
#if VM_COVERAGE
    Verilated::mkdir("logs");
    VerilatedCov::write("logs/coverage.dat");
#endif

    // Return good completion status
    return 0;
}
  1. Verilated空间的接口用来控制环境,包括覆盖率、波形、参数处理、环境结束等。
  2. Vtop对应Verilog的Top模块,可以直接操作Top模块的信号来进行赋值验证。
  3. Top模块的信号,必须使用SystemC的风格来进行设置修改。

C++版本的代码思路和SystemC版本的思路基本一样,只是代码的操作形式略有差异。SystemC版本可以更方便地直接调用SystemC的建模代码。

3.2.3. 纯verilog示例

如果Top模块不需要传输信号,则可以使用纯verilog模块,即不需要编写C++代码。

Verilator将verilog转换为C++,并生成一个简单的main.cpp文件作为启动文件。其编译过程:

verilator --binary -j 0 top.v

3.2.4. 生成波形

生成波形需要VerilatedVcdSc模块。

    if (flag && 0 == strcmp(flag, "+trace"))
    {
        tfp = new VerilatedVcdSc;
        top->trace(tfp, 0); 
        tfp->open("wave.vcd");
    }

在仿真运行时:

./obj_dir/Vtop +trace

3.2.5. 查看波形

gtkwave wave.vcd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值