一生一芯 预学习阶段 verilator学习以及双控开关的示例详解

一、简介

上一篇文章已经讲过了verilator官方提供的C++示例。

链接:一生一芯 预学习阶段 搭建verilator仿真环境 以及 运行官方示例-CSDN博客

在一生一芯学习的教程文档中也有一个双控开关的示例,但是提供的代码需要我们自己进行修改才能完善。源代码如下,这是不能正常仿真的:

因此接下来分两步来完成这个示例:

  • 1、学习verilator,以及它的操作(会有一些拓展)

(本部分借用verilator官方例程学习,链接:Example C++ Execution — Verilator Devel 5.027 documentation);

  • 2、完成该示例。

二、详解

1、verilator的学习

1)、Verilator 基本介绍

Verilator 本质上是一个 Verilog / SystemVerilog 仿真器。 它速度快,免费,开源。但不能直接替代 Modelsim、Questa Sim、Synopsys VCS、Vivado Xsim 和其他基于事件的仿真器。 Verilator 是一种基于周期的仿真器,这意味着它不会评估单时钟周期内的时间,也不会仿真精确的电路时序, 电路状态通常在每个时钟周期评估一次,因此无法观察到周期内的任何故障,也不支持定时信号延迟。 

Verilator 要求将 C++ 测试平台编译成本地系统二进制文件。

但是,我们不能将我们的 Verilog / System Verilog  模块原封不动地加入 C++ 测试平台,我们首先需要使用 Verilator 将 Verilog / System Verilog 代码转换为 C++,或者说 "Verilate"(验证)它。

也就是说Verilator 是把 .v 的文件编译成C++文件,再进行验证。

执行如下代码后就将所有编译后的文件加入到 obj_dir文件中:

 verilator --cc example.v

会生成很多类似文件:

$ ls -l obj_dir/
Vexample___024unit.cpp  Vexample___024unit__Slow.cpp  Vexample.cpp  Vexample.mk
Vexample__Syms.cpp  Vexample__ver.d Vexample___024unit.h    Vexample_classes.mk
Vexample.h    Vexample__Slow.cpp  Vexample__Syms.h    Vexample__verFiles.dat

  • .mk 文件将与 Make 一起用于构建我们的仿真可执行文件;
  • .h .cpp 文件包含我们的 C++ 头文件和实现源代码;
  • Vexample.h 这是主设计头文件,包含转换后的 "example "类定义,将在 C++ 测试平台中将其 "实例化 "为 DUT;
  • Vexample___024unit.h 这是 "example "类的内部头文件,其中包含 operation_t 类型定义;

到此DUT模块算是完成。

接下来看testbench(C++)

官方说明文档给出的C++示例:

 #include "Vour.h"
 #include "verilated.h"
  int main(int argc, char** argv) {
      VerilatedContext* contextp = new VerilatedContext;
      contextp->commandArgs(argc, argv);
      Vour* top = new Vour{contextp};
      while (!contextp->gotFinish()) { top->eval(); }
      delete top;
      delete contextp;
      return 0;
  }

先看头文件:

我们需要include Verilator 安装时附带的 <verilated.h> 以访问常用的 Verilator 例程,

另外还有 #include  <verilated_vcd_c.h>,将波形写入 VCD文件;

"Vour.h "包含了我们的 Verilated  ALU 模块的顶层类;

还可以# include "Valu___024unit.h", 包含了我们的 typedef 枚举的 Verilated 版本。

头文件内容到此。

接下来是比较重要的主函数:

# 是这一部分  
int main(int argc, char** argv) {
      VerilatedContext* contextp = new VerilatedContext;
      contextp->commandArgs(argc, argv);
      Vour* top = new Vour{contextp};

      /*<......>*/

      delete top;
      delete contextp;
      return 0;
  }

这一部分

Vour* top = new Vour{contextp};

这样的代码,这个代码就类似于SV里面的 example dut_1 (.*); 也就是将模块例化进去。

其中 dut 就是例化的名字。

这一行代码

VerilatedContext* contextp = new VerilatedContext;
contextp->commandArgs(argc, argv);

用 VerilatedContext 创建一个 contextp 的对象,contextp 的对象里面有commandArgs方法;里面有argc和argv两个参数。

这一行:

Vour* top = new Vour{contextp};

用 Vour 的类创建一个 top 的对象并new 一下,Vour应该是来源于 "Vout.h" 的头文件。

最后的几行:

delete top;
delete contextp;
return 0;

就是清空内存。

2)、示例完成代码

 DUT模块代码:

module switch(
  input a,
  input b,
  output f
);
  assign f = a ^ b;
endmodule

sim.cpp文件代码(我做了两种写法):

 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include "Vswitch.h"
 
 int main(int argc, char** argv){

   VerilatedContext* contextp = new VerilatedContext;                        
   contextp->commandArgs(argc, argv);
   Vswitch* top = new Vswitch{contextp};

   while (!contextp->gotFinish()){
     int a = rand() & 1;
     int b = rand() & 1;
     top->a = a;
     top->b = b;
     top->eval();
     printf("a = %d, b = %d, f = %d\n", a, b, top->f);
     assert(top->f == (a ^ b));
   }

   delete top;
   delete contextp;
   return 0;

 }
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Vswitch.h"
 
int main(int argc, char** argv) {

  Verilated::commandArgs(argc,argv);
  Vswitch *top = new Vswitch("top");

  while (!Verilated::gotFinish()){
    int a = rand() & 1;
    int b = rand() & 1;
    top->a = a;
    top->b = b;
    top->eval();
    printf("a = %d, b = %d, f = %d\n", a, b, top->f);                       
    assert(top->f == (a ^ b));
  }

  delete top;
  return 0;
}

我认真为这两种区别就是argc和argv这两个参数能不能单独访问。

同时argc和argv这两个参数的值应该就来自于终端中输入的参数。

两个模块写完之后执行如下代码:

$ verilator --cc --exe --build -j 0 -Wall sim.cpp switch.v
$ obj_dir/Vswitch

终端中就会有结果不断输出,如图:

因为不会停止,所以要按 <Ctrl + c> 。

关于不会停止的问题,应该是 !contextp->gotFinish() 的问题;

gotFinish() 是 VerilatedContext 类的一个成员函数,它用于检测仿真是否收到了结束信号(如 $finish 或 $stop 语句)

因为DUT中没有相关可以暂停的语句,因此我做了如下修改:

 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include "Vswitch.h"
 
 int main(int argc, char** argv){

   VerilatedContext* contextp = new VerilatedContext;                        
   contextp->commandArgs(argc, argv);
   Vswitch* top = new Vswitch{contextp};
   
   int counter = 0;

   while (!contextp->gotFinish()){
     int a = rand() & 1;
     int b = rand() & 1;
     top->a = a;
     top->b = b;
     top->eval();
     printf("a = %d, b = %d, f = %d\n", a, b, top->f);
     assert(top->f == (a ^ b));
     counter++;
   }

   delete top;
   delete contextp;
   return 0;

 }

实现了值计算十次后暂停,结果如下:

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值