| |
完整的、详细的设计规范是验证工作的重要起点。
验证工作根据设计规范(specification)进行,详细的spec是RTL代码编写工作的依据,也是验证工作的依据。当验证过程发现DUT的响应与testbench预计的不符时,需要根据spec判断是DUT出现错误还是testbench出现错误。
参数化的全局定义
全局定义会给书写和仿真带来很大方便,在编写testbench过程中,如果激励中有一些重复的事件,可以考虑将这些语句编写成一个task。比如:
1.Register相关位及其数值可以全局宏定义在reg_define.v中。
2.相关路径可以全局宏定义在define_board.v中。
3.系统重要变量的显示信息可以定义在display.v中。
4.与Register相关的比较任务和报错任务可以编写成task定义在reg_cmp.v中。
5.时钟周期参数的定义,一般局部定义,用parameter定义。
存取波形及相应变量的数据,使用`ifdef为全局定义使用
1.波形源头文件是VCD波形,但过于庞大,可用来做功耗分析。
1 $dumpfile(“wave.vcd”); //打开数据库
2 $dumpvars(1, top.u1); //scope = top.u1, depth = 1
3 //第一个参数表示深度, 为0时记录所有深度; 第二个参数表示scope,省略时表当前的scope.
4 $dumpvars; //depth = all scope = all
5 $dumpvars(0); //depth = all scope = current
6 $dumpvars(1, top.u1); //depth = 1 scope = top.u1
7 $dump0ff;//暂停记录数据改变,信号变化不写入库文件中
8 $dumpflush;//重新恢复记录
2.SHM波形是Cadence的,可以用simvision打开。
1 $shm_open("waves.shm"); //打开波形数据库
2 $shm_probe(top, "AS"); //set probe on "top"
3 //A -- signals of the specific scrope
4 //S -- Ports of the specified scope and below, excluding library cells
5 //C -- Ports of the specified scope and below, including library cells
6 //AS -- Signals of the specified scope and below, excluding library cells
7 //AC -- Signals of the specified scope and below, including library cells
8 //还有一个 M ,表示当前scope的memories, 可以跟上面的结合使用, 如"AM" "AMS" "AMC"。什么都不加表示当前scope的ports。
9 $shm_close //关闭数据库
3.FSDB波形是Novas的,可以用nwave打开。
1 $fsdbDumpfile(“wave.fsdb”); //打开数据库
2 $fsdbDumpvars(0, top.u1); //scope = top.u1, depth = 0
4.VPD波形是Synopsys的,可以用dve打开。
1 $vcdplusfile(“wave.vpd”); //打开数据库
2 $vcdpluson(1, top.u1); //scope = top.u1, depth = 1
5.变量的存取,使用文件I/O来操作。
(1).打开文件
1 integer file_id;
2 file_id = fopen("file_path/file_name");
(2).写入文件
1 //$fmonitor只要有变化就一直记录
2 $fmonitor(file_id, "%format_char", parameter);
3
4 //$fwrite需要触发条件才记录
5 $fwrite(file_id, "%format_char", parameter);
6
7 //$fdisplay需要触发条件才记录
8 $fdisplay(file_id, "%format_char", parameter);
(3).读取文件
1 integer file_id;
2 file_id = $fread("file_path/file_name", "r");
(4).关闭文件
$fclose(fjile_id);
(5).由文件设定存储器初值
1 $readmemh("file_name", memory_name"); //初始化数据为十六进制
2 $readmemb("file_name", memory_name"); //初始化数据为二进制
(6).还可以使用宏来选择变量的存取和存取时间使用
1 `ifdef SAVE_LROUT
2 start_save = 1’b1;
3 #(10e6) stop_save = 1’b1;
4 `endif
5 xxx = $fopen(“xxx”, “w”);
6 if (start_save && !stop_save)
7 $fwrite(xxx, “%f\n”, x);
8 $fclose;
测试案例,case
1.case本身尽可能模块化。
2.case最好是自动的、自检的case,可以自动报错,以节省测试时间。
3.覆盖率问题:覆盖率分为功能覆盖率,代码覆盖率,还有人为添加的一些覆盖点的覆盖率。它提供关于仿真的统计信息,包括所经历的结构和转移,以及如何经历。可以决定设计的哪些部分没有被仿真,以知道验证中的薄弱处。最容易实现100%的是代码覆盖率,但是如果verilog代码中使用了case的default,那就很难实现100%覆盖了。功能覆盖率就是一些函数的功能,还有状态机的状态覆盖率等等。然后还有就是验证工程师添加的覆盖点。一般验证工作完成以后要使用这些东西完成报告的。
4.主要的仿真线程常常用初始语句initial模仿,包含一系列阻塞表达式。
5.个人认为,写case本身并不是很重要,重要的是你的case里的测试点是否全面,相关的东西测的全不全。
6.case要尽量提供随机激励信号来增加验证的测试空间,这样能够使验证覆盖的功能空间最大化。这里的随机一般是约束随机,而不是一般意义上的随机一般的随机没有针对性。
7.case的编写可以分为以下三步:From specification to features, From features to testcase, From testcase to testbenches
(1).Case编写的第一步是辨别需要验证的特征(feature),不同的feature,适合的验证层次也不同,有些适合在component(unit/reusable/asic)级进行验证,有些则必须在system级验证。Component级的feature完全包含在待验证的component中,因此其验证与系统其他模块无关,可以独立进行。System-level features涉及系统多个单元之间的相互作用,System-level features不宜多,能够在Component-level验证的features,不要定义为System-level features。
(2).形成testcase之前,首先要对Features进行分类:
Must-have(必须的):设计为了能正常工作或满足市场需要而必须具有的功能,这是first-time success的主要内容,应在各种条件下做彻底的验证。
Should-have(应该有的):主要用于扩展设计的性能或与竞争对手相区别,只需对基本功能进行验证,若有时间与资源,可做进一步详细验证;
Nice-to-have(最好有的):做为设计实现的可选项,若时间允许,可验证一次,一般都不做验证。
根据Features的prioritize,可避免调整验证计划时漏掉must-have features。
8. case验证细节也需要划分。比如有些case可以直接通过自检或是通过波形便可知道功能对错,而有些case因为涉及到dsp性能指标(如信噪比、频谱、分离度等),需要验证人员将输出数据导出到MATLAB中得出更加具体的分析。
系统激励
1. 用MATLAB产生归一化的数据,使用readmemb/readmemh读入Verilog仿真。同样可以将仿真后的数据读入到MATLAB中,以分析相关特性。
2. Testbench的时钟和复位应该在全局层次上模仿。用非阻塞赋值初始化testbench的时钟和复位,用阻塞赋值更新它们。
1 `timescale 1ns/1ns
2 `define PERIOD 5 //100MHz clock
3 initial begin
4 clk <= 0;
5 forever #(`PERIOD) clk = ~clk;
6 end
7 initial begin
8 rst_n <= 0;
9 @(negedge clk) rst_n = 1;
10 end
3. 时间刻度`timescale:根据仿真精度和运行时间平衡来选择。
4. 总线功能模型BFM:为仿真模型中定义的接口提供手段。也就是说,设计者没有仿真整个器件的低层次模型,就可以对一组时序或协议的要就进行校验。
后仿文件添加
1. 设计好的testbench一般允许方便地移植到门级仿真,带门级模块的测试台的主要变化是移去了可综合的RTL文件和添加带支持库和时序信息的网表。
与前仿真相比,后仿真测试对象的变化带来的仿真环境的变化主要体现在两点:
(1).引脚的连接:在RTL代码和逻辑网表中引脚定义的方式是相同的。但针对同一个逻辑,RTL代码综合后的逻辑网表的引脚命名方式同RTL代码中引脚的命名方式就有一些差别了。
(2).调用SDF文件:Cadence的仿真工具NC-verilog既可以进行RTL仿真,又可以进行门级仿真。它提供解析SDF的任务,并在仿真的开始调用。
1 `ifdef GATE_SIM
2 $sdf_annotate ("sdf_file"
3 {,module_instance}
4 {,"config_file"}
5 {,"log_file"}
6 {,"mtm_spec"}
7 {,"scale_factors"}
8 {,"scale_type"} );
2. 设计者必须分别在快和慢两个极端时考虑保持和建立时间的冲突,与此对应,验证人员后仿时的SDF文件有slow_sdf,、fast_sdf 和typical三种。
验证语言与验证方法
1.Verilog就不说了,是基本,要求是debug的时候能定位到错误发生的位置。
2.Systemverilog是验证的核心,现在各家大公司用的验证环境几乎清一色的都是使用 systemverilog 搭建的。
3.C一般是用来写stimulus,一般SoC都有至少一个cpu core,里面放C程序来跑仿真。
4.至于脚本语言,因为Linux/Unix上基本都是命令行操作,脚本会大大提高你的工作效率,因此也是必须掌握的。脚本一般是在完成验证环境搭建以后,执行相关运行命令和批处理命令等,Perl、Shell、Tcl之类的学会至少一种即可。
5.常见的验证平台有 VMM 和 OVM,以后还会有UVM。当然现在市场的主力军还是 VMM,但是由于 OVM 是开源的,所以 OVM 发展也是很快的。VMM是synopsys 公司主导使用的,OVM 是由 Cadence 和 mentor 合作开发的。
6.断言(assert)是个好东西,assert 功能很强大,也很容易上手,能深层次的发掘设计错误,定位很准确,也正是由于这些优点,所以验证工程师不能非常容易的使用它,因为验证工程师一般可以不需要了解太多的设计细节就可以对设计模块进行验证,但是assert 需要比较清楚的了解内部信号,才能将内部信号连接到相应的 assert 上。建议 IC 设计工程师学习哦。
现在一般设计采用层次化设计,相应地验证工作要采用分层验证的方法。
验证层次一般划分如下:
–Unit-Level Verification(功能单元的验证)
–Reusable Components Verification(可重用单元的验证)
–ASIC and FPGA Verification(ASIC和FPGA验证)
–System-level Verification(系统级验证)
–Board-Level Verification(板级验证)
各验证层次的特点和验证方法分别叙述如下:
1. Unit-Level Verification(功能单元的验证) Design unit的划分是一种逻辑划分,随着设计的深入,design unit的功能和接口将发生较大的变化,因此Design unit的验证一般由设计者自行验证,验证的目的是保证设计单元的RTL代码无语法错误且能实现基本的功能,不用考虑代码的覆盖率及递归测试。对于大型设计,每个Design unit都需要一个专门的验证环境,产生激励和检查响应要花去大量的时间,而且每个Design unit都写testbench ,工作量非常之大,所以进行正式的验证过程是不可能的,所以Design unit的验证一般采用ad-hoc(特别)的形式。但Design unit的集成性需在ASIC或FPGA-level进行验证。对于复杂的ASIC设计,可能存在复杂的Design unit,该Design unit的验证需具有较强的可见性与可控性,而且和该Design unit相关的功能尽可能都得到验证。
2. Reusable Components Verification(可重用单元的验证) 可重用单元是一种独立的设计部件,和具体应用无关,它具有标准的外部接口,其testbench 具有重用性。修改过的可重用单元应进行递归验证,以保证设计的后向兼容性,如果对设计的功能进行了修改,形式验证不会起作用;设计可重用单元时,应将验证过程以文档的形式加以记录,获取用户对可重用单元的信任;
3. ASIC and FPGA Verification(ASIC和FPGA验证) ASIC and FPGA 属于物理上的划分,它们的接口和功能在初步设计时就定义好了,不会有太大改动,这时可进行black-box验证。对于复杂的ASIC芯片,可将ASIC验证做为系统验证;
4. System-level Verification(系统级验证) System是一种逻辑上的划分,由独立验证过的部件组成,系统级验证主要验证Design unit之间的相互关系,对于Design unit本身的功能在Unit-level或ASIC-level已进行过验证。为了减少仿真的迭代,对定义的testcase,尽量将testcase不需要的Design unit排除,使system规模尽可能小。
5. Board-Level Verification(板级验证) 利用Board-level 设计工具生成的板级模型,设计的物理实现和Board-level仿真之间是一致的,与System 逻辑模型不同。板级模型所包含的组件模型可来自第三方也可用hardware modeler代替,验证时应对板级物理参数进行模拟,保证功能的正确性。Board-level连通性的验证采用形式验证,将对组件管脚连接的描述和Board-level 设计工具生成的网表进行比较。验证时首先要定义验证的粒度等级,如:system level、unit level,同时还需确定各级的testcase,根据对设计实现的把握决定back-box或white-box的testcase,即testcase 的抽象级别,然后确定激励的输出结果及输出结果的检查办法。