verilator手册中 9.1.8 How do I generate waveforms (traces) in C++?

9.1.8 How do I generate waveforms (traces) in C++?

See also the next question for tracing in SystemC mode.

A. Pass the --trace option to Verilator

  1. Pass the --trace option to Verilator. Then you may use $dumpfile and $dumpvars to enable traces, the same as with any Verilog simulator, although Verilator ignores the arguments to $dumpvars.

  2. See the example in examples/make_tracing_c in the distribution.

  3. If writing the top-level C code, call Verilated::traceEverOn(true) to enable tracing. This is done for you if using --binary.

B. Or, for finer-grained control, or C++ files with multiple Verilated modules

You may also create the trace purely from C++. Create a VerilatedVcdC object, and in your main loop, right after eval(), call trace_object->dump(contextp->time()) every time step, and finally call trace_object->close().

#include "verilated_vcd_c.h"
...
int main(int argc, char** argv) {
    const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
    ...
    Verilated::traceEverOn(true);  // Enable tracing
    VerilatedVcdC* tfp = new VerilatedVcdC;
    topp->trace(tfp, 99);  // Trace 99 levels of hierarchy (or see below)
    // tfp->dumpvars(1, "t");  // trace 1 level under "t"
    tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
    ...
    while (contextp->time() < sim_time && !contextp->gotFinish()) {
        contextp->timeInc(1);  // Increment simulation time
        topp->eval();  // Evaluate the module
        tfp->dump(contextp->time());  // Dump trace at current time
    }
    tfp->close();  // Close the trace file
}

9.1.8 如何在 C++ 中生成波形(Trace)

参见下一个问题,了解如何在 SystemC 模式下进行跟踪。

步骤A. 使用 --trace 选项生成波形

  1. 通过将 --trace 选项传递给 Verilator 来启用波形生成。然后可以使用 $dumpfile$dumpvars 来启用波形输出,就像在任何 Verilog 仿真器中一样,尽管 Verilator 会忽略 $dumpvars 的参数。

  2. 参考 examples/make_tracing_c 中的示例。

  3. 如果你编写的是顶层 C 代码,调用 Verilated::traceEverOn(true) 来开启跟踪功能。如果你使用的是 --binary,则此操作已经为你完成。

步骤B. 通过 C++ 进行更细粒度的控制

如果你的项目包含多个 Verilated 模块,并且你想要更细粒度的控制,可以完全通过 C++ 代码来生成波形。具体步骤如下:

  1. 创建一个 VerilatedVcdC 对象。
  2. 在每个仿真步骤中,调用 trace_object->dump(contextp->time()) 来记录当前时间步的波形数据。
  3. 最后,调用 trace_object->close() 来关闭波形文件。

官方文档中所附带的代码:

#include "verilated_vcd_c.h"
...
int main(int argc, char** argv) {
    const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
    ...
    Verilated::traceEverOn(true);  // 启用波形生成
    VerilatedVcdC* tfp = new VerilatedVcdC;
    topp->trace(tfp, 99);  // 追踪 99 层的层级(或者参考下文)
    // tfp->dumpvars(1, "t");  // 追踪 "t" 下的一层
    tfp->open("obj_dir/t_trace_ena_cc/simx.vcd");
    ...
    while (contextp->time() < sim_time && !contextp->gotFinish()) {
        contextp->timeInc(1);  // 增加仿真时间
        topp->eval();  // 评估模块
        tfp->dump(contextp->time());  // 记录波形
    }
    tfp->close();  // 关闭波形文件
}

示例代码(含注释,Verilog文件为top.v,顶层模块名为top):

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// assert.h 是一个标准 C 语言头文件,它定义了 assert 宏,用于在程序中插入断言 (assertion)。
// 断言的作用是帮助程序员验证程序在运行时的假设是否成立,主要用于调试。
#include "Vtop.h"
#include "verilated.h"

#include "verilated_vcd_c.h"// 引入 Verilator VCD 追踪库

int main(int argc, char** argv) {
    // 创建并初始化一个 Verilator 仿真上下文 contextp
    VerilatedContext* contextp = new VerilatedContext;
    contextp->commandArgs(argc, argv);

    // 创建顶层模块实例
    Vtop* top = new Vtop{contextp};

    // 启用波形跟踪,这一行代码启用了波形追踪功能。它必须在仿真开始之前调用,确保 Verilator 会生成波形数据。
    // 这个函数告诉 Verilator 启用波形记录并为每个仿真步骤生成数据。
    Verilated::traceEverOn(true);  // 必须在仿真时间开始之前调用,因此它通常位于仿真初始化部分。

    // 创建 VCD 文件输出对象
    // 这里创建了一个 VerilatedVcdC 对象,负责管理和输出仿真波形数据。VerilatedVcdC 是 Verilator
    // 提供的类,用于将仿真数据转储到 .vcd 文件中。
    VerilatedVcdC* tfp = new VerilatedVcdC;

    // 配置追踪等级
    // top->trace() 用来设置我们要追踪的信号。top 是我们实例化的 Verilog 顶层模块对象,trace() 函数将 tfp
    //  传入,以便将波形数据保存到 tfp 指定的 VCD 文件中。
    // 99 表示信号的层级深度。一般来说,设置为 99
    //  可以追踪所有信号(包括所有子模块的信号)。这个参数可以根据需要调整,通常设置为 99 以确保捕获完整的波形。
    top->trace(tfp, 99); // Trace 99 levels of hierarchy

    // 这一行打开一个名为 waveform.vcd 的文件,将仿真波形写入这个文件。
    // VCD(Value Change Dump)文件是一种标准的波形格式,可以被许多波形查看工具(如 GTKWave)读取。open()
    //  函数会打开文件供写入,如果文件不存在,Verilator 会创建该文件。
    tfp->open("waveform.vcd"); // 打开 VCD 文件

    // 仿真主循环
    while (!contextp->gotFinish()) {
        int a = rand() & 1;
        int b = rand() & 1;
        top->a = a;
        top->b = b;
        top->eval();// eval() 会根据当前信号和状态更新设计的状态,并执行任何时序或组合逻辑的计算。
        printf("a = %d, b = %d, f = %d\n", a, b,top->f);
        assert(top->f == (a ^ b)); // 检查 XOR 输出是否正确

        tfp->dump(contextp->time()); // 记录当前时间的波形数据
        contextp->timeInc(1);// 增加时间步长
    }

    // 关闭 VCD 文件
    // 仿真结束后,调用 close() 关闭 VCD 文件。关闭文件是确保所有数据被正确保存的重要步骤。
    tfp->close();

    // 释放资源
    delete top;
    delete contextp;

    return 0;
}
posted @ 2025-12-05 10:51  阿坤不咕  阅读(0)  评论(0)    收藏  举报