Verilator入门(一)


Verilator可将硬件描述语言Verilog/SystemVerilog编译成优化后的支持多线程的高级语言C++/SystemC模型,通过在高级语言中例化硬件描述语言,进而使用高级语言的特性实现对Verilog/SystemVerilog的验证。简单来说,Verilator对Verilog/SystemVerilog等硬件描述语言添加了一个基于C++/SystemC的wrapper,而后借助C++和Verilator的特定库实现对Verilog/SystemVerilog的高效仿真。

Verilator 本质上是一个 Verilog/SystemVerilog 模拟器。它具有商业级、超快速、免费和开源等特性,但是它不能直接替代 Modelsim、 Questa Sim、 Synopsys VCS、 Vivado Xsim 和其他基于事件的模拟器。Verilator 是一个基于周期的模拟器,这意味着它不能在单个时钟周期内计算时间,也不能模拟精确的电路时序。因此,在Verilator中电路状态通常每个时钟周期评估一次,因此不能观察到任何周期内的小故障,并且不支持定时信号延迟。

速度特性

由于Verilator是基于周期的,因此它不能用于时序模拟、反向注释网表、异步(无时钟)逻辑,或者任何涉及时间概念的信号变化。无论何时评估电路,所有输出都会瞬间切换。

然而,由于时钟边缘之间的一切都被忽略了,Verilator 的模拟运行速度非常快(比SystemC模型快10倍左右,比Verilog仿真器快100倍左右,且支持多线程),在模拟具有一个或多个时钟的同步数字逻辑电路的功能,或从Verilog/SystemVerilog 代码中创建软件模型以用于软件开发方面非常有效。

代码特性

由于 verilator 是基于周期的,它不能完全支持 IEEE Verilog 和 SystemVerilog 标准(综合工具通常也不是完全兼容的)。Verilator 对提供的 Verilog/SystemVerilog 代码非常严格。除了不支持任何时间延迟,它也不会接受大多数不可综合的代码 ,因而不能简单地用Verilator来综合基于 SystemVerilog的 测试平台。

因为 Verilator 不支持不可综合的代码,所以与其他模拟器相比,它更接近于综合工具的行为,这带来了意想不到的好处。它将迫使用户编写更好的可综合代码,从而有可能减少在后续开发过程中可能会遇到的问题数量。

1. 安装Verilator

参考官方手册

# Prerequisites:
#sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache
#sudo apt-get install libgoogle-perftools-dev numactl perl-doc
#sudo apt-get install libfl2  # Ubuntu only (ignore if gives error)
#sudo apt-get install libfl-dev  # Ubuntu only (ignore if gives error)
#sudo apt-get install zlibc zlib1g zlib1g-dev  # Ubuntu only (ignore if gives error)

git clone https://github.com/verilator/verilator   # Only first time

# Every time you need to build:
unsetenv VERILATOR_ROOT  # For csh; ignore error if on bash
unset VERILATOR_ROOT  # For bash
cd verilator
git pull         # Make sure git repository is up-to-date
git tag          # See what versions exist
#git checkout master      # Use development branch (e.g. recent bug fixes)
#git checkout stable      # Use most recent stable release
#git checkout v{version}  # Switch to specified release version

autoconf         # Create ./configure script
./configure   --prefix /opt/verilator_path   # Configure install path and create Makefile
make -j `nproc`  # Build Verilator itself (if error, try just 'make')
sudo make install

安装后检查版本信息:

yyx@yyx-virtual-machine:~/riscv/projects/LITEX/litex$ verilator --version
Verilator 4.028 2020-02-06 rev v4.026-92-g890cecc1

2. 上手Verilator

/****** alu.sv ******/
typedef enum logic [1:0] {
     add     = 2'h1,
     sub     = 2'h2,
     nop     = 2'h0
} operation_t /*verilator public*/;

module alu #(
        parameter WIDTH = 6
) (
        input clk,
        input rst,

        input  operation_t  op_in,
        input  [WIDTH-1:0]  a_in,
        input  [WIDTH-1:0]  b_in,
        input               in_valid,

        output logic [WIDTH-1:0]  out,
        output logic              out_valid
);

        operation_t  op_in_r;
        logic  [WIDTH-1:0]  a_in_r;
        logic  [WIDTH-1:0]  b_in_r;
        logic               in_valid_r;
        logic  [WIDTH-1:0]  result;

        // Register all inputs
        always_ff @ (posedge clk, posedge rst) begin
                if (rst) begin
                        op_in_r     <= '0;
                        a_in_r      <= '0;
                        b_in_r      <= '0;
                        in_valid_r  <= '0;
                end else begin
                        op_in_r    <= op_in;
                        a_in_r     <= a_in;
                        b_in_r     <= b_in;
                        in_valid_r <= in_valid;
                end
        end

        // Compute the result
        always_comb begin
                result = '0;
                if (in_valid_r) begin
                        case (op_in_r)
                                add: result = a_in_r + b_in_r;
                                sub: result = a_in_r + (~b_in_r+1'b1);
                                default: result = '0;
                        endcase
                end
        end

        // Register outputs
        always_ff @ (posedge clk, posedge rst) begin
                if (rst) begin
                        out       <= '0;
                        out_valid <= '0;
                end else begin
                        out       <= result;
                        out_valid <= in_valid_r;
                end
        end

endmodule;

上述代码的预期结果如下:

在这里插入图片描述

2.1 模型转换

使用下面的指令可将alu.sv转换成对应的C++模型,其中的—cc参数指明转换的目的语言为C++,同样,也可使用—sc参数转换为对应的SystemC模型。

verilator --cc [alu.sv](http://alu.sv/)

另外,通过运行指令”verilator —help“,可查看Verilator的所有内置参数的含义,用于日常使用查询,如下:

在这里插入图片描述

上述指令运行结束后,会生成一个obj_dir目录,该目录下包含了C++模型等一系列文件,且认真观察就会发现,大部分文件的命名都是以”V+模块名称”的前缀形式。如下:

在这里插入图片描述

其中主要需要关注如下几个文件:

  • Valu.h:该文件是生成C++模型的基本头文件,包含了alu类的声明,用于在测试中声明dut。
  • Valu___024unit.h:alu类的内部头文件,包含了alu中operation_t类型的声明。

2.2 构建测试文件

基于上述alu文件,一个基本的基于Verilator C++测试文件如下:

#include <stdlib.h>
#include <iostream>
#include <verilated.h>   //Verilator库的头文件
#include <verilated_vcd_c.h>  //VCD波形输出头文件
#include "Valu.h"
#include "Valu___024unit.h"

#define MAX_SIM_TIME 200 //定义模拟的时钟边沿数(包括上下边沿)
vluint64_t sim_time = 0;

int main(int argc, char** argv, char** env) {
    // 实例化一个 Valu 类型的对象 dut
    Valu *dut = new Valu;

    // 开启波形跟踪
    Verilated::traceEverOn(true);
    // 实例化一个 VerilatedVcdC 类型的对象 m_trace,用于波形跟踪
    VerilatedVcdC *m_trace = new VerilatedVcdC;
    // 将 m_trace 与 dut 进行关联,其中5表示波形的采样深度为5级以下
    dut->trace(m_trace, 5);
    m_trace->open("waveform.vcd");

    int a,b,invalid;
    while (sim_time < MAX_SIM_TIME) {// 循环执行仿真
        dut->clk ^= 1;// 取反时钟信号

        if(dut->clk == 0){
            a = rand() % 32;// 生成随机数 a
            b = rand() % 32;// 生成随机数 b
            dut->a_in = a;
            dut->b_in = b;
            dut->op_in = rand() % 3;
            invalid = rand() % 2;
            dut->in_valid = invalid;
        }

        //调用eval(), 计算 ALU 模块中的所有信号值
        dut->eval();

        if(dut->clk == 0 && invalid == 1){
            printf("sim_time = %ld,a = %d, b = %d, out = %d out_valid = %d\n",sim_time, a, b, dut->out,dut->out_valid);
        }
        
        //将所有跟踪的信号值写入波形转储文件
        m_trace->dump(sim_time);
        sim_time++; // 模拟时钟边沿数加1
    }
    // 关闭波形文件
    m_trace->close();
    delete dut;
    exit(EXIT_SUCCESS);
}

需要注意以下几点:

  • dut→clk不需要初始化,直接异或进行时钟取反操作;
  • 仔细观察可知,MAX_SIM_TIME并不是指仿真时间,而是指时钟跳变的次数。而在Verilator中,跳变一次默认是1ns,因而在上述的程序中,仿真时间为200ns,对应100个时钟周期,一个时钟周期为2ns。
  • 结合上一条时钟说明,通过if(dut→clk==0),使输入数据一个时钟周期跳变一次,如果不加判断,会导致数据一个时钟跳变就变化一次。

2.3 构建仿真脚本

为了生成仿真波形文件,需要通过运行仿真、编译和运行可执行文件三个步骤。

1.运行仿真,执行指令:

verilator -Wall $(TOP).sv main.cpp --cc --exe --trace --top-module $(TOP)

其中,TOP为顶层文件文件名称变量,此处为alu。

-Wall参数用于打开运行仿真过程中CPP文件中的所有警告信息。同样,也存在关闭运行过程中的警告信息参数-Wno_fatal。例如,在使用-Wall参数情况下,当打印仿真时间sim_time的值时,若采用%d的输出格式,会出现如下警告信息:

在这里插入图片描述

而当使用-Wno-fatal参数时,会隐藏上述警告信息。

—exe:声明创建可执行文件;

—trace:打开波形跟踪功能;

—top-module:声明顶层文件名

2.编译

make -C obj_dir -f V$(TOP).mk V$(TOP)

-C:指定执行的文件目录

-f:指定make执行的文件

即在当前obj_dir目录的Valu.mk文件中构建Valu目标,生成Valu可执行,在本例程中生成的Valu.mk文件的Valu规则如下:

在这里插入图片描述

3.运行可执行文件

运行指令:

./obj_dir/Valu

最终整合在Makefile文件中如下:

default: compile
TOP ?= alu

obj_dir/V$(TOP).mk:
	verilator -Wall $(TOP).sv main.cpp --cc --exe --trace --top-module $(TOP)

compile: obj_dir/V$(TOP).mk
	make -C obj_dir -f V$(TOP).mk V$(TOP)  

run:compile
	obj_dir/V$(TOP)
	
.PHONY: default compile clean run

clean:
	rm -rf obj_dir

3. 波形查看

采用gtkwave软件,查看波形指令如下:

gtkwave waveform.vcd

在这里插入图片描述

4. 参考链接

https://itsembedded.com/dhd_list/

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值