【VCS Verdi】VCS Verdi 联合仿真总结

news/2024/7/21 22:28:09 标签: EDA, VCS, simulation, 数字IC

1. VCS 介绍

VCS是编译型 Verilog 模拟器,它完全支持 OVI 标准的 Verilog HDL 语言、PLI 和 SDF。VCS 具有行业中较高的模拟性能,其出色的内存管理能力足以支持千万门级的 ASIC 设计,而其模拟精度也完全满足深亚微米 ASIC Sign-Off 的要求。

VCS 对文件的处理主要分为以下几个部分:

  • 原始的 .v 文件输入。

  • 转换成 .c 文件。

  • 编译成可执行的二进制文件。

  • 最后生成 simv,即可查看仿真的结果。

VCS 有三步法和两步法,三步法用于仿真混合语言,比如 Verilog 和 VHDL 的混合,而两步法用于仿真单种语言,一般都只仿真 Verilog 语言,因此这里只介绍两步法。

两步法分为:

  • Compilation 编译。编译是仿真设计的第一步。在这个阶段,VCS 构建实例层次结构并生成一个二进制可执行文件 simv。这个二进制可执行文件稍后将用于仿真。

  • Simulation 仿真。在编译过程中,VCS生成一个二进制可执行文件simv。可以使用 simv 来运行仿真。


2. 常用的编译指令

编译命令的格式:vcs sourcefile [compile_time_option] (编译选项用来控制编译过程),可以另外加一些命令,比如:debug_all(加入单步调试功能)、-sverilog(提供对systemverilog的支持)等

常见的编译命令如下:

  • -v lib_file:lib_file 是 Verilog 文件,包含了引用的 module 的定义,可以是绝对路径,也可以是相对路径。

  • -y lib_dir:lib_dir 是参考库的目录,vcs 从该目录下寻找包含引用的 module 的 Verilog 文件,这些文件的文件名必须和引用的 module 的名一样。

  • -full64:vcs 以 64 位模式编译,生成 64 位的 simv,如果不加这条命令则无法生成 simv。

  • -R:编译后立即进行仿真。

  • +v2k:支持 Verilog 2001 标准。

  • -sverilog:提供对 Systemverilog 的支持。

  • -l log_file:用于将编译产生的信息放在 log 文件内。

  • -debug_all:用于产生debug所需的文件。

  • -Mupdate:源文件有修改时,只重新编译有改动的.v文件,节约编译时间。

  • -timescale=1ns/1ns 设置仿真精度,可以根据实际需要进行更改。

  • -kdb:-kdb 选项支持输出 kdb 格式的数据,用于与 Verdi 在交互模式交换数据,而 kdb 格式属于 "Limited Customer Availability" 特性,必须通过 -lca 选项开启。


3. Verdi 介绍

Verdi 和 VCS 一样也是 Synopsys 公司的软件,它是一款强大的仿真波形查看器,通过查看波形进行 debug 会更高效,配合着 VCS 共同进行仿真验证是众多公司的选择,因此学习 VCS 联合 Verdi 进行仿真很有必要。

在 testbench 中添加以下系统函数,用于生成 .fsdb 文件,fsdb文件是 Verdi 可识别的波形文件。

initial begin
    $fsdbDumpfile("*.fsdb");
    $fsdbDumpvars(0);
end

以下为常见的fsdb波形文件的系统函数及其含义。

如果要Dump FSDB波形,将以下语句选择性加在TB中。

initial begin
    $fsdbDumpfile(“dump.fsdb”) ;           //fsdbDumpfile - 指定FSDB文件名
    $fsdbDumpvars(level,start_module) ;    //要记录的信号,level=0表示记录所有
    $dumpvars(2, top. u1);                 // Dump实例top. u1及其下一层的信号
    $fsdbDumpMDA();                        //fsdb dump波形时会记录二维数组2D array signal的值,
                                             便于在verdi中debug查memory内部信号。
    $fsdbDumpSingle;               //Dump指定的信号
    $fsdbDumpvariable;              //Dump指定的VHDL变量
    $fsdbSwitchDumpFile(“<newFSDBname>”);  //将dumping切换到另一个FSDB文件
    $fsdbAutoSwitchDumpfile(<filesize>, “<FSDBname>”,< numberoffile>);        
                                           //限制文件大小并在数据量过大时自动创建新的FSDB文件
    $fsdbDumpMem(<regname>, [<startaddr>, [<size>]]);
    $fsdbDumpoff;                          //停止记录
    $fsdbDumpon;                           //重新开始记录
    $fsdbDumplimit();                      //限制VCD文件的大小(以字节为单位)
    $fsdbDumpall;                          //记录所有指定的信号值
end

4. makefile 书写

如果使用终端手敲命令的方法进行 VCS 和 Verdi 的仿真,会显得过于麻烦,不仅费时间而且容易出错,因此可以通过 makefile 脚本的形式对整个过程的命令自动化,在对不同的文件进行仿真时,只需要修改部分内容即可重复使用,非常便捷。

以下为 Makefile 的脚本,可以使用 “make all” 命令实现从 VCS 编译到 Verdi 查看波形等一系列操作的自动化。

PRJ = /home/ICer/Project/prj
​
FILENAME = $(PRJ)/*.v 
OUTPUT = sync_fifo
​
#仿真并打开vcs verdi界面
all:clean elab run verdi
​
elab:
    vcs -full64 -LDFLAGS -Wl,-no-as-needed -debug_acc+all -timescale=1ns/1ns -fsdb -sverilog -l comp.log ${FILENAME}
​
run:
    ./simv -l ${OUTPUT}.log ${FILENAME}
​
rung:
    ./simv -gui -l run.log ${FILENAME}
​
verdi:
    verdi ${FILENAME} -ssf ./*.fsdb &
​
clean:
    rm -rf ./csrc *.daidir *.log simv* *.key *.vpd ./DVEfiles verdiLog novas*

5. 文件准备

现在用一个实际例子,用来演示 VCS 联合 Verdi 进行仿真,这里以一个简单的同步 FIFO 为例,以下为同步 FIFO 的 Verilog 实现。

`timescale1ns/1ps
//
// Company: 
// Engineer: Linest-5
// Create Date: 2022/05/07
// Design Name: 
// Module Name: Sync_FIFO
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 同步FIFO的verilog实现
// Dependencies: 
// Revision:
// Additional Comments:
// 
//
​
​
moduleSync_FIFO#(
    parameter   DATA_WIDTH          =8,                      //数据位宽
    parameter   FIFO_DEPTH          =8,                      //FIFO的深度
    parameter   ALMOST_FULL_DEPTH   =7,                      //将满的深度
    parameter   ALMOST_EMPTY_DEPTH  =1,                      //将空的深度
    parameter   ADDR_WIDTH          =3,                      //地址位宽
    parameter   READ_DATA_MODE      =0                       //读取的模式,0为组合逻辑。1为时序逻辑
    )(   
    input                           clk,  
    input                           rst_n,  
    input                           wr_en,  
    input                           rd_en,  
    input       [DATA_WIDTH-1:0]    wr_data,                     
    output  reg[DATA_WIDTH-1:0]    rd_data,  
    output                          almost_full,                 
    output                          almost_empty,   
    output                          full,                 
    output                          empty,        
    output  reg                     overflow,                 //上溢出信号
    output  reg                     underflow                 //下溢出信号
    );
​
    reg   [ADDR_WIDTH-1:0]          wr_ptr;                   //写指针
    reg   [ADDR_WIDTH-1:0]          rd_ptr;                   //读指针
    reg   [ADDR_WIDTH:0]            fifo_cnt;                 //使用的FIFO空间计数
    reg   [DATA_WIDTH-1:0]          buf_mem[FIFO_DEPTH-1:0]; //创建数组作存储     
​
  //对FIFO空间使用作计数
  always @(posedgeclkornegedgerst_n)begin
    if(!rst_n)begin
      fifo_cnt <='d0;
    end
    elseif(wr_en&&~full&&rd_en&&~empty)begin
      fifo_cnt <=fifo_cnt;
    end
    elseif(wr_en&&~full)begin
      fifo_cnt <=fifo_cnt+'d1;
    end
    elseif(rd_en&&~empty)begin
      fifo_cnt <=fifo_cnt-'d1;
    end
    elsebegin
      fifo_cnt <=fifo_cnt;
    end
  end
​
  //写指针
  always @(posedgeclkornegedgerst_n)begin
    if(!rst_n)begin
      wr_ptr <='d0;
    end
    elseif(wr_en&&~full)begin
      wr_ptr <=wr_ptr+'d1;
    end
    elseif(wr_ptr==FIFO_DEPTH-1)begin
      wr_ptr <='d0;
    end
    elsebegin
      wr_ptr <=wr_ptr;
    end
  end
​
  //读指针
  always @(posedgeclkornegedgerst_n)begin
    if(!rst_n)begin
      rd_ptr <='d0;
    end
    elseif(rd_ptr==FIFO_DEPTH-1)begin
      rd_ptr <='d0;
    end
    elseif(rd_en&&~empty)begin
      rd_ptr <=rd_ptr+'d1;
    end
    elsebegin
      rd_ptr <=rd_ptr;
    end
  end
​
  //数据写入
  always @(posedgeclkornegedgerst_n)begin
    if(!rst_n)begin
      buf_mem[wr_ptr] <='d0;
    end
    elseif(wr_en&&~full)begin
      buf_mem[wr_ptr] <=wr_data;
    end
    elsebegin
      buf_mem[wr_ptr] <=buf_mem[wr_ptr];
    end
  end
​
  //根据模式的选择将数据读出,模式0为组合逻辑输出,模式1为时序逻辑输出
  generate
    if(READ_DATA_MODE=='d0)begin
      always @(*)
      rd_data=buf_mem[rd_ptr];                          //组合逻辑输出
    end
    elsebegin
      always @(posedgeclkornegedgerst_n)begin
        if(!rst_n)begin
          rd_data <='d0;
        end
        elseif(rd_en&&~empty)begin
          rd_data <=buf_mem[rd_ptr];                     //时序逻辑输出
        end
        elsebegin
          rd_data <=rd_data;
        end
      end
    end   
  endgenerate
​
  //上溢出信号
  always @(posedgeclkornegedgerst_n)begin
    if(!rst_n)begin
      overflow <='d0;
    end
    elseif(wr_en&&full)begin
      overflow <='d1;
    end
  end
​
  //下溢出信号
  always @(posedgeclkornegedgerst_n)begin
    if(!rst_n)begin
      underflow <='d0;
    end
    elseif(rd_en&&empty)begin
      underflow <='d1;
    end
  end
​
  //定义将满、将空、满、空信号
  assignalmost_full=(fifo_cnt >=ALMOST_FULL_DEPTH);
  assignalmost_empty=(fifo_cnt <=ALMOST_EMPTY_DEPTH);
  assignfull=(fifo_cnt==FIFO_DEPTH);
  assignempty=(fifo_cnt=='d0);
​
endmodule

此外,还需要准备其对应的 testbench,如下所示。

//
// Company: 
// Engineer: Linest-5
// Create Date: 2022/05/07
// Design Name: 
// Module Name: tb_Sync_FIFO
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 同步FIFO的testbench文件
// Dependencies: 
// Revision:
// Additional Comments:
// 
//
`timescale1ns/1ns
​
moduletb_Sync_FIFO(); 
​
    parameter         DATA_WIDTH=8;
    parameter         FIFO_DEPTH=8;
    parameter  ALMOST_FULL_DEPTH=7;
    parameterALMOST_EMPTY_DEPTH=1;
    parameter         ADDR_WIDTH=3;
    parameter     READ_DATA_MODE=1;
​
    reg                    clk;
    reg                    rst_n;
    reg                     wr_en;
    reg                     rd_en;
    reg  [DATA_WIDTH-1:0] wr_data;
    wire[DATA_WIDTH-1:0]  rd_data;
    wire                    almost_full;
    wire                    almost_empty;
    wire                    full;
    wire                    empty;
    wire                    overflow;
    wire                    underflow;
​
    initialbegin
        clk=0;
        rst_n=0;
        #50
        rst_n=1;
    end
​
    always#10clk=~clk;
​
    initialbegin
        wr_en=0;
        rd_en=0;
        wr_data=0;
    end
​
    initialbegin
        #100
        send_wr;
    end
​
    initialbegin
        #400
        send_rd;
        #1000;
        $finish;
    end
​
    //创建写任务
    integeri;
    tasksend_wr;
    begin
        for(i=0;i<8;i=i+1)begin
            @ (posedgeclk)begin
                wr_en <='d1;
                wr_data <=i+1;
            end
        end
        @ (posedgeclk)begin
            wr_en <='d0;
            wr_data <='d0;
        end
        repeat(10)@ (posedgeclk);
    end
    endtask
​
    //创建读任务
    tasksend_rd;
    begin
        for(i=0;i<8;i=i+1)begin
            @ (posedgeclk)begin
                rd_en <='d1;
            end
        end
        @ (posedgeclk)begin
                rd_en <='d0;
        end
    end
    endtask
​
    Sync_FIFO#(
        .DATA_WIDTH        (DATA_WIDTH),
        .FIFO_DEPTH        (FIFO_DEPTH),
        .ALMOST_FULL_DEPTH (ALMOST_FULL_DEPTH),
        .ALMOST_EMPTY_DEPTH(ALMOST_EMPTY_DEPTH),
        .ADDR_WIDTH         (ADDR_WIDTH),
        .READ_DATA_MODE    (READ_DATA_MODE)
    )inst_Sync_FIFO(
        .clk                (clk),
        .rst_n              (rst_n),
        .wr_en              (wr_en),
        .rd_en              (rd_en),
        .wr_data            (wr_data),
        .rd_data            (rd_data),
        .almost_full      (almost_full),
        .almost_empty      (almost_empty),
        .full               (full),
        .empty              (empty),
        .overflow           (overflow),
        .underflow          (underflow)
    );
​
    initialbegin
        $fsdbDumpfile("tb_Sync_FIFO.fsdb");
        $fsdbDumpvars(0);
        $fsdbDumpMDA();
    end
​
endmodule

有几点需要注意:

  • 添加仿真精度和仿真单位。

`timescale 1ns/1ns
  • 添加停止仿真系统函数。如果不加的话仿真会一直仿真不停止,导致无法观察仿真波形。

$finish
  • 添加仿真需要的系统函数,$fsdbDumpfile 表示生成的 fsdb 文件名,用于 Verdi 查看波形;$fsdbDumpvars 表示需要查看的信号,0 表示所有信号都将纳入观察;$fsdbDumpMDA 表示 fsdb dump 波形时会记录二维数组 2D array signal 的值,便于在 Verdi 中 debug 查 memory 内部信号。

initial begin
$fsdbDumpfile("tb_Sync_FIFO.fsdb");
$fsdbDumpvars(0);
$fsdbDumpMDA();
end

6. 实操练习

接下来直接进行最后的实操仿真,打开终端进入到工程目录下。

ls 查看当前目录所含的文件,可以看到包含有同步 FIFO 的 .v 文件以及对应的 TB 文件,另外还有实现自动化的 makefile 脚本文件。

根据前面 makefile 的说明,直接使用 make all 命令即可实现自动仿真。

随后会显示一些信息,如顶层文件,仿真精度和单位等。

同时也会打印 VCS 仿真报告。

随后会自动弹出 Verdi 的 GUI 界面。

  • 第一部分为菜单栏部分,这里可以进行查看电路图、加载源文件等操作。

  • 第二部分为当前加载的文件层次结构,可以看到顶层文件为 sync_fifo,因为当前只有一个文件,所以毫无疑问顶层文件只能是它,如果包含多个设计文件,含有顶层及子模块,这里就会清晰的显示出来。

  • 第三部分为设计文件的内容显示,同时在后续会介绍可以直接通过这里查看相应信号的波形。

  • 第四部分为波形显示区,这里就是 Verdi 最重要的区域,我们就是通过这里查看信号波形进行 debug 的。

但是这是可以看到在波形显示区并没有波形出现,这时需要我们手动选择添加观察信号,鼠标选择要观察的模块信号,并按下快捷键“CTRL + 4”,即可将选中模块所包含的信号加入到波形观察区中。

此外在前面埋下伏笔说了可以在代码观察区添加观察信号,在 debug 时,有时需要观察非端口信号,比如在内部定义的 reg/wire 信号,这时就可以在代码观察区添加,比如我要观察在内部定义的写使能信号 wr_en,鼠标选中此信号,按下快捷键“CTRL + w”即可将信号添加至观察区进行 debug。

可以看到当前的波形太大,不利于我们观察,可以按 F 键,便会自动将波形整形成最适合当前的比例。

另外可以通过上方的按钮将波形进行放大和缩小,也可以通过按住 CTRL 键在滑动鼠标滚轮将波形进行放大和缩小。

当添加的模块信号太多时,会显得很杂乱,可以将其分组分类进行观察,只需要将同组的信号选中向下拖拽即可。下图将信号分为五组。

过多的信号都是同一种颜色容易看花,可以将不同类的信号设置成不同的颜色,便于区分,只需选中信号,按 T 键即可切换颜色。

另外选中信号右击鼠标会出现一列菜单栏,在这里可以更改信号的进制、形式(数字或模拟)、重命名等操作,这里就不一一操作了,如果之前使用过 Vivado、Modelsim等工具,相信这些操作还是很容易上手的。


7. 总结

这篇文章只介绍,VCS 和 Verdi 最基本的操作,更多高阶的操作可以查看其官方手册,也可以在平时的练习中不断加强对软件操作的熟练度,工具学起来还是比较容易的,难的是如何对设计进行快速的仿真并找出其中的 bug,这需要在未来长期学习训练与总结。


http://www.niftyadmin.cn/n/25248.html

相关文章

2023 年值得关注的 7 大人工智能 (AI) 技术趋势

&#x1f482; 个人网站:【海拥】【摸鱼游戏】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 人工智能 (AI) 已经接…

Curator实现分布式锁(可重入 不可重入 读写 联锁 信号量 栅栏 计数器)

文章目录前言代码实践1. 配置2. 可重入锁InterProcessMutex3. 不可重入锁InterProcessSemaphoreMutex4. 可重入读写锁InterProcessReadWriteLock5. 联锁InterProcessMultiLock6. 信号量InterProcessSemaphoreV27. 栅栏barrier8. 共享计数器8.1. SharedCount8.2. DistributedAto…

简单实现Java定时器

✨✨hello&#xff0c;愿意点进来的小伙伴们&#xff0c;你们好呐&#xff01; &#x1f43b;&#x1f43b;系列专栏&#xff1a;【JavaEE】 &#x1f432;&#x1f432;本篇内容&#xff1a;自己实现Java定时器 &#x1f42f;&#x1f42f;作者简介:一名现大二的三非编程小白&…

JavaScript 运算符

文章目录JavaScript 运算符JavaScript 算术运算符JavaScript 赋值运算符用于字符串的 运算符对字符串和数字进行加法运算JavaScript 运算符 运算符 用于赋值。 运算符 用于加值。 运算符 用于给 JavaScript 变量赋值。 算术运算符 用于把值加起来。 实例 指定变量值&am…

k8s之实战小栗子

写在前面 本文一起看一个基于k8s的实战小栗子&#xff0c;在这篇文章 中我们基于docker搭建了一个WordPress网站。本文就通过k8s再来实现一遍。架构图如下&#xff1a; ![在这里插入图片描述](https://img-blog.csdnimg.cn/9c73ac0c183a429a8f4b1a2feb363527.png 从上图可以…

Java设计模式中代理模式是什么/JDK动态代理分为哪些,静态代理又怎么实现,又适合哪些场景

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 5.结构型模式 5.1 概述 根据如何将类或对象按某种布局组成更大的结构&#xff0c;分为类结构模式和对象结构模式&#xff0c;前者采用继承机制来组织接口和类&am…

基于java ssm springboot+VUE疫情防疫系统系统前后端分离设计和实现

基于java ssm springbootVUE疫情防疫系统系统前后端分离设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留…

分布式基础篇2——分布式组件

一、SpringCloud Alibaba1、简介2、为什么使用3、版本选择4、依赖选择二、SpringCloud Alibaba 组件1、Nacos作为注册中心2、OpenFeign3、Nacos作为配置中心namespaceData IDGroup同时加载多个配置文件三、Spring Cloud1、GateWay简介三大核心部分网关的使用视频来源: 【Java项…