专栏首页根究FPGAVCS与Verdi的联合仿真

VCS与Verdi的联合仿真

一、Verdi用途与优势

Verdi主要用于生成fsdb模型,同VCS使用的vcd文件相比,verdi使用的fsdb相当于vcd文件经过霍夫编码压缩之后的精简版,可用于查看fsdb波形并追踪RTL代码。

虽说verdi、modelsim都是用来调试波形, modelsim与verdi相比,最大的缺点是波形不会全dump,wave窗口拉不全的话需要重新跑,而verdi边运行边查看。具体做法是,在使用tcl指令,在运行仿真时,设置仿真时间,每次run完毕之后,在nWave窗口中file——>自动加载(shift+L快捷键),异常方便!

二、FSDB波形

Verdi只能查看fsdb格式的波形,而VCS可以生成供DVE查看的vpd格式的波形,如果想要输出fsdb格式的波形,需要额外在testbench中添加fsdb指令,或者通过脚本进行设置。FSDB全称为(Fast Signal Data Base):快速信号数据库,fsdb文件是Verdi使用的一种专用数据格式,fsdb通过verilog的PLI接口实现,如:

fsdbDumpfile("f0.fsdb"); //指定生成的fsdb文件的文件名
fsdbDumpars(0,top);  //指定dump的变量

三、开发环境与配置

使用Verdi前需要对开发环境进行配置,使得系统在调用Verdi时找到其路径,在使用verdi时找到其库文件与路径,所以要注意三个变量和三条指令的使用:Verdi_HOME/NOVAS_HOME 仿真器默认,为设置PATH做准备 PATH 让系统能够找到verdi,如果不设置PATH,在调用Veidi时系统无法识别。

LD_LIBRARY_PATH 为了使得Verdi在运行过程中找到需要的库文件,对Library的位置在开发环境中进行配置,可通过:

echo $LD_LIBRARY_PATH | sed  ‘s/:/\n/g’

进行查看,库文件中包含.so(共享对象文件,shared object),pli.a(静态库)等库文件,还需要*.tab(表格文件)来获取对应信息、索引等。

echo 用于获取环境变量,获取并打印,打印环境变量指令为:

echo $PATH | sed ‘s/\ :/\n/g’

which 查询当前路径是否设置成功,若设置成功会显示一个软件路径,查看verdi路径:which verdi

uname 查询当前系统信息,如硬件平台否为:x86_64

四、VCS+Verdi 如何dump波形

在dump波形时会用到那些命令,解决的是生成fsdb波形的问题,为了生成.fsbd格式的文件,可以使用verilog波形函数,也可以使用ucli/tcl接口:

(一)使用Verilog系统函数

作为小白,我觉得这种方式很友好,通过Verilog的PLI接口实现,在tb中添加两个函数:

initial begin
$fsdbDumfile(“uart.fsdb”);     //指定生成的fsdb文件的文件名
$fsdbDumpars(0,uart_byte_tx_tb); //指定dump的层次,0表示存储所有的wave,tb为起始层
end 

(二)、使用ucli/tcl接口

使用ucli/tcl接口时无需在tb中调用与fsdbDumpvars()函数,仅需在脚本中进行设置即可。在运行仿真时,打开ucli接口,通过Tcl脚本对fsdb进行设置,设置fsdb文件的文件名,设置fsdb文件的集成类型和起始文件:

global env

# tcl脚本引用环境变量,Makefile中通过export定义

fsdbDumpfile "$env(demo_name).fsdb"

# 设置波形文件名,受环境变量env(demo_name)控制

# demo_name在makefile中使用exportdemo_name=demo_fifo

fsdbDumpvars 0 "tb_top"

# 设置波形的顶层和层次,表示将tb_top作为顶层,Dump所有层次

run

+fsdb+autoflush

+fsdb+f+autoflush:用于开启一边仿真以一边Dump波形的功能,在不开启该功能时,运行完仿真之后,未退出命令行,直接在新终端中启动Verdi调用波性文件的话是一个用文件,没有波形,这是因为只有在结束仿真之后,波形才会Dump为静态文件供verdi调用,没有出现波形的原因是此时的.fsdb只是一个空文件,波形还未Dump,如下图所示:

此时可以在仿真的命令行中键入:fsdbDumpflush,启动波形Dump,在另一个终端中启动verdi加载波形,波形正常加载:

verdi优于modelsim也正是因此,通过tcl语言的控制,每次设置run时间,不断的加载仿真波形,十分方便!

VCS编译环节

无论采用调用系统函数还是调用tcl脚本,编译时两种操作相同,都是为了生成一个二进制可执行文件,其重点是VCS与Verdi的库进行连接:

-LDFLAGS
#表示下面要加载(load)的标志,将要传递VCS的Linker链接库,与接下来的两条指令配合使用。
-rdynamic
#加载动态库,提示需要加载动态库,如*库文件名录下的.so文件
-P $(Verdi_HOME)/share/PLI/VCS/LINUX64/novas.tab
#加载表格文件  
$(Verdi_HOME)/share/PLI/VCS/LINUX64/pli.a
#加载静态库

五、Makefile脚本设计

要用到的基本指令为:编译、仿真、verdi加载fsdb波形、清除文件 所以在脚本中设计伪指令:

.PHONY: com sim run_verdi clean

运行编译时会生成一个二进制可执行文件供仿真使用,该文件默认名为:simv,可通过脚本设置:

OUTPUT = uart #也可以自己设置名字 运行编译,设计编译指令,设置编译开关:

vcs表示运行编译,+v2k表示支持verilog2001标准,-timesacle=1ns/1ns用于设置仿真时间精度,-debug_all用于设置debug开关,-f dile_list.f用于设置编译文件,-o (OUTPUT)用于设置输出二进制可执行文件的文件名,-full64表示VCS为64位版本。

verdi加载fsdb文件显示波形:

-f file_list.f制定要加载的.v文件,-ssf $(OUTPUT).fsdb 表示打开verdi时默认自动加载.fsdb仿真文件,-nologo表示不显示欢迎界面。

六、两种fsdb文件生成方式的比较

基于系统函数

优点:熟悉verilog代码,接受较快

缺点

1.需要重新编译系统,浪费时间(不使用valuevalueplusargs时); 2.Verilog是低级语言,对于文本处理比较困难,不支持正则表达式

基于ucli/tcl接口:

优点:

1.不需要重新编译仿真顶层;

2.使用高级语言接口,容易完成复杂处理,例如传递变量,例如使用正则表达式;

3.交互式接口,控制灵活,仿真过程可修改dump信息,如dumpon/dumpoff

七、Verdi快捷键

  • save和restore 存储当前查看波形的工程 将当前所有信号存成一个*.rc文件 重新打开波形界面时,restore信号 nwave:file -> Save Signal/Restore Signal
  • 快速熟悉一个设计:通过nTRACE界面查看设计结构:

熟悉设计的IO Driver【D】(Input) 哪些信号驱动了当前信号,可以在nWAVE界面中进行查看。Load【L】 当前信号驱动了哪些信号,可以在nWAVE界面中进行查看。

操作演示:

最后分享一下我所用的工程,是一个UART发送的测试,其工程代码为:

module uart_byte_tx(
  Clk,
  Rst_n,
  data_byte,
  send_en,
  baud_set,
  
  Rs232_Tx,
  Tx_Done,
  uart_state
);

  input Clk;
  input Rst_n;
  input [7:0]data_byte;
  input send_en;
  input [2:0]baud_set;
  
  output reg Rs232_Tx;
  output reg Tx_Done;
  output reg uart_state;
  
  reg bps_clk;  //波特率时钟
  
  reg [15:0]div_cnt;//分频计数器
  
  reg [15:0]bps_DR;//分频计数最大值
  
  reg [3:0]bps_cnt;//波特率时钟计数器
  
  reg [7:0]r_data_byte;
  
  localparam START_BIT = 1'b0;
  localparam STOP_BIT = 1'b1;
  
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    uart_state <= 1'b0;
  else if(send_en)
    uart_state <= 1'b1;
  else if(bps_cnt == 4'd11)
    uart_state <= 1'b0;
  else
    uart_state <= uart_state;
  
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    r_data_byte <= 8'd0;
  else if(send_en)
    r_data_byte <= data_byte;
  else
    r_data_byte <= r_data_byte;
  
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    bps_DR <= 16'd5207;
  else begin
    case(baud_set)
      0:bps_DR <= 16'd5207;
      1:bps_DR <= 16'd2603;
      2:bps_DR <= 16'd1301;
      3:bps_DR <= 16'd867;
      4:bps_DR <= 16'd433;
      default:bps_DR <= 16'd5207;      
    endcase
  end  
  
  //counter
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    div_cnt <= 16'd0;
  else if(uart_state)begin
    if(div_cnt == bps_DR)
      div_cnt <= 16'd0;
    else
      div_cnt <= div_cnt + 1'b1;
  end
  else
    div_cnt <= 16'd0;
  
  // bps_clk gen
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    bps_clk <= 1'b0;
  else if(div_cnt == 16'd1)
    bps_clk <= 1'b1;
  else
    bps_clk <= 1'b0;
  
  //bps counter
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)  
    bps_cnt <= 4'd0;
  else if(bps_cnt == 4'd11)
    bps_cnt <= 4'd0;
  else if(bps_clk)
    bps_cnt <= bps_cnt + 1'b1;
  else
    bps_cnt <= bps_cnt;
    
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    Tx_Done <= 1'b0;
  else if(bps_cnt == 4'd11)
    Tx_Done <= 1'b1;
  else
    Tx_Done <= 1'b0;
    
  always@(posedge Clk or negedge Rst_n)
  if(!Rst_n)
    Rs232_Tx <= 1'b1;
  else begin
    case(bps_cnt)
      0:Rs232_Tx <= 1'b1;
      1:Rs232_Tx <= START_BIT;
      2:Rs232_Tx <= r_data_byte[0];
      3:Rs232_Tx <= r_data_byte[1];
      4:Rs232_Tx <= r_data_byte[2];
      5:Rs232_Tx <= r_data_byte[3];
      6:Rs232_Tx <= r_data_byte[4];
      7:Rs232_Tx <= r_data_byte[5];
      8:Rs232_Tx <= r_data_byte[6];
      9:Rs232_Tx <= r_data_byte[7];
      10:Rs232_Tx <= STOP_BIT;
      default:Rs232_Tx <= 1'b1;
    endcase
  end  

endmodule

testbench为:


`define clk_period 20

module uart_byte_tx_tb;

  reg Clk;
  reg Rst_n;
  reg [7:0]data_byte;
  reg send_en;
  reg [2:0]baud_set;
  
  wire Rs232_Tx;
  wire Tx_Done;
  wire uart_state;
  
  uart_byte_tx uart_byte_tx(
    .Clk(Clk),
    .Rst_n(Rst_n),
    .data_byte(data_byte),
    .send_en(send_en),
    .baud_set(baud_set),
    
    .Rs232_Tx(Rs232_Tx),
    .Tx_Done(Tx_Done),
    .uart_state(uart_state)
  );
  
  initial Clk = 1;
  always#(`clk_period/2)Clk = ~Clk;
  
  initial begin
    Rst_n = 1'b0;
    data_byte = 8'd0;
    send_en = 1'd0;
    baud_set = 3'd4;
    #(`clk_period*20 + 1 )
    Rst_n = 1'b1;
    #(`clk_period*50);
    data_byte = 8'haa;
    send_en = 1'd1;
    #`clk_period;
    send_en = 1'd0;
    
    @(posedge Tx_Done)
    
    #(`clk_period*5000);
    data_byte = 8'h55;
    send_en = 1'd1;
    #`clk_period;
    send_en = 1'd0;
    @(posedge Tx_Done)
    #(`clk_period*5000);
    $finish;  
  end

 // initial begin: fsdb_generate
 // $fsdbDumpfile("uart.fsdb");
 // $fsdbDumpvars(0,uart_byte_tx_tb);
 // end 
endmodule

Makefile脚本为:

.PHONY: com sim run_verdi clean

OUTPUT =uart

export demo_name=$(OUTPUT)

#ALL_DEFINE = + define + BAUD_9600

#compile command 
VCS =vcs     +v2k  -timescale=1ns/1ns                    \
     -debug_all                                          \
     -LDFLAGS                                            \
     -rdynamic                                          \
     -P $(Verdi_HOME)/share/PLI/VCS/LINUX64/novas.tab    \
      $(Verdi_HOME)/share/PLI/VCS/LINUX64/pli.a          \
      -full64                                            \
      +vcs_lic+wait                                      \
      -f file_list.f                                    \
      -o ${OUTPUT}                                      \
      -l compile.log          

VERDI=verdi -f file_list.f   \
      -ssf $(OUTPUT).fsdb    \
      -nologo                \
      -l v.log             

#sim command
SIM = ./$(OUTPUT)                              \
      -ucli -i ../scripts/dump_fsdb_vcs.tcl    \
      +fsdb+autoflush                          \
      -l sim.log
    
#start compile
com:
  $(VCS)

#start simulation
sim:
  $(SIM)

#run verdi
run_verdi:
  $(VERDI) &
#clean
clean:
  rm -rf  ./verdiLog  ./dff ./csrc *.daidir *log *.vpd *.vdb simv* *.key *race.out* *.rc *.fsdb *.vpd *.log *.conf *.dat *.conf uart

tcl指令为:

global env
fsdbDumpfile "$env(demo_name).fsdb"
fsdbDumpvars 0 "uart_byte_tx_tb"
run 10000ns

好啦,本次更新到此结束,最后衷心的感谢相量子大哥公众号数字ICer的大哥,一位大哥提供了装好了虚拟机,另一位大哥在使用过程中提供了诸多使用技巧,感谢大家的关注。

文章分享自微信公众号:
根究FPGA

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

如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • 【仿真技巧】 0:00 VCS+VERDI+reverse=败者食尘!!

    假设一种场景,在调试环境的时候,运行到15min的时候,环境出现bug,需要去debug。也许错误的第一现场并不是15min的时候,可能在14min30s-15...

    空白的贝塔
  • EasySim: 一个简单的仿真脚本 v1.1

    Edit conf.json, add "timescale=1ns/1ps", or "override_timescale=1ns/1ps"

    ExASIC
  • Linux系统下VCS2016和Verdi_2016的安装教程

    首先要说明的是,此处安装的VCS2016和Verdi_2016版本均不支持Linux 4.x以上的内核,否则即使安装成功后也可能无法使用,并且由于Linux发行...

    网络交换FPGA
  • 【AMBA VIP | VC_formal | VCS】EDA docker 镜像使用指南

    在拉取镜像之前,先为docker设置镜像加速器,推荐阿里云的docker镜像加速服务

    空白的贝塔
  • 手撕distributed ram类型同步FIFO

    同步fifo设计的核心在于full与empty信号的控制,今天看网上发布的部分verilog代码,虽然可以完成读写操作,但是存在部分问题,就是最后一个数据的读取...

    根究FPGA
  • 平头哥开源项目wujian100_open | 基于VCS+Verdi的仿真

    创建项目文件夹 makdir wujian100_open 进入文件夹 cd wujian100_open

    数字芯片社区
  • 【干货】推荐一款FPGA仿真调试鸟枪换炮的工具!

    调试FPGA,大家常用的工具主要有以下几种:Quartus,ISE或Vivado ,而仿真工具则常用ModelSim,个别初学者甚至还拿ISE/Vivado或Q...

    网络交换FPGA
  • docker使用指南更新

    phyzli/centos8_xfce4_tigervnc_hspice2010,2060年到期,需要lmgrd;

    空白的贝塔
  • Synopsys全套docker镜像使用指南

    本教程通过win10下的docker实现,过程简单,不需要自己进行破解,同时是通过wsl方式实现,比虚拟机效率会更加高一些。

    空白的贝塔
  • 后仿中的异步D触发器设置

    在PR后仿时,经常会遇到讨厌的红色X(不定态)。而debug不定态的起因又很麻烦,有可能用Verdi调试半天还是没能找到根本的原因。

    ExASIC
  • 数字IC设计 | 入门到放弃指南

    Verilog语言与软件语言最大的区别就是,因为它是用于描述电路的,因此它的写法是非常固定的,因为电路的变化是非常有限的。学习Verilog的时候,很多时候我们...

    数字芯片社区
  • VCS入门教程(二)

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

    数字芯片社区
  • 平头哥开源项目wujian100_open | 基于synplify+vivado生成bitfile

    Use the sdc2fdc Tcl shell command to convert the timing constraints.

    数字芯片社区
  • 当我们做后仿时我们究竟在仿些什么(补充)

    自从上次关于后仿的文章发布以后,又陆续收集到了一些关于后仿的其它小技巧。这次整理出来作为前文的补充,希望对大家有所帮助。文中提到的仿真器默认是VCS.

    icsoc
  • 芯片后仿及SDF反标

    启用SDF反标。在file.sdf中指定的最小值、类型或最大值中的一种,在实例instance_name上进行反标。

    数字芯片社区
  • ubuntu VCS+verdi安装教程

    链接:https://pan.baidu.com/s/1EWX8PulBncy9bjwW_mySIg

    FPGA开源工作室
  • 使用Verdi的小技巧(四)

    其实日常的频繁、快速地查看 Verilog 源代码,一般是在独立的文本编辑器中完成的。比如,VCS 编译产生的 log 文件中,对应的 Warning、Erro...

    icsoc
  • 验证仿真提速系列--认识“时间”与平台速度定量分析

    仿真时间(simulation time)

    IC验证

扫码关注云+社区

领取腾讯云代金券