前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VCS入门教程(二)

VCS入门教程(二)

作者头像
数字芯片社区
发布2020-09-04 17:33:04
3.2K0
发布2020-09-04 17:33:04
举报
文章被收录于专栏:数字芯片数字芯片

本文授权转发自知乎用户 橘子汽水 链接:https://www.zhihu.com/people/xing-qi-55-65/posts

一、前言

本文主要介绍VCS进行verilog代码debug的基本方法。

二、三种方法

1. 使用系统函数

首先我们在编写verilog模块的testbench时,可以在里面使用一些verilog的系统函数,在运行simv文件跑仿真时,进行一些控制。例如:

$time 代表当前的仿真时间。

$display 类似C语言的printf函数,仿真时在终端上打印一些信息,比如一些变量的值。

monitor 和display类似,不同的是display在被调用的时候打印一些信息,monitor可以自动监测变量,当变量值发生变化时,便打印出信息。

$stop 调用时使仿真产生一次中断。

$finish 调用时使仿真结束。

$readmemb 用于存储器建模时的初始化,将一个文本文件里的数据,写入存储器。

readmemh readmemb 以二进制数的形式写入,

下面来看下VCS Labs 里lab1/parta 下addertb.v 的内容。

module addertb;
reg [7:0] a_test, b_test;
wire [7:0] sum_test;
reg cin_test;
wire cout_test;
reg [17:0] test;

add8 u1(a_test, b_test, cin_test, sum_test, cout_test);

initial begin                
$monitor("time = %d, a = %h, b = %h, sum = %h;  cin = %h, cout = %h",
            $time, a_test, b_test, sum_test, cin_test, cout_test);
end

initial
begin
  for (test = 0; test <= 18'h1ffff; test = test +1) begin
    cin_test = test[16];
    a_test = test[15:8];
    b_test = test[7:0];
    #50;
    if ({cout_test, sum_test} !== (a_test + b_test + cin_test)) begin
      $display("***ERROR at time = %0d ***", $time);
      $display("a = %h, b = %h, sum = %h;  cin = %h, cout = %h",
               a_test, b_test, sum_test, cin_test, cout_test);
      $finish;
    end
   #50;
  end
  $display("*** Testbench Successfully completed! ***");
  $finish;
end
endmodule

上面是一个二输入加法器的testbench,a_test和b_test 为输入,sum_test为和,cin_test为来自低位的进位输入,cout_test为向高位的进位输出。本人在里面新增了$monitor 的部分。

运行仿真时,每延迟100个时间单位,test的值变化一次,使输入发生一次变化。monitor 监测四个变量,当任何一个发生变化时,打印出输入输出和当前仿真时间的值。当输入输出不满足加法关系时,调用display函数打印错误信息。并使用

下面看一个使用 $readmemb 的例子

`timescale 1ns/10ps
module myrom (read_data, addr, read_en_);
    input read_en_;
    input [3:0] addr;
    output [3:0] read_data;
    reg [3:0] read_data;
    reg [3:0] mem [0:15];
    initial
        $readmemb ("my_rom_data", mem);
    always @( addr or read_en_)
        if (! read_en_)
            read_data = mem[addr];
endmodule

my_rom_data 文件里的内容:

0000
0101
1100
0011
1101
0010
0011
1111
1000
1001
1000
0001
1101
1010
0001
1101

在上述ROM建模中,使用 $readmemb 将文本数据写入ROM来进行初始化。利用系统函数进行debug的方式大概介绍到这里。

2. 使用UCLI (用户命令行接口)

使用 lab2/partb 里面的源码,addertb.v 与上面的代码一致,就是将monitor部分去掉了。在判断发生error的地方,将finish 更改为

图 1

图 2

在编译指令中加入 -ucli 使用UCLI。

图 3

在仿真时会打开UCLI,并使仿真停止在 0 时刻。

图4

使用 run 继续运行仿真,当出错时,$stop被调用而使仿真停下。使用 scope 查看当前 module 名。使用 show 查看信号列表。使用 get sum_test -radix hex 查看当前某个信号的值。

使用 UCLI 进行Debug其实是非常低效的,使仿真在错误的地方停止,用命令打开一个一个“黑盒子”(module) 并查看内部信号与预期是否一致。在实际使用VCS的时候基本不用,在此简单介绍,不做过多赘述。

3. 使用DVE

在前面我们已经使用命令 ./simv -gui 。以图形化界面的方式运行仿真。以下介绍一种更为常用的方式。

还是使用lab1/parta 下的例子,修改 addertb.v 的内容。

module addertb;
reg [7:0] a_test, b_test;
wire [7:0] sum_test;
reg cin_test;
wire cout_test;
reg [17:0] test;

add8 u1(a_test, b_test, cin_test, sum_test, cout_test);

initial begin          
`ifdef DUMP_VPD
       $vcdpluson();
`endif             
end

initial
begin
  for (test = 0; test <= 18'h1ffff; test = test +1) begin
    cin_test = test[16];
    a_test = test[15:8];
    b_test = test[7:0];
    #50;
    if ({cout_test, sum_test} !== (a_test + b_test + cin_test)) begin
      $display("***ERROR at time = %0d ***", $time);
      $display("a = %h, b = %h, sum = %h;  cin = %h, cout = %h",
               a_test, b_test, sum_test, cin_test, cout_test);
      $finish;
    end
   #50;
  end
  $display("*** Testbench Successfully completed! ***");
  $finish;
end
endmodule

在 addertb.v 中新增了一个 initial 块。表示如果在编译时,定义了 DUMP_VPD 这个宏,那么在仿真时,打开 $vcdpluson() 这个开关选项。

图 5

使用上图命令编译源码后仿真,+define+DUMP_VPD表示在编译时定义 DUMP_VPD 这个宏,即在仿真时,打开了$vcdpluson() 这个开关选项。

图 6

我们可以看到,在仿真完成后,生成了 vcdplus.vpd 这个文件。这个文件记录了仿真过程中所有信号的波形,可以使用 dve 打开。

图 7

通过 dve & 命令打开 dve, "&"的用途是后台打开dve,以免终端被占用。我们可以看到 dve 打开后界面为空白。

图 8

File -> Open Database

图 9

选择 vpd 文件并打开

图 10

在Hierarchy 部分,可以查看顶层模块里面的子模块,右键 -> Add to Waves 查看对应模块的波形图。

在上述方法中,在编译时通过定义一个宏,打开 testbench 中 $vcdpluson() 这个开关选项,在运行 simv 进行仿真时,VCS便把所有的波形记录下来,生成一个 .vpd 文件 (波形文件)。在dve中打开文件,即可查看仿真波形,方便之处在于波形可以发给他人查阅。

几点补充:

  1. 在编译时,将 -debug_all 选项 更改为 -debug_pp。打开生成 VPD 文件的功能,关掉UCLI的功能,节约编译时间。
  2. 在编译时,使用 +define+macro1 将宏macro1传给源代码。使用+define+macro1=value+macro2=value 将macro1和macro2 传给源文件中同名的宏。
  3. 在编译时,使用 +vpdfile+filename 可以更改生成 VPD 文件的文件名,默认为 vpdplus.vpd。
  4. 可直接使用命令:dve -vpd vcdplus.vpd & 后台打开 dve 并加载 vpd 文件,代替上面图7~图9的过程。
  5. 调用 vcdpluson() 时可以加入一些参数,如果什么都不加,则默认记录顶层模块下所有子模块的信号波形。参数格式:vcdpluson(level_number, module_instance, ... , ... )。比较重要的参数为前两个。数字设计里面的 module 是层次化/结构化的,类似于一层一层的黑盒子不断包含下去。module_instance 表示从哪一个module开始记录波形,level_number表示查看 module_instance 下子模块多少层的波形。下面使用一些例子来说明。

图 11

vcdpluson() 或者 vcdpluson(0, addertb) 记录 addertb 及其所有子模块的波形。

$vcdpluson(1, addertb) 只记录 addertb 层的波形。

$vcdpluson(2, addertb) 记录 addertb 层和 u1(add8) 层的波形。

$vcdpluson(3, addertb) 记录 addertb , u1(add8) ,u1(add4),low_add, high_add 层的波形。

三、对makefile的补充

在VCS入门教程(一)中,我们已经写过一个 makefile,现针对上述使用dve debug 的方法,对其做一些补充。仍使用上面 lab1/parta 内的代码。修改makefile如下:

.PHONY:com sim debug clean

OUTPUT = adder_top
ALL_DEFINE = +define+DUMP_VPD

VPD_NAME = +vpdfile+${OUTPUT}.vpd

VCS = vcs -sverilog +v2k -timescale=1ns/1ns             \
	  -debug_pp					\
	  -o ${OUTPUT}					\
	  -l compile.log				\
	  ${VPD_NAME}					\
	  ${ALL_DEFINE}

SIM = ./${OUTPUT} ${VPD_NAME} -l ${OUTPUT}.log

com:
	${VCS} -f verilog_file.f

sim:
	${SIM}

debug:
	dve -vpd ${OUTPUT}.vpd &
	
clean:
	rm -rf ./csrc *.daidir *.log simv* *.key *.vpd ./DVEfiles

在终端上分别使用 make com ,make sim ,make debug ,make clean 来编译,仿真,查看波形和清理生成的文件和目录。

四、结束语

本文介绍了VCS 进行 debug 的三种方式,其中第三种是最常使用最有效的。在实际工程中,通常使用VCS生成 fsdb 格式的波形文件,将其导入另一个软件 Verdi 查看波形,代替DVE进行联合仿真。感兴趣的同学可以查阅相关资料进行了解。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字ICer 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
    • 二、三种方法
    • 三、对makefile的补充
    • 四、结束语
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档