• SV基础知识---功能覆盖率 覆盖组、数据采样(语言部分)


    一、覆盖组详解

    • 覆盖组与类相似,一次定义后便可以进行多次实例化。它含有覆盖点、选项、形式参数可选触发。一个覆盖组包含了一个或者多个数据点,全都在同一时间采集。
    • 覆盖组可以定义在里面,也可以定义在程序模块层次上,它可以采样任何可见的变量或者信号
    • 在SV中,覆盖组应该定义在适当的抽象层次上,这个层次可以在测试平台和设计的边界上,在读写数据的总线交换单元中,在环境配置类里或者任何需要的地方。
    • 对任何事务的采样都必须等到数据被待测设计收到以后,如果在事务中间注入一个错误,导致数据传输失败,那么就需要改变功能覆盖中对这种情况的处理方式。
    • 一个类里可以包含多个覆盖组,每个组可以有单独的触发,可以从多个源头收集数据。
    • 一个覆盖组必须实例化后才可以用来收集数据。
       

    1、在类里定义覆盖组

    覆盖组可以在程序、模块、类中定义,但都要进行明确地实例化后才可以采样

    类里的功能覆盖率

    1. class Transactor;
    2. Transaction tr;
    3. mailbox mbx_in;
    4. covergroup CovPort; //定义了一个名为 CovPort 的covergroup(覆盖组)
    5. coverpoint tr.port;
    6. //覆盖组里至少有一个覆盖点,不然采样谁?
    7. // tr.port 采样句柄里软件的变量,和数值,若没列举数值,则采样所有合法数值
    8. endgroup
    9. function new(mailbox mbx_in);
    10. CovPort = new(); //覆盖组例化
    11. this.mbx_in = mbx_in;
    12. endfunction
    13. task main;
    14. forever begin
    15. tr = mbx_in.get(); //获取下一个事务
    16. ifc.cb.port <= tr.port; //发送到待测设计中
    17. ifc.cb.data <= tr.data;
    18. CovPort.sample(); //发生采样,收集覆盖率 CovPort:这里相当于实例名称,不是类名
    19. end
    20. endtask
    21. endclass

    二、覆盖组的触发 [下面三个例子陆科没讲,自行看绿皮书]

    • 功能覆盖率的两个主要部分是采样的数据数据被采样的时刻/事件。当这些新数据都准备好了以后,测试平台便会触发覆盖组。可以直接使用sample函数来完成。
    • 如果想在程序代码中显式地触发覆盖组,可以使用sample方法。
    • 如果要借助已有的事件或信号来触发覆盖组,可以在covergroup声明中使用阻塞语句
       

    1、使用回调函数进行采样

    使用回调函数可以在决定数据采集位置时间

    使用功能覆盖率回调函数的测试

    1. program automatic test;
    2. Environment env;
    3. initial begin
    4. Driver_cbs_coverage dcc; //把回调基类Driver_cbs扩展成Driver_cbs_coverage
    5. env = new();
    6. env.gen_cfg();
    7. env.build();
    8. //创建并登记覆盖率回调函数
    9. dcc = new();
    10. env.drv.cbs.push_back(dcc); //放进驱动器的队列中
    11. env.run();
    12. env.wrap_up();
    13. end
    14. endprogram

    用于测试功能覆盖率的回调函数

    1. class Driver_cbs_coverage extends Driver_cbs;
    2. covergroup CovPort;
    3. ...
    4. endgroup
    5. virtual task post_tx(Transaction tr);
    6. CovPort.sample(); //采样覆盖率数值
    7. endtask
    8. endclass

    2、使用事件触发的覆盖组

    与直接调用sample方法相比,使用事件触发的好处在于能够借助已有的事件进行采样。

    带触发的覆盖组

    1. event trans_ready; //覆盖组CovPort在测试平台触发trans_ready事件时进行采样
    2. covergroup CovPort @(trans_ready);
    3. coverpoint ifc.cb.port; //测量覆盖率
    4. endgroup

    3、使用SV断言进行触发

    带SV断言的模块

    1. module mem(simple_bus sb);
    2. bit[7:0] data, addr;
    3. event write_event;
    4. cover property
    5. (@(postedge sb.clock) sb.write_ena == 1)
    6. -> write_event;
    7. endmodule

    使用SVA触发覆盖组

    1. program automatic test(simple_bus sb);
    2. covergroup Write_cg @($root.top.m1.write_event);
    3. coverpoint $root.top.m1.data;
    4. coverpoint $root.top.m1.addr;
    5. endgroup
    6. Write_cg wcg;
    7. intitle begin
    8. wcg = new();
    9. //在此处添加激励
    10. sb.write_ena <= 1;
    11. ...
    12. # 10000 $finish;
    13. end
    14. endprogram

    三、数据采样

    • 关于覆盖率信息的收集,在覆盖点上指定一个变量或表达式时,SV便会创建很多的“仓(bin)”来记录每个数值被捕捉到的次数。
    • 这些仓是衡量功能覆盖率的基本单位。采样一个单比特变量,最多会有两个仓被创建。
    • 每次覆盖组被触发,SV都会在一个或者多个仓里留下标记。
    • 在每次仿真的末尾,所有带有标记的仓会被汇聚到一个新创建的数据库中。最后利用分析工具进行分析并生成覆盖率报告
       

    1、个体仓和总体覆盖率

    • 为了计算出一个点上的覆盖率,首先必须确定所有可能数值的个数,这也被称为
    • 一个仓中可能有一个或多个值,覆盖率就是采样值的数目除以bin的数目。例如一个3比特变量覆盖点的域是0:7,正常情况下SV会自动分配8个bin【每个bin分别对应一个变量可能值,用于记录采样此值的次数】,仿真过程中如果有7个仓的值被采样到【不管采样多少次,最后只采样到7个值】,那么覆盖率为7/8或者87.5%,所有这些点组合在一起便构成了一个组的覆盖率,而所有组组合在一起就可以得到整个仿真数据库的覆盖率。
       

    2、自动创建仓

    SV会自动为覆盖点创建仓,它通过被采样的表达式的域来确定可能值得范围。对于一个位宽为N的表达式,有2^N个可能值。

    3、限制自动创建仓的数目

    覆盖组选项auto_bin_max指明了自动创建仓的最大数目,缺省值是64。值域超过指定的最大值,SV会把值域范围平分到auto_bin_max个仓中。

    实际操作中,自动创建bin方法不实用,建议自定义仓或减少auto_bin_max的数值。

    使用auto_bin_max并把仓数设置成2

    1. covergroup CovPort;
    2. coverpoint tr.port
    3. {options.auto_bin_max = 2;}
    4. endgroup

    覆盖率报告:

    在所有覆盖点中使用auto_bin_max 

    1. covergroup CovPort;
    2. options.auto_bin_max = 2; //影响port和data
    3. coverpoint tr.port;
    4. coverpoint tr.data;
    5. endgroup

    4、对表达式进行采样

    在覆盖点里使用表达式

    1. class Transaction;
    2. rand bit[2:0] hdr_len; //范围0-7
    3. rand bit[3:0] payload_len; //范围0-15
    4. rand bit[3:0] kind; //范围0-15
    5. ...
    6. endclass
    7. Transaction tr;
    8. covergroup CovLen;
    9. len16 : coverpoint(tr.hdr_len + tr.payload_len);
    10. len32 : coverpoint(tr.hdr_len + tr.payload_len + 5'b0); //带有额外常量哑元的表达式,可以计算达到5比特精度,从而把自动生成的最大仓数扩大到32。
    11. endgroup

    5、使用用户自定义的仓发现漏洞

    自动生成的仓适用于匿名数值,如计数值、地址值或2的幂值,而对于其他数值,应该明确对仓命名。SV会自动为枚举类型的仓命名。

    为事务长度定义仓

    1. covergroup CovLen;
    2. len : coverpoint(tr.hdr_len + tr.payload_len + 5'b0);
    3. {bins len[] = {[0:23]};}
    4. endgroup

    覆盖率报告:

    从图中可以看到长度为23,即十六进制17的一项没有出现过,最长的头是7,最长的负载是15。0-7一共有八种情况,用3bit来表示,0-15一共有16种情况,用4bit来表示。所以总共是7+15=22,而不是23!,我们应该在仓的声明中改成{bins len[] = {[0:22]};}才对。

    6、命名覆盖点的仓

    当自定义仓时,SV不再自动创建仓,而且会忽略掉那些不被事先定义的仓所涵盖的数值,计算功能覆盖率时只会使用创建的仓。

    指定仓名

    1. //对一个4比特变量kind进行采样
    2. covergroup CovKind;
    3. coverpoint tr.kind{
    4. bins zero = {0}; //一个仓zero,对kind采样值为0进行计数
    5. bins lo = {[1:3],5}; //lo仓代表1:35的值
    6. bins hi[] = {[8:$]}; //数组仓:8个独立的仓8-15,分别保存
    7. bins misc = default; //一个仓用来保存没有被选中的值
    8. }
    9. endgroup

     7、条件覆盖率

    使用关键字iff可以覆盖点添加条件。

    条件覆盖——复位期间禁止

    1. covergroup CoverPort;
    2. //reset=1时不要收集覆盖率数据
    3. coverpoint port iff(!bus_if.reset);
    4. endgroup

    也可以使用start和stop函数来控制覆盖组里各个独立的实例

    1. initial begin
    2. CovPort ck = new(); //实例化覆盖组
    3. //复位期间停止收集覆盖率数据
    4. # 1ns ck.stop(); //关闭采样
    5. bus_if.reset = 1;
    6. # 100ns bus_if.reset = 0;
    7. ck.start(); //开始采样
    8. ...
    9. end

    8、为枚举类型创建仓

    对于枚举类型,SV会为每个可能值创建一个仓。

    枚举类型的功能覆盖率

    1. typedef enum{INIT,DECODE,IDLE} fsmstate_e;
    2. fsmstate_e pstate, nstate;
    3. covergroup cg_fsm;
    4. coverpoint pstate;
    5. endgroup

    9、翻转覆盖率

    • 覆盖点可以用来记录变量A值到B值跳转情况
    • 还可以确定任何长度覆盖点的翻转次数
    1. covergroup CoverPort;
    2. coverpoint port{
    3. bins t1 = (0 => 1), (0 => 2), (0 => 3);
    4. }
    5. endgroup

    使用范围表达式可以快速地确定多个翻转过程,(1,2 => 3,4)创建了四个翻转过程,分别是(1 => 3),(1 => 4),(2 => 3),(2 => 4)。

    10、在状态和翻转中使用通配符

    可以使用关键字wildcard来创建多个状态或翻转。在表达式中,任何X、Z或?都会被当成0或1的通配符。

    用在覆盖点仓中的通配符

    1. bit[2:0] port;
    2. covergroup CoverPort;
    3. coverpoint port{
    4. wildcard bins even = {3'b?? 0};
    5. wildcard bins odd = {3'b?? 1};
    6. }

    11、忽略数值

    使用ignore_bins排除掉不用来计算功能覆盖率的数值

    1. bit[2:0] low_ports_0_5; //只使用数值0-5
    2. covergroup CoverPort;
    3. coverpoint low_ports_0_5{
    4. ignore_bins hi = {[6,7]}; //忽略掉6,7两个仓
    5. }
    6. endgroup

    12、不合法的仓

    使用illegal_bins对仓进行标识,可以捕捉到那些被错误检查程序遗漏掉的状态

    1. bit[2:0] low_ports_0_5; //只使用数值0-5
    2. covergroup CoverPort;
    3. coverpoint low_ports_0_5{
    4. illegal_bins hi = {[6,7]}; //如果出现6,7两个仓就会报错
    5. }
    6. endgroup

    13、状态机的覆盖率

    如果在状态机上使用了覆盖组,那么就可以使用仓来列出特定的状态和翻转轨迹,使用代码覆盖率工具可以自动提取状态寄存器、状态以及翻转轨迹。

  • 相关阅读:
    百度百科数据爬取 python 词条数据获取
    通过Docker Compose安装MQTT
    UVA-122 树的层次遍历 题解答案代码 算法竞赛入门经典第二版
    批量归一化(标准化)处理
    SPA项目开发之首页导航与左侧菜单 - mock.js模拟数据
    技术分享 | app自动化测试(Android)--显式等待机制
    网络爬虫 -- 验证码识别
    吞吐量(TPS)、QPS、并发数、响应时间(RT)概念
    云计算发展
    【Java SE]位运算和移位运算需要知道的重点
  • 原文地址:https://blog.csdn.net/Arvin_ing/article/details/127814363