覆盖目标与覆盖模型
为减少资源浪费,覆盖率被用作指导验证资源分配的基准,通过识别设计中已测试和未测试的部分来实现。覆盖率定义为已达成验证目标的百分比,作为评估验证项目进度的指标,从而减少验证设计所需的仿真周期。
总体而言,覆盖率指标主要分为两大类:一类是能从设计代码中自动提取的指标,例如代码覆盖率;另一类是用户自定义的指标,用于将验证环境与设计意图或功能特性相匹配。后者被称为功能覆盖率,是用户自定义的指标,用于衡量测试计划中列出的功能特性是否已得到充分验证。该指标可用于评估测试计划中定义的有趣场景、边界情况、规范不变量或其他适用设计条件是否已被观察、验证和测试。
| | 同义词 | 定义 | 关联 | | — | — | — | — | | 覆盖率 | coverage | 覆盖率定义为已达成验证目标的百分比,作为评估验证项目进度的指标 | 验证目标 | | 覆盖目标 | coverage target/goal | 验证计划的一部分,从spec的功能点得出的量化的任务条目(功能覆盖率),从代码的复杂程度、项目要求得出覆盖率目标值(代码覆盖率) | (验证计划) 功能点 | | 覆盖度量 | coverage metrics | 测试度量标准,包括代码覆盖率、功能覆盖率、断言覆盖率的所有细分子类 | 根据创造方式与来源分类 代码覆盖率是implicit+implementation 功能覆盖率是explicit+specification 断言覆盖率是explicit | | | explicit coverage | | | | | measure | 选择使用的覆盖度量 | 覆盖度量 | | 覆盖空间 | coverage space | 覆盖空间是现有和未有的所有coverage metrics的集合 | 覆盖度量 | | | cover directive | | |
代码覆盖率
| 同义词 | 定义 | 关联 | |
|---|---|---|---|
| 代码覆盖率 | code coverage, 结构性覆盖率, structural coverage | ||
| 行覆盖率 | line coverage | ||
| 条件覆盖率 | condition/expression coverage | 一个分支的所有可能条件是否都出现过 | |
| FEC(focused expression coverage) | |||
| 有限状态机覆盖率 | fsm coverage | ||
| 翻转覆盖率 | toggle coverage | ||
| 分支覆盖率 | branch coverage | 每个分支是否都执行过 | |
| 语句覆盖率 | statement coverage | 类似行覆盖率,一个语句有多行 |
功能覆盖率
功能验证是设计和验证复杂系统所需资源的重要组成部分。通常需要在不重复劳动的前提下实现全面验证。
sv提供功能覆盖率结构covergroup与cover property/sequence,用来搭建覆盖率模型
| 同义词 | 定义 | 关联 | |
|---|---|---|---|
| 功能覆盖率 | function coverage | 功能覆盖率是已测用户定义的功能点占全部用户定义的功能点的比例,通过覆盖率模型获取。分为cover stimulus、cover scoreboard与cover configuration | 功能点 覆盖率模型 |
| 激励覆盖率 | cover stimulus | ||
| 记分板覆盖率 | cover scoreboard | ||
| 配置覆盖率 | cover configuration | ||
| 功能覆盖率类的覆盖度量 | 功能覆盖率这一类的coverage metric分为覆盖属性与覆盖组 | 覆盖属性 覆盖组 | |
| 覆盖率收敛 | coverage closure | 通过测试激励与覆盖率模型的迭代(功能覆盖率),或者测试激励与测试平台的修改(代码覆盖率),使得覆盖率达到覆盖目标的要求 | 测试激励 覆盖率模型 覆盖目标 |
| 功能特性项 | feature | 在项目初期的spec中对功能进行描述和粗粒度的分解 | |
| 功能点 | function point | 功能点是从设计或验证计划的角度,对feature进行细粒度拆分,所得到的一个或一系列完成特定功能的、可验证的硬件行为 | 验证计划 功能特性项 |
| 覆盖率模型 | coverage model | 覆盖率模型是带有意图、针对验证目标编写的有层次、有结构的一组覆盖点集合,分为covergroup modelling和cover property modelling。 | 覆盖组建模 覆盖属性建模 |
覆盖组模型的实现
| 同义词 | 定义 | 关联 | |
|---|---|---|---|
| 覆盖组建模 | covergroup modelling | 由覆盖组实现,检查数值的组合 | 覆盖组 |
| 基于覆盖组采集的覆盖率 | covergroup based coverage | ||
| 覆盖组 | covergroup, coverage group | 覆盖组是在指定事件下采样的一组覆盖点 | 覆盖点 |
| 覆盖点 | coverpoint, coverage item | 覆盖点是对指定信号检查是否处于覆盖仓中 | 覆盖仓 |
| 在timeslot的哪个region采样和评估? | |||
| 覆盖仓 | coverbin, coverage bucket | 覆盖仓是一个取值范围(表达式、transition?这里需要sv的术语) |
覆盖属性模型的实现
覆盖率模型中的覆盖属性建模对应覆盖率中的协议覆盖,以类似SVA语法的形式。
这些往往用于形式验证、断言覆盖率,但是也可以用于功能覆盖率,作为covergroup的补充
| 同义词 | 定义 | 关联 | |
|---|---|---|---|
| 覆盖属性建模 | cover property modelling | 由断言实现,检查时序状态的跳转 | 覆盖属性 |
| 时序上的覆盖率 | temporal coverage | ||
| 覆盖属性 | cover property | 覆盖属性验证时序行为是否发生 | property 关键字 |
| 覆盖序列 | cover sequence | 覆盖序列(不检查必然性) | sequence关键字 |
| immediate cover | 虽然列着但是根本没用 | ||
| 用断言语法实现覆盖属性 | 这一点不是很精确,断言是assert property,覆盖是cover property,语义是不一样的,assert是检查对不对,cover statement是如果对就覆盖,但是不会fail。但是确实列在assert覆盖率下 |
cover property返回:属性被尝试检验的次数(attempt),属性成功的次数(match),属性失败的次数(fail),属性空成功的次数(vacuous match)。
- 使用property assertion或sequence匹配序列,知道某个时序场景是否发生过,完成覆盖属性
- hybrid coverage:当sequence完成、property assertion通过时,触发covergroup 采样
举例:
covergroup axi_resp_cg;
coverpoint bresp;
coverpoint rresp;
endgroup: axi_resp_cg
axi_resp_cg axi_resp_cg_1;
initial begin
axi_resp_cg_1 = new();
end
property WRITE_CHANNEL_VALID_DEPENDENCY;
@(monitor_cb) $rose(awvalid&awready) |->
$rose(wvalid && wready && wlast)[->1]
##[1:20] bvalid;
endproperty: WRITE_CHANNEL_VALID_DEPENDENCY
WRITE_CHANNEL_VALID: assert property(WRITE_CHANNEL_VALID_DEPENDENCY); // 断言
COV_WRITE_CHANNEL_VALID: cover property(WRITE_CHANNEL_VALID_DEPENDENCY) begin // 覆盖属性并触发覆盖组采样
axi_resp_cg_1.sample();
end
总结
功能覆盖率的分类,根据验证环境:
- 激励覆盖率:收集序列或驱动器给DUT的信号,用于确保测试空间被完全探索,而不是确保DUT对这些激励都能正确反应
- 记分板覆盖率:收集记分板或监视器从DUT获得的信号,代表DUT正确完成的功能。这里只收集DUT的输入是没有意义的,应该收集输出,或者同时收集输入与输出合并成一个监视事务
-
配置覆盖率:定义在验证环境中
(对激励与记分板覆盖率,可以定义在interface中,直接采集DUT的信号,用于收集协议、时序覆盖率,可以与断言共享采样;或者定义在验证环境中,收集事务级的覆盖率,用于收集记分板和比较复杂的激励覆盖率。有一种分类,将这两种称为协议覆盖与测试计划覆盖)
另一种覆盖率分类,根据验证对象:
- bus protocol monitor & coverage:接口协议验证,主要使用property assertion + property coverage与hybrid coverage,对应interface、agent层次。往往是现成的vip
-
block level coverage:模块功能验证,对一个模块的全面覆盖;主要使用covergroup。使用寄存器模型(不关心接口如何实现,因为接口会使用bus protocol monitor & coverage来充分验证)
功能覆盖率只是对测试计划的数据统计,由于测试计划本身不完备,功能覆盖率100%不代表验证充分
断言覆盖率
功能覆盖率中,使用覆盖属性模型得到的时序行为覆盖率子集
RTL代码中的断言都被评估过
用于简单协议的时序验证和大模块内部的子模块接口
覆盖率收集与分析
验证数据库与报告(URG)
覆盖率数据的合并和汇总
urg -dir simv.vdb/ -f filelist_vdb.f -dbname simv_merge.vdb
生成易于阅读的HTML覆盖率报告
urg -dir simv_merge.vdb -report coverage_report
基于HVP的验证计划
| 同义词 | 定义 | 关联 | |
|---|---|---|---|
| 验证数据库 | coverage database | ||
| HVP | hierarchical verification plan | 构建可执行的验证计划的描述语言。 | 验证计划 |
| 计划 | plan | hvp中定义的验证计划,包括了属性、标注、测度与功能特性项的层次结构 | 属性 标注 测度 功能特性项 |
| 规范 | spec | 关联pdf文件 | |
| 子计划 | sub-plan | 通过包括.hvp文件,在计划或功能特性项下包含的计划 | |
| 测度 | metric | 要从验证数据库标注到验证计划的数据 | 覆盖率测度 测试测度 |
| 内嵌测度 | built-in metrics | 在编译过程中被引入、用户不能重新定义的测度 | 测度 |
| 测度类型 | ratio、…… | ||
| 目标 | goal | 测度完成要满足的目标表达式 | |
| 聚合方法 | aggregator | 测度的数据怎么合并计算,包括max、min、sum、average。 | |
| apply | explicit … | ||
| 标注 | annotation | 整数、实数或字符串类型的变量,不能传递给功能特性项层次结构的子树 | |
| 属性 | attribute | 整数、实数或字符串类型的变量,可传递给功能特性项层次结构的子树 | |
| 功能特性项 | feature | 包括属性、测量项、子功能特性项的定义,其中测量项对应的数据会被反标到功能特性项上。 | |
| feature与verification specification pdf文件 | |||
| 子功能特性项 | sub-feature | 功能特性项下定义的功能特性项 | |
| 测试项 | measure | 选择从验证数据库提取哪些测度 | |
| 测量源 | source | 验证数据提取的范围,包括但不限于实例、模块、覆盖组、断言。 | |
| owner | plan与feature的默认内置属性 | ||
| at_least | plan与feature的默认内置属性 | ||
| 权重 | weight | plan与feature的默认内置属性 | |
| description | plan与feature的默认内置属性 | ||
| test.expected | plan与feature的默认内置属性 |
寄存器模型
寄存器抽象层 from VMM:RAL就是验证环境中寄存器的“软件模型”或“镜像”。使寄存器访问变得抽象、标准化和可重用。
寄存器模型进一步隔离了与dut强交互的组件 和 与dut弱交互的组件(命令层与功能层),但同时增强了与dut弱交互的组件 和 dut 之间数据交换的自由度,并且从时间和空间上扩展了寄存器访问的范围。
- 为那些与激励无关的组件提供直接的、访问寄存器值的方法:在没有寄存器模型之前,只能启动 sequence 通过前门(FRONTDOOR)访问的方式来读取寄存器,局限较大,在 scoreboard、refmodel中难以控制。现在其他这些组件只需要和寄存器模型交互
- 在仿真之外的时间访问寄存器值:有了寄存器模型后,可以在任何耗费时间的phase中使用寄存器模型以前门访问或后门(BACKDOOR)访问的方式来读取寄存器的值,同时还能在某些不耗费时间的 phase(如 check_phase)中使用后门访问的方式来读取寄存器的值。
在这个项目中,寄存器模型可以有这几个用处:
- 后门读取内部的数据,来比对写操作的结果
- 使得组件前门访问来发送激励
- 初始化时,先后门访问随机化所有寄存器,再正常前门访问设置必要的寄存器
ralgen生成寄存器模型
参考https://alvinrolling.github.io/eda/uvm/ralgen/
Synopsys ralf文件
| cd ../env; ralgen -uvm -l sv -t top_env -c b -c a -c -b “top_env_top.ram_true_dual_port” f top_env.ralf;cd - | 在run/Makefile 的comp中,使用ralgen从top_env.ralf文件生成寄存器模型 选项参数: -t [.ralf文件中顶层block或system的名称] -uvm 使用UVM作为生成代码的方法学,代码文件的名称是ral_topname.sv -c a 生成覆盖率模型【Address Map】 -c b 生成覆盖率模型【Register Bits】 -c f 生成覆盖率模型【Field Values】 -b 生成后门访问代码 | | — | — | | block top_env { bytes 1; #register CHIP_ID @’h0000 {
field REVISION_ID {
bits 8;
access ro;
reset ‘h03;
}
field CHIP_ID {
bits 8;
access ro;
reset ‘h5A;
}
field PRODUCT_ID {
bits 10;
access ro;
reset ‘h176;
}
#} memory RAM (ram_data) @’h0 { size 16; bits 8; access ro; initial 0++; } } | field是最基本的寄存器单元 field 名称 { bits 位宽; access 是否允许读写; reset 复位值; } Register是Field的集合(little-endian则先包含处于低位的field)。多个Register可以组合成Register File或者Blocks register 名称 @地址偏移 { field … } Register File是一系列连续Register的集合。它可以组合成Blocks。 regfile 名称 [宽度] @地址偏移 { register … } Block 是Register和Memory的集合 block 名称 { bytes 可同时读取的字节数; endian 字节序; register … memory … } System包括了Block或者sub-system,可用于生成更顶层的System (理论上可以用圆括号添加hdl path,但是我这么写没有用) | | /* build regmodel */ this.regmodel = ral_block_top_env::type_id::create(“regmodel”,this); regmodel.build(); // ToDo: To enable backdoor access specify the HDL path regmodel.set_hdl_path_root(“top_env_top.ram_true_dual_port”); uvm_config_db#(ral_block_top_env)::set(this, “refm”, “regmodel”, regmodel); | 在验证环境env中,创建寄存器模型对象,并构建。 将寄存器模型集成到系统中时,设置根路径,即寄存器模型(block top_env)对应实际电路模块的路径(这里可以获取dut的路径,赋给regmodel,避免频繁修改) 将寄存器模型通过config_db连接到参考模型,参考模型会在特定时间后门访问。 | | virtual function void build(); this.default_map = create_map(“”, 0, 1, UVM_LITTLE_ENDIAN, 0); this.RAM = ral_mem_top_env_RAM::type_id::create(“RAM”,,get_full_name()); this.RAM.configure(this, “ram_data”); this.RAM.build(); this.default_map.add_mem(this.RAM, `UVM_REG_ADDR_WIDTH’h0, “RW”, 0); uvm_config_db #(uvm_reg_block)::set(null,””,”RegisterModel_Debug”,this); endfunction : build | 这是regmodel.build()的具体内容。 在寄存器模型(继承uvm_reg_block)中创建RAM对象(继承uvm_mem) 由于要后门访问,通过RAM.configure设置路径参数(RAM相对regmodel的路径。不需要后门访问就设为空) 构建 将RAM添加到default_map(如果没有给它分配地址空间、不进行前门访问可以没有这一步) | | this.add_hdl_path(“top_env_top.ram_true_dual_port”); this.RAM.add_hdl_path_slice(“ram_data”, 0, 8); | 这两句在这个场景是不必要的。如果一个reg是由很多个field拼接而成,需要单独添加每个field的hdl路径 |
寄存器模型的覆盖组
- 使用ralgen生成覆盖组:
- 为system/block/memory/register/field添加cover +a+b+f属性,注意f属性(字段值)默认关闭,即使添加了自定义的覆盖组也需要写cover +f;成员继承属性
- 覆盖组可以通过ralgen的执行参数自动生成,也可以自定义
- 寄存器模型需要额外的操作以启用覆盖组:
- 寄存器模型默认是不启用访问,不启用覆盖组的
- address map coverage和register bit coverage会跟着读写自动采样,field_value_coverage需要用户自己调用顶层的sample_value采样
| function void build_phase(uvm_phase phase); uvm_reg::include_coverage(“*”, UVM_CVR_ALL); rm = reg_model::type_id::create(“my_reg_model”); rm.configure(null, “top.dut”); rm.build(); rm.lock_model(); rm.reset(); rm.set_coverage(UVM_CVR_ALL); endfunction | 在测试基类的build_phase include_coverage开启属性对应的覆盖,否则会有报错: include_coverage not located did you mean xxx? configure函数一则将这个寄存器模型挂在其父节点下,二则设置HDL路径用于后门访问 build构建模型 在调用函数get_reg_by_offset、get_mem_by_offset之前,需要先调用lock_model()锁定模型(不能再添加mem或reg),以避免模型后续被修改、访问出错 在test基类的end_of_elaboration_phase中手动打开统计覆盖率,例如:env.regmodel.set_coverage(UVM_CVR_ALL)。 | | — | — |