• 【数字IC验证快速入门】11、Verilog TestBench(VTB)入门


    导读:作者有幸在中国电子信息领域的排头兵院校“电子科技大学”攻读研究生期间,接触到前沿的数字IC验证知识,旁听到诸如华为海思清华紫光联发科技等业界顶尖集成电路相关企业面授课程,对数字IC验证有了一些知识积累和学习心得。为帮助想入门前端IC验证的朋友,思忱一二后,特开此专栏,以期花最短的时间,走最少的弯路,学最多的IC验证技术知识。

    前言

    IC的整个开发中,验证通常包含两个:

    • Function(逻辑功能验证)
    • ATE(制造测试验证)【DFX团队做一些可测试性电路,进而进行工艺制造缺陷的测试】

    本节学习目标

    • 掌握ASIC设计流程

      • 设计、验证、中断(BES)、后端主要解决哪些问题
      • 主流EDA工具
      • 验证工程师的基本技能需求
    • 掌握ASIC验证的基本概念

      • Verilog TestBench 和 testcase(测试点)
    • 掌握ASIC验证策略:RCDV(Random Coverage Driven Verification)

      • 随机化策略
        • 为什么要做随机化?现在集成电路规模越来越大,设计复杂度越来越高,对于TB,通过直接用例构造可能会不充分,有些边界的corner可能会想不到。另外,设计复杂度越来越高,验证的空间越来越大,直接用例构造,跑的时间越来越长,可能跑不完。随机化则是用时间换空间,每个用例用的时间不长,但是可以每天不停的跑。
      • 覆盖率驱动
        • 功能覆盖【主观】:人为根据应用场景,把功能点分解出来之后,去设定一个目标,哪些功能需要去覆盖,通过语言描述出来。覆盖率可达100%
        • 代码覆盖【客观】:代码写出来之后,工具自动去看跑的激励有没经过代码的所有分支。存在冗余代码,覆盖率达不到100%。
    • 如何验证一个加法器【在实例中固化之前学习的知识】

      • 掌握EDA工具Questasim进行逻辑仿真的步骤:编译、仿真、vsim查看波形
      • 掌握Makefile自动化编译的方法
      • 理解testbench和testcase的含义
      • 掌握code voverage的含义和Questasim自动化统计代码覆盖率的方法
      • 掌握verilog实现随机化验证的基本方法:$random,随机中继(seed)的传递机制

    一、ASIC/SoC 设计流程

    面试上常常会问,注意理解!

    理论上设计流程

    注:流程很多,掌握其中一种就可以了。

    在这里插入图片描述

    • RTL设计 Design

      • 首先会去写一些规格书(Specification):Function Spec(FS) -> Architecture Spec(AS) -> Design Spec(DS),由顶向下去做
      • 在做AS期间也会去做一些模块划分(Partition),尽量把一些功能比较内聚的东西划分成一个模块
      • 在DS器件会去画一些关键的电路原理图,有了电路之后去RTL Coding,由底向上去做,最后进行集成(IP Integration)
    • 功能验证Verification

      • 测试计划:1、测试的对象;2、测试的工具; 3、测试的策略:哪些点用直接用例,哪些点用随机用例;4、功能覆盖率多少算成功;5、验证阶段规划及花费时间
      • TestBench和Testcase:分解Testpoint(测试点),写Testcase(测试用例),搭建TestBench
      • Regression(回归):验证会去一条条跑用例,用例用的是随机的策略,所以我们会每天都去做回归【时间换空间】。覆盖的面会越来越多越来越广,通过回归把验证做的完备!同时在用例前期,如果代码很复杂,可能还会有一个调试过程(调试验证环境)。
      • Integration Verification:小模块集成后,集成性验证。UT(Unit Test) -> BT(Block Test) -> IT(Integration Test)
    • RTL freeze:RTL冻结

      • Pre-synthesis sign-off:预综合,看结构、时序等物理实现方面是否有问题。
    • 逻辑综合 logic synthesis

      • map RTL to gate-level netlist
    • Postsynthesis design validation

      • FM:Format Verification,形式验证,看网表和RTL代码是否一致,是静态验证,不需要去灌激励,工具把电路结构分解之后,RTL Transfer Level基于每个寄存器的逻辑椎的顶点,组合逻辑输入的作为椎体输入,遍历去验证。
    • 静态时序分析:STA(static timing analysis)

      • Post-synthesis timing verification:根据时序路径检查每个D触发器的setup和hold能不能满足,看物理实现能不能满足器件的时序要求,与功能同样没有太大的关系。
      • 逻辑综合之后做STA,里面的走线延时、CELL延时是没有的,因为这个时候还可以做PR,都不知道CELL摆哪个位置,走线有多长,此时是一个零负载模型(走线延时等于0)。此时为了模拟一个走线延时,会把时钟进行一个过约,本来时钟是跑100MHz的,现在将其跑到110MHz。综合的时候,进行STA,可以看到路径有没有违例,一方面工具会进行优化,另外还会反馈到设计,有必要的话也会去改设计。
    • 可测试性设计(DFT,Design For Test):Test generation and fault simulation

      • 制造性缺陷涉及,会在D触发器中插一些scan链,在mem中插入一些membist的电路,在芯片的回来之后,做功能测试之前会先去做制造性缺陷验证,没有问题后,才会去做功能测试。
    • 物理实现(PD,Phsical Design)【迭代过程】

      • Placement(每个CELL放在DIE里的哪个位置),scan chain(这个在逻辑综合就会去做) and CTS(时钟树),cell routing(CELL之间连线方式) verify physical and electrical design rules(物理电气特性的检查,DRC),Extract parasitics(提取寄生参数)
      • 物理实现后还回去做STA,这时有了真实的CELL延时和走线延时信息,这个时候做STA,时钟就需要按照100MHz的频率去分析,因为这个时候很多时序信息就是真实的了。只有时序分析真实了之后,才会去进行sign-off,即tape-out,流片。
    • Production-ready masks

      • 制造厂会去制造掩图,给他们的是GDSII文件

    二、ASIC/SoC设计技能

    在这里插入图片描述

    • UPF: Universal Power Format,描述芯片电源结构的一种语言,主要用来做功耗分析。【倾向于架构级工程师掌握】

    • UVM:Universal Verification Methodology

    • BIST:Build In Self Test,内建性测试。membist,内部自己产生激励并对比结果。当然还有LogicBist,逻辑内建性测试。

    • SCAN:通过芯片外围的IO管脚灌入激励,看scan chain的输出结果是否正确

    • ATPG:Automatic Test Pattern Generation,从芯片外部的机台产生的激励。

    • CTS:Clock Tree Synthesis

    • DRC:Design Rule Check

    • LVS:Logic Versus Schematic,类比FM

    芯片公司,FPGA用来做原型验证,对于数字验证来讲是一个互补的关系,FPGA更多应用在系统级大的模块的数据流验证中,跑的很快;并且SoC软件的调试等工作也会放到FPGA中进行!

    EDA更多的是跑一些BT,IT,系统级的更多会放在FPGA上跑!

    三、ASIC/SoC 设计流程中使用的主流EDA

    在这里插入图片描述

    四、数字系统设计节点SoC Design Node

    在这里插入图片描述

    五、VTB练习

    5.1、Verilog TestBench (VTB)功能复习

    在这里插入图片描述

    • 产生激励 Generate stimulus
    • 将激励输入到待测设计(DUT - Design Under Test)
    • 产生预期 Generate Expectation
    • 获取响应(Capture response)
    • 检查响应的正确性(Check the response for correctness)
      • 对于复杂的TestBench,如后续用SystemVerilog写的TestBench(即SVTB),大都需要在TestBench中加入Reference Module(RM)或者又叫Golden Module,然后将激励引入到RM模块中,并将RM的输出结果保存起来,进而与响应输出的结果进行比对(check)。如果结果一致,那么用例通过;如果对不上,那么DUT或RM可能有问题,需要重新检查。
      • 对于VTB,测试对象比较小,功能比较简单通常不需要加入RM
      • RM的逻辑行为与DUT一样,只不过其中没有了延时信息!
    • 根据验证目标评估验证进度(Measure the progress against the overall verification goals)
      • 验证的核心思想:验证完备性,不仅仅是找BUG
      • 证明DUT的功能是ok的,所以首先要将DUT的功能点(Feature)给分解完全!然后依次验证每个功能点是否ok,如果error,那么就要去找BUG。
      • 验证进度需要看覆盖率CDV(Coverage Driven Verification),通常可分为两种:功能覆盖率【主观】(分解功能点,需要验证每个功能点,= 100%)、代码覆盖率【客观】(可能会存在冗余代码,某些代码会验不到,<100%)

    :输入的信号称为激励,输入激励的不同组合我们称之为不同的Pattern,也叫测试点(Test Pattern)。输出的信号称为响应

    5.2、验证一个32位加法器(VTB)(概览)

    • 当我们的输入验证空间很大的时候,会采取随机化策略
    • 后面system verilog会更加深入做随机化,这里的verilog随机化简单了解即可!
    • system verilog的随机会给权重,届时也会更加深入,这里提这个仅仅作为了解!

    5.2.1、编写设计源文件(随机化策略)(概览)

    在这里插入图片描述

    • 问:随机仿真中出现错误,如何复现错误?
      • 答:种子不变,随机数据不变,所以只要找到对应错误时刻的种子即可!

    5.2.2、编写Makefile文件(覆盖率驱动)(概览)

    • 最终验证通过覆盖率去检查!
    • +plusargs_save +seed=$(SEED):把SEED传入到上面的设计源文件中去!
    • -cm line+cond+fsm+tgl+branch:代码覆盖率统计,包括行、条件、状态机、tgl、分支
    • 这个脚本后面还会深入去讲,这里仅仅有个“复杂空间,我们会去做随机化”这个概念即可!

    在这里插入图片描述

    5.2.3、覆盖率check(概览)

    • 包含覆盖率示意图

    在这里插入图片描述

    5.3、验证一个32位加法器(VTB)(详细)

    5.3.1、添加.vimrc设置选项

    首先在.vimrc中再添加一些设置选项(具体含义,可自行百度搜索),帮助我们更高效地编程,之前的.vimrc设置参考:【数字IC验证快速入门】5、快速上手Linux下的文本编辑神器gvim

    如果.vimrc打开显示只读,尝试加上前缀sudo

    set noai
    set nosi
    set cursorline
    set cursorcolumn
    
    iab md module module_name();<cr><cr><cr>endmodule
    iab alc always @(*)begin<cr><cr><cr>end
    iab als always @(posedge clk or negedge rst_n) begin<cr>if(rst_n == 1'b0) begin<cr><cr>end<cr>else begin<cr><cr>end
    iab if0 if(  ) begin<cr><cr>end
    iab if1 if(  ) begin<cr><cr>end<cr>else begin<cr><cr>end
    iab if2 if(  ) begin<cr><cr>end<cr>else if(  ) begin<cr><cr>end<cr>else begin<cr><cr>end
    iab if3 if(  ) begin<cr><cr>end<cr>else if(  ) begin<cr><cr>end<cr>else if(  ) begin<cr><cr>end<cr>else begin<cr><cr>end
    iab ini initial begin<cr><cr><cr><cr>end
    iab dispb $display("@%0t: xxx a=%0b, b=%0b",$time, a, b);
    iab dispd $display("@%0t: xxx a=%0d, b=%0d",$time, a, b);
    iab disph $display("@%0t: xxx a=%0h, b=%0h",$time, a, b);
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • set cursorline:高亮光标当前行
    • set cursorcolumn:高亮光标当前列
    • set noai:取消自动对齐
    • set nosi:取消自动换行
    • iab xxx:在输入模式下,写出缩写,按下空格键即可自动补全!

    5.3.2、编写源文件

    adder32.v

    module adder_32(
    	input 	wire 	[31:0] 	a_in,
    	input	wire	[31:0]	b_in,
    	input	wire			c_in,
        output  wire    [31:0]  sum_out,
        output  wire            c_out
    );
        assign {c_out, sum_out} = a_in + b_in + c_in;
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 输入模式下,输入md,然后回车会自动补全我们在.vimrc设置的对应内容
    • 命令模式下,输入:%s/module_name/adder32_tb_rand/g,替换module的默认名字
    • 命令模式下,使用ws来分割窗口,使用ctrl+[h/j/k/l]在各个窗口之间切换;使用vt来打开左侧导航窗格,使用r来刷新显示刚刚新建的adder32_tb_rand.v
      • 当然也可以用:sp .来分割窗口表示在gvim另开一个窗口,并打开上级目录,然后通过方向键选择文件,回车键确定文件,ctrl + w可以在两个窗口之间进行切换!
    • 例化DUT模块:将adder32.v中的module头信息复制到adder32_tb_rand.v中进行例化,在adder32.v中光标定位到模块开头,然后8yy即可赋值8行内容!在adder32_tb_rand.v合适位置按下p即可粘贴!粘贴完毕后,选中adder32.v所在窗口,然后输入wc即可关闭该窗口!
    • adder32_tb_rand.v粘贴的内容中,在c_out后面加上一个,,便于后面使用正则表达式进行内容的替换!
    • 将第四行的module adder32改成adder32 u_adder32,使用正则表达式:4,4s/\(\w\+\)\(\s\+\)\(\w\+\)/\3\2u_\3/g。正则表达式的框架是%s/①/②/g,首先作用域是第四行,所以%改成了4,4;正则表达式的 - \(\w\+\)\(\s\+\)\(\w\+\) 是将module adder32分为3组,注意其中括号()是需要转义的,所以要用\(\)\w\+是匹配任意多个单词字母;\s\+是匹配任意多个空格;正则表达式的 - \3\2u_\3为要替换的内容,\3表示第三组的内容,其他以此类推即可!
    • 接着把adder32.v中复制的module头信息中的输入输出接口给删除了,删除方法使用visual模式:先按ctrl+v进入visual block模式,然后鼠标或者h/j/k/l
    • 接着还是改module头信息的输入输出,比如更改a_in,.ain (ain),。显然可以使用正则将其分为两组,一组是word,一组是逗号。使用的正则表达式::5,10s/\(\w\+\)\(,\)/.\1 (\1)\2/g,框架还是%s/①/②/g,首先是5-10行,所以%改成了5,10;正则表达式的 - \(\w\+\)\(,\) 是将a_in,这一类的代码分为2组,第一组是a_in之类的字符串,第二组是逗号;正则表达式的 - \3\2u_\3为要替换的内容,替换内容是在第一组前面加点,然后第一组括号,最后是第二组。
    • 最后再将c_out最后的,进行去除。

    同上操作,我们再复制adder32.v中的信号名到adder32_tb_rand.v中并用正则表达式进行一些替换操作!至此便把DUT进行了例化并把所有的信号进行了连接,此时代码内容如下:

    module adder32_tb_rand();
    reg   [31:0] 	a_in;
    reg	  [31:0]	b_in;
    reg			    c_in;
    wire  [31:0]  sum_out;
    wire          c_out;
    adder_32 u_adder_32(
    	.a_in    (a_in),
    	.b_in    (b_in),
    	.c_in    (c_in),
        .sum_out  (sum_out),
        .c_out    (c_out)
    );
    endmodule 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    接下来产生激励,采用的是随机化的方式。

    • 定义一个reg型的数组data_in :reg [32:0] data_in = {32'h0, 32'h0, 32'h0};,还有一个需要注意的是:这里定义的是33bit,因为我们后面要使用系统函数sum来对data_in的三个元素进行求和!(单比特数组的求和返回单比特的数值

      • 这里有必要再解释以下data_in,它是个数组。扩充以下:如果data_in是简单数据,定义reg [7:0] data_in,那么data_in[1]代表数据第1bit;如果data_in是数组,定义reg [7:0] data_in[20],那么data_in[1]代表数据第1元素,元素起始位置也是0,data_in[0].
      • 但是需要注意,后面我们要将data_in的值赋给a_inb_in,但是a_inb_in是32bit的。一种解决方法是,在产生data_in的数据的时候,我们只要32bit,如:data_in[0][31:0] = $random(seed);
    • 定义随机种子seed和激励数目stimulus_num,这两个都是外面的脚本(通常是Makefile)传递进来的

    • 接下来再initial块中读取seedstimulus_num,输入ini即可自动填充完整initial框架!再initial块中输入if1即可自动填充完整if语句块!

      • if语句块中使用$value$plusargs("seed=%d", seed)来获取seed,如果没有获取成功将seed赋值为100,获取成功使用系统函数display将其打印出来!注意我们是十进制打印,直接在输入模式下dispd,然后按空格即可自动补全!
      • 同理可以获取stimulus_num,直接使用6yy + p的方式快速复制粘贴!然后使用:32,37s/seed/stimulus_num/g进行快速替换
    • 接下来使用随机种子来产生激励,在initial块中来赋值,输入ini然后按空格即可自动补全!然后使用repeat来产生stimulus_num个激励(给data_in赋值),注意变量名字很长时,可以使用tab键来快速补全!(Tab键自动补全还有个好处是可以避免拼写错误,这是个不错的好习惯!)

      • 写完data_in[0] = $random(seed); 可以yy + p + p来快速赋值两行,然后在命令模式下使用r + 1r + 2来快速替换data_in的索引。
      • 32位加法器通常没有c_in,所以我们将data_in[2]设置为1'b0。快速操作的方法:光标定位到$random(seed);开头,然后在命令模式下按下D,即可删除光标到行尾的所有内容!
    • 为了和其他模块通信,获取激励是否成功产生的标志,我们这里再定义一个信号量sti_gen(标志信号),在每次激励产生前sti_gen等于0,产生后等于1,这样其他模块捕获一个上沿即可获取是否成功产生激励。

      • 需要注意的是,激励产生完毕,不要立即赋值sti_gen = 1,这样可能会造成dut_sumgld_sum错位的情况发生,应该等一会,这里设置为#10;
    • 产生激励后,将激励值赋给DUT,这里直接使用always的组合逻辑即可,输入模式下输入alc,即可自动补全。

    • 除了产生激励,还要给出Golden模型(Reference Module),Golden结果通常会放到一个队列中!队列语法后面会详细介绍!

    • 给了Golden model还需要将激励发送到Golden model,使用initial块来完成,输入ini即可自动补全!注意,gld_sum.push_back(data_in.sum);操作是队列的一些操作,后面会再详细介绍,这里知道作用即可。其中.push_back.sum均是系统函数!golden model中的.sum是一个行为级描述,而DUT中的是一个RTL描述,通过比对两者结果即可验证!

    • 结果收集好了,接下来就是要做对比了。这里定义两个临时变量gld_sum_rsltdut_sum_rslt。在比较前,需要等到stimulus产生完之后,所以这里需要定义一个事件event sti_end;,关于事件的详细介绍,后面也会详细介绍,这里仅仅知道作用。在stimulus发送完毕之后,把这个事件触发,然后才进行结果的比对!如何把这个事件触发,在Step 2末尾加上-> sti_end即可。然后在Step5中加入@sti_end等待触发即可!

    • 然后获取gld_sum和dut_sum进行比对!

    • 在testbench中check最终结果,我们可以增加一个错误标志信号!err_cnt来记录错误个数,如果err_cnt为0那么就可以进行pass!

    adder32_tb_rand.v

    module adder32_tb_rand();
    reg 	[31:0] 	a_in;
    reg	  [31:0]	b_in;
    reg			      c_in;
    wire  [31:0]  sum_out;
    wire          c_out;
    int        seed;
    int        stimulus_num;
    reg        sti_gen;
    reg [32:0] data_in[3] = {33'h0, 33'h0, 33'h0};
    reg [32:0] gld_sum [$];
    reg [32:0] dut_sum [$];
    reg [32:0] gld_sum_rslt;
    reg [32:0] dut_sum_rslt;
    event sti_end;
    int err_cnt;
    adder_32 u_adder_32(
    	.a_in    (a_in),
    	.b_in    (b_in),
    	.c_in    (c_in),
      .sum_out  (sum_out),
      .c_out    (c_out)
    );
    // Step1, Get random seed and stimulus number arguments from external scripts
    initial begin
      if( ! $value$plusargs("seed=%d", seed) ) begin
        seed = 100;
      end
      else begin
        $display("@%0t: seed a=%0d",$time, seed); 
      end 
      if( ! $value$plusargs("stimulus_num=%d", stimulus_num) ) begin
        stimulus_num = 50;
      end
      else begin
        $display("@%0t: stimulus_num a=%0d",$time, stimulus_num); 
      end
    end 
    // Step2, Generate stimulus with random seed
    initial begin
      sti_gen = 1'b0;
      repeat(stimulus_num) begin
        sti_gen = 1'b0;
        #10;
        data_in[0][31:0] = $random(seed);
        data_in[1][31:0] = $random(seed);
        data_in[2] = 1'b0;
        #10;
        sti_gen = 1'b1;
        #10;
      end
      -> sti_end;
    end 
    // Step2.1, Send stimulus to DUT
    always @(*)begin
      a_in = data_in[0];
      b_in = data_in[1];
      c_in = data_in[2];
    end 
    // Step3, Send stimulus to Golden model
    initial begin
      repeat(stimulus_num) begin
        @(posedge sti_gen)
          gld_sum.push_back(data_in.sum);
      end
    end 
    // Step4, Capture DUT output
    initial begin
      repeat(stimulus_num) begin
        @(posedge sti_gen)
          dut_sum.push_back({c_out, sum_out});
      end
    end 
    // Step5, Check result
    initial begin
      gld_sum_rslt = 'h0;
      dut_sum_rslt = 'h0;
      err_cnt      = 'h0;
      @sti_end;
      repeat(stimulus_num) begin
        gld_sum_rslt = gld_sum.pop_front();
        dut_sum_rslt = dut_sum.pop_front();
        if(gld_sum_rslt != dut_sum_rslt) begin
          $display("@%0t: Error: gld_sum != dut_sum; gld_sum=%0h, dut_sum=%0h",$time, gld_sum_rslt, dut_sum_rslt); 
          err_cnt ++;
        end
      end
      if( err_cnt == 'h0 ) begin
        $display("***********************************"); 
        $display("***********************************"); 
        $display("***********************************"); 
        $display("*************TEST PASS*************"); 
        $display("***********************************"); 
        $display("***********************************"); 
        $display("***********************************"); 
      end 
      $finish();
    end 
    endmodule 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    至此,TestBench编写完毕,接下来我们来编写Makefile。

    Makefile

    
    all: compilation simulation
    seed = $(shell date +%s)
    stimulus_num = 100
    compilation:
    	vcs -full64 -sverilog adder32.v adder32_tb_rand.v -debug_all -l comp.log
    simulation:
    	./simv -l sim_$(seed).log +plusargs_save +seed=$(seed) +stimulus_num=$(stimulus_num)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • seed = $(shell date +%s);:执行一个shell命令,shell命令叫date%s以serial的形式打印,打印出来是一串数字,固定格式!
    • stimulus_num = 100;:定义激励个数为100
    • VCS仿真分为两个部分,其中一个部分是compilation
      • -sverilog:有用到system verilog的语法,所以这里需要加上这个选项!
      • -debug_all:习惯
      • -l comp.log:打印记录放里面
    • VCS仿真另外一个部分是simulation
      • ./simv:仿真命令
      • -l sim_$(seed).log:记录仿真结果,通常与种子联系起来!
      • +plusargs_save +seed = $(seed) +stimulus_num = $(stimulus_num):将外部变量读取到TB中!这里就是读取seedstimulus_num了。要特别注意:seed = $(shell date +%s)stimulus_num = 100这两句后面不能加标点,不然会出现传递不进去参数的情况,如果出现传递参数错误,通常会出现如下提示信息:
      • 另外一个判断参数传递是否成功的方法,可以通过在程序中加入打印信息。比如本程序如果不能打印@0: seed a=1630487312@0: stimulus_num a=100则说明参数传递错误,需要及时debug!
    ...
    /bin/sh: .log: command not found
    /bin/sh: +stimulus_num: command not found
    make: *** [simulation] Error 127
    [verifier@localhost adder32]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    写完之后便可以进行初步的仿真了。

    5.3.3、运行仿真

    在命令行直接输入make即可运行仿真!

    成功输出后的打印日志如下:

    [verifier@localhost adder32]$ make
    vcs -full64 -sverilog adder32.v adder32_tb_rand.v -debug_all -l comp.log
                             Chronologic VCS (TM)
           Version O-2018.09-SP2_Full64 -- Wed Sep  1 17:08:31 2021
                   Copyright (c) 1991-2018 by Synopsys Inc.
                             ALL RIGHTS RESERVED
    This program is proprietary and confidential information of Synopsys Inc.
    and may be used and disclosed only as authorized in a license agreement
    controlling such use and disclosure.
    Warning-[DEBUG_DEP] Option will be deprecated
      The option '-debug_all' will be deprecated in a future release.  Please use 
      '-debug_acc+all -debug_region+cell+encrypt' instead.
    Parsing design file 'adder32.v'
    Parsing design file 'adder32_tb_rand.v'
    Top Level Modules:
           adder32_tb_rand
    No TimeScale specified
    Starting vcs inline pass...
    1 module and 0 UDP read.
    recompiling module adder32_tb_rand
    make[1]: Entering directory `/home/verifier/workspace/adder32/csrc'
    rm -f _csrc*.so pre_vcsobj_*.so share_vcsobj_*.so
    if [ -x ../simv ]; then chmod -x ../simv; fi
    g++  -o ../simv    -Wl,-rpath-link=./ -Wl,-rpath='$ORIGIN'/simv.daidir/ -Wl,-rpath=./simv.daidir/ -Wl,-rpath='$ORIGIN'/simv.daidir//scsim.db.dir  -rdynamic  -Wl,-rpath=/opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux64/lib -L/opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux64/lib   objs/amcQw_d.o   _46704_archive_1.so  SIM_l.o       rmapats_mop.o rmapats.o rmar.o rmar_nd.o  rmar_llvm_0_1.o rmar_llvm_0_0.o          -lzerosoft_rt_stubs -lvirsim -lerrorinf -lsnpsmalloc -lvfs    -lvcsnew -lsimprofile -luclinative /opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux64/lib/vcs_tls.o   -Wl,-whole-archive -lvcsucli -Wl,-no-whole-archive          /opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux64/lib/vcs_save_restore_new.o -ldl  -lc -lm -lpthread -ldl 
    ../simv up to date
    make[1]: Leaving directory `/home/verifier/workspace/adder32/csrc'
    CPU time: .148 seconds to compile + .118 seconds to elab + .142 seconds to link
    ./simv -l sim_1630487312.log +plusargs_save +seed=1630487312 +stimulus_num=100
    Chronologic VCS simulator copyright 1991-2018
    Contains Synopsys proprietary information.
    Compiler version O-2018.09-SP2_Full64; Runtime version O-2018.09-SP2_Full64;  Sep  1 17:08 2021
    @0: seed a=1630487312
    @0: stimulus_num a=100
    ***********************************
    ***********************************
    ***********************************
    *************TEST PASS*************
    ***********************************
    ***********************************
    ***********************************
    $finish called from file "adder32_tb_rand.v", line 116.
    $finish at simulation time                 3000
               V C S   S i m u l a t i o n   R e p o r t 
    Time: 3000
    CPU Time:      0.150 seconds;       Data structure size:   0.0Mb
    Wed Sep  1 17:08:32 2021
    [verifier@localhost adder32]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    附录

    附录1、VCS 常用编译选项

    • 在学习VCS(Verilog Compile Simulator)过程中遇到不知道的编译命令可以使用 vcs -full64 -help 来查看帮助!
    • VCS学习过程中可能需要查看对应的 User Guide,一般情况下在vcs的安装目录下的doc文件夹里面会有PDF文件,或者查看这里。
    • VCS就是编译用户输入的源文件生成对应的可执行文件(默认是二进制的simv文件),在之后的仿真中运行这个可执行文件。

    附录2、常用编译选项及其含义

    VCS语法如下:

    $ vcs [options] source_files
    
     
    • 1
    • 1

    常用编译选项及其含义

    -help:vcs帮助,有各编译选项意义;
    -full64:以64位模式编译设计并创建64位可执行文件用于64位模式下的模拟;
    -vpi:允许使用vpi PLI访问例程;
    -sverilog:允许在Accellera systemVerilog规范中使用Verilog语言扩展;
    -v2k:使用Verilog 1364-2001标准;
    -cpp:使用c++编译器;
    -debug_pp:允许转储到VPD并使用UCLI命令和DVE;
    -debug:启用UCLI命令和DVE;
    -debug_all:启用UCLI命令和DVE,也使线路步进;
    -notice:启用详细的诊断消息;
    +lint=[no]ID|none|all,... 使能或者禁用verilog的lint消息;
    +rad:对设计进行辐射技术优化;
    +vcs+lic+wait:如果没有可用的通知,则告诉VCS等待网络许可证;
    -f <filename>: 指定一个文件,其中包含源文件和编译时选项的路径名列表;
    -o <name>:指定输出可执行文件的文件名,默认为 simv;
    -R:该选项告诉VCS在编译完后直接运行可执行程序,若没有该选项,那么vcs在编译后直接退出;
    -l <filename>:(小写字母L)如果包含-R,-RI或-RIG选项,则指定VCS记录编译消息和运行时消息的日志文件;
    -Mupdate[=0]:默认情况下,VCS会在编译之间覆盖Makefile。 如果希望在编译之间保存Makefile,请输入此内容选项与0参数。输入不带0参数的参数,指定默认情况下,增量编译和更新Makefile文件;
    -CFLAGS <options>:将选项传递给C编译器,允许多个-CFLAGS,允许传递C编译器优化级别。
    -timescale=<time_unit>/<time_precision>:指明时间精度;
    -ucli:在运行时指定UCLI模式;
    +incdir+<directory>:指定包含使用`include 编译器指令指定的文件的目录,可以指定多个目录,用+字符分隔每个路径名称;
    +libext+<extension>:指定VCS仅在具有指定扩展名的Verilog库目录中搜索源文件,可以指定多个扩展名,用+字符分隔每个扩展名。例如+libext++.v指定搜索没有扩展名和库扩展名为.v的库文件。 输入-y选项时输入此选项。
    +systemverilogext+<ext>:指定包含SystemVerilog源代码的源文件的文件扩展名;
    -gui[=<dve|verdi>]:启动用户指定的图形用户界面,如果未提供参数,则在检测到有效的VCS_HOME环境变量时,VCS将启动Verdi。 否则DVE将默认启动;
    -vcd <filename>:将输出VCD文件名设置为指定文件。默认文件名为verilog.dump。Verilog源代码中的$dumpfile系统任务将覆盖此选项;
    -verdi:使用verdi图形界面;
    +vcdfile+<filename>:指定想要用于后期处理的VCD文件;
    -vpd_file <filename>:在运行时,定义VCS写入的VPD文件的替代名称,而不是缺省名称vcdplus.vpd;
    +define+VCS:定义全局的VCS,编译器在编译时如果源文件有类似`ifdef VCS等字样,那么会执行定义之后的代码。
    +vcs+vcdpluson:编译选项,加入后会使能产生vpd文件,默认文件名vcdplus.vpd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    常见问题解决办法

    问题'/opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux/bin/vcs1' for a machine of this type 'linux'. Please check whether 'VCS_HOME' is incorrect

    [verifier@localhost adder32]$ make compilation
    vcs -sverilog adder32.v adder32_tb_rand.v -debug_all -l comp.log
    
    • 1
    • 2

    Error-[VCS_COM_UNE] Cannot find VCS compiler
    VCS compiler not found. Environment variable VCS_HOME
    (/opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux) is selecting a directory in
    which there isn't a compiler
    /opt/synopsys/vcs/vcs-mx/O-2018.09-SP2/linux/bin/vcs1’ for a machine of
    this type ‘
    linux’.
    Please check whether ‘
    VCS_HOME’ is incorrect; if not, see below.

    Perhaps vcs hasn’t been installed for machine of type “linux”.
    Or the installation has been damaged.
    To verify whether vcsO-2018.09 supports machine of type “Linux 3.10.0-1160.el7.x86_64”,
    please look at ReleaseNotes for more details .
    We determine the machine type from uname; maybe uname is incorrect.
    You can fix installation problems by reinstalling from CDROM
    or downloading it from the Synopsys ftp server.
    For assistance, please contact vcs technical support
    at vcs_support@synopsys.com or call 1-800-VERILOG
    make: *** [compilation] Error 1

    解决方法:出现这样的问题是因为装的VCS版本是64 bit的,所以要加个-full64

    参考

  • 相关阅读:
    k8s apiserver启动执行流程之aggregatorServer
    python算法部署(通信篇)
    接口的安全设计要素:ticket,签名,时间戳
    Maven基础学习——tomcat插件配置(含web工程配置)
    陈大好:持续创造小而美的产品丨独立开发者 x 开放麦
    Roson的Qt之旅 #134 QIconEngine类
    Spring(五)Spring 管理第三方Bean和核心容器
    pagmo并行全局多目标优化算法库的安装编译与使用
    一文带你看透手机号码归属地
    作为公司测开岗的面试官,我是怎么选人的....
  • 原文地址:https://blog.csdn.net/luoganttcc/article/details/125634611