对Verilator的定位
verilator并不是一个传统的仿真器,而是一个编译器,用于将hdl代码编译为C++等语言进行执行。
PS:什么是lint?
在计算机科学中,lint是一种工具程序的名称,它用来标记源代码中,某些可疑的、不具结构性(可能造成bug)的段落。 它是一种静态程序分析工具,最早适用于C语言,在UNIX平台上开发出来。 后来它成为通用术语,可用于描述在任何一种计算机程序语言中,用来标记源代码中有疑义段落的工具。
运作原理
- 读取hdl代码,对其进行分析,并利用其构建一个C++仿真模型,该模型可选择加入覆盖率分析、绘制波形图等功能;
- 为了构建仿真模型,需要用户手动编写仿真main文件,在其中实例化一个C++对象,作为仿真验证模型;
- 将用户编写的main文件(wrapper)、verilator创建的文件、verilator提供的运行时库一并使用C++编译器进行编译,得到一个可执行仿真文件;
- 这个可执行仿真文件将真正实现仿真器的功能
对在shell中发起的一条verilator指令进行解析
verilator --cc --exe --build -j 0 -Wall sim_main.cpp our.v
- 其中
--cc
的含义是表明最终的输出模型是c++模型;
--exe
一般和wrapper file一并使用,表示需要构建一个可执行文件,而非仅仅创建一个库;
-j 0
to Verilate using use as many CPU threads as the machine has.:可能跟cpu线程有关系,用于调高verilator性能;
-Wall
so Verilator has stronger lint warnings enabled.
常用Verilator参数
--top/--top-module <filename>
显示指定顶层模块的文件名称,一般用于同时拥有多个顶层模块的仿真当中;
--Mdir <path>
显示指明仿真验证模型的目标文件,默认情况下是obj_dir;
--prefix
应该是用来显示指定目标文件的前缀的,但具体用法并不是很清楚;
--exe
,加入该参数后,verilator最终会构建一个可执行仿真文件,如果不使用该参数,则只会产生一个.a文件;
将两种情况产生的结果进行对比,对比后可以理解,只要使用该参数,verilator则会将sim_main.cpp与其产生的文件进行链接,得到一个可执行文件;


--build
该参数会调用make来构建仿真模型:删掉该选项,得到如下结果;

可见并没有生成.a文件,即连基本的编译都没有进行。但是仍旧存在的疑问是,使用该选项调用的第一个makefile文件是什么;
连接Verilated module
Verilator会输出一个
prefix.h
文件,里面定义了一个prefix
类,在main函数中,你需要去实例化这个类,并将其作为Verialted module的接口;输出文件中会包括一个
prefix.mk
文件,其作用是利用make构建prefix__All.a
库文件;产生的模型类会管理模型所有的内部状态,并将如下接口暴露出来:
- 顶级模块的io端口;
- Public top level module instances are exposed as pointers to allow access to
/* verilator public */
items.
- The root of the design hierarchy (as in SystemVerilog $root) is exposed via the rootp member pointer to allow access to model internals, including /* verilator public_flat */ items.
Wrappers编写及仿真循环
当使用C++对模型进行评估时,需要调用
eval() eval_step() eval_end_step()
这一类函数。- 如果只有一个设计实例,在一个给定上下文中,需要调用
designp->eval()
;
- 当有多个设计实例时,需要逐个调用
designsp->eval_step()
并在其后调用designsp->eval_end_step()
.
- 当
eval()等函数
被调用时,Verilator会寻找时钟信号的改变,并且评估与之相关的,顺序的always块;随后Verilator会评估组合逻辑;
- 文档中有一句话并不是很明白:
Alternatively, if all always_ff statements use only the posedge of clocks, or all inputs go directly to always_ff statements, as is typical, then you can change non-clock inputs on the negative edge of the input clock, which will be faster as there will be fewer eval() calls.
- 更多评估信息,可以看发行版中的docs/internals.rst文件.
Verilated 以及 VerilatedContext
多个C++ Verilated module可能会共享同一个仿真context。这个通用的仿真context信息被存储在一个
VerilatedContext
结构中。如果VerilatedContext没有先于模型被创建,那么一个默认的全局变量将被自动创建。有一中VerilatedContext方法的调用细则如下:
- 可以使用
Verilated::<methods>
来调用方法,包括Verilated::commandArgs
,这种调用是使用默认的全局VerilatedContext变量;如果你使用了多个仿真context,你不应该这样使用,应该为相应的VerilatedContext对象调用函数,例如:
- 更多内容可以查看
include/verilated.h
.
仿真细则(Verilated-Model Runtime)
暂略
如何使用C++产生仿真波形
方法A
- 使用
--trace
选项调用verilator;
- 在顶级C++代码中,利用context,调用Verilated::traceEverOn(true)(默认)。
- 然后在verilog代码中利用
$dumpfile
和$dumpvars
去使能traces;
方法B
- 创建VerilatedVcdC对象;(需要包含verilated_vcd_c.h头文件)
- 在main仿真循环中,在
eval()
之后调用trace_object->dump(contextp->time())
,在每一个time step,并在最后调用trace_object->close()
相关API
topp->trace()
: 应该是起到一个初始化trace的作用;
tfp->open()
: 打开波形图文件
contextp->timeInc(1)
: 1 timeprecision period passes
contextp->time()
: 获取仿真时间
tfp->dump(contextp->time())
:每个time step保存一下;
contextp->gotFinish()
tfp->close()
: 关闭tfp;