UVM入门和进阶1:验证方法学概述_类库地图_工厂机制_覆盖方法

参考文档链接:https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/

1 验证方法学概述

1.1 我们所处的验证时代

原有HDL受限于静态例化,同时随即约束短板
验证技术应该更为灵活化


Verilog1.0->Verilog2.0———>SystemVerilog3.0(基于verilog发展的所以一上来就是3.0)


1.2 UVM的优势

Universal(通用)

精力集中到:设计验证计划,减轻创建测试环境

1.3UVM的发展历程与演变

  • UVM 1.0就是基于OVM
  • UVM1.1 UVM1.2
  • UVM1.2重大里程碑,被纳为标准

注意哪些是新方法,哪些是旧方法(一些旧方法已注销)

1.4 学习路线(共五周)

同v2SV一样,参考如何构建环境进行学习

五周学习不会超过50个类,非常核心的也就二十到三十个类之间大概十分之一,剩下的谁在用(UVM自动调用)

2 类库地图

2.1 概述

  • SV通过句柄一层一层的实现访问,UVM有替代

  • 创建、访问、修改、配置都是可以重用的,所以反到类库当中

  • 标准化减轻了构建验证环境的负担

SV与UVM对验证环境的共同需求

2.2 UVM世界(10大核心类)

对象的生成是动态的,需要UVM帮助去创建,如②提供对底层组建的创建与访问

环境中层次之间的创建,链接,层次间组建的控制方法

2.3 UVM世界讲解

核心基类:

​ ① uvm_void与uvm_object(非常核心):会提空基本的方法,比如:拷贝、创建、打印

​ ② 工厂类:注册、创建、覆盖我们的一些类型

​ ③ 事务和序列类:发送激励,帮助实现测试场景。
事务类:组件之间数据类型的定义,数据是如何产生

​ ④ 结构创建类:uvm_component(uvm组件类,重要,因为第五个所有的类型都继承于uvm_component。我们在UVM会经常提到这个类是继承于object还是component)

​ ⑤ 环境组件类

​ ⑥ 通信管道类:fifo , channel 与SV学到的队列和信箱类比
组件间相连用端口,发送数据的存储到通信管道内(因此第四周学通信管道和事务接口)

​ ⑦ 消息报告类:消息如何打印,消息是怎么样控制的

​ ⑧ 寄存器模型类(第五周学)

​ ⑨ 线程同步类:与SVevent对比

​ ⑩ 事务接口类(稍微特殊) :
有一些port后缀,代表端口,端口用来做通信的
这些端口是验证组件之间要通信的话他们之间往往需要端口,即⑩类与⑤类有关系,在组件之间例化,通过端口连接
⑩类还比较特殊再不继承于object而直接继承void

组件类、管道类、接口类的关系

3 工厂机制

3.1 概述:工厂机制是UVM的魅力所在,也是一种典型的设计模式(design pattern)

3.2 工厂的意义

3.2.1 替换、注册、配置

  • 工厂为了:替换实例或注册过的类,为配置和覆盖(override)带来灵活性

  • 覆盖:替换实例或类型。被替换的实例或类型应满足注册(registration)和多态(polymorphism)的要求

  • UVM验证环境两部分:

    • 一部分构成了环境层次:通过uvm_component完成
    • 一部分构成了环境属性(如配置):通过uvm_object完成
    • uvm_component与uvm_object对比

    3.2.2 注册

依赖于工厂创建对象

创建对象之前工厂需要模具/蓝图(类进行注册):SV的new与注册的区别在哪,因为有一个覆盖的好处

3.3 uvm_component与uvm_object

工厂我们一共就注册两大类型:uvm_component与uvm_object

  • 验证不动产

    • 性质:构成验证环境的
    • 在uvm_component都有对应
  • 非不动产

    • 性质:帮助实现验证场景,在验证过程中动态产生
    • transcation事务级传输对象数据包,这些类在UVM统一由uvm_object表示

3.3.2 uvm_{component, object}的例化

工厂提供的方式

1
2
3
4
//创建uvm_component对象
comp_type::type_id::create(string name, uvm_component parent);
//创建uvm_object对象
object_type::type_id::create(string name);

type_id是你注册到工厂里面的类型

1
2
3
comp_type::type_id::create(val1,val2);
//type_id是你注册到工厂里面的类型
//create调用方法

creat与new

使用new或者create都能完成例化,但在UVM中我们用create,他是工厂提供的创建实例方式

3.3.4 工程提供的便利——创建分两步:①注册(registration)、②创建(create)

注册两个宏

`uvm_component_utils(comp_name);

`uvm_object_utils(obj_name);

三个步骤

  • 定义:extends
  • 注册:宏
  • 构建:new

第一个例子(继承于component类)

泛式:形式不变,内容变

第一步:注册环节,第一个泛式

1
`uvm_component_utils(comp1);//使用宏。固定的泛式,把当前的comp1类型注册到工厂里

第二步:new函数,new函数参数是第二个泛式,参数不能添加新的参数也不能减少原有的参数

1
function new(string name="comp1", uvm_component parent=null);//parent是说例化当前实例的组件

各个phase函数第二周学

第二个例子(继承于uvm_object类)

泛式:形式不变,内容变

第一步:注册环节,第一个泛式

1
`uvm_object_utils(obj1);//使用宏。固定的泛式,把当前的obj1类型注册到工厂里

第二步:new函数,new函数参数是第二个泛式

1
function new(string name="obj1");//

各个phase函数第二周学

3.3.5 工厂提供的便利

3.4 工厂机制:工厂类

不一定会出现在你的代码里面,但帮你规划了你的类

3.4.1 uvm_coreservice_t类

  • uvm_factory成员类:唯一并全局,负责注册、覆盖、例化

  • report_server成员类:唯一并全局,消息统筹和报告

  • tr_database成员类:全局,用于记录transaction记录

  • get_root成员方法:返回UVM环境的结构顶层对象

uvm_coreservice_t,uvm_factory并不是uvm_component或uvm_object,没有例化在UVM环境结构中


目前学习的实例uvm_default_factory是已经存在的,不需要自己做例化

3.4.2 注册宏`uvm下划线{component, object}下划线utils


注册机制与create,以下自学:


3.4.3 注册后的对象创建(component或者object)

注意有无parent关系到是否可以看到UVM结构


创建后放置到类型库中

创建时,创建的类型如果没有被覆盖,则直接使用该类型的基础类型

3.5.6 componetn/object与工厂有关的方法(三类创建方法)

object/component组件(uvm_component)去创建对象的方法:

  • create()
  • create_component()
  • get()
  • get_type_name()
  • set_inst_override()
  • set_type_override()

一旦创建,会把当前类型的图纸,放入到类型库里面

如果没有覆盖类型,则用工厂去创建

工厂去创建也有很多方法:

  • 使用typeid::创建

  • create_component_by_name()

  • create_component_by_name()
  • create_object_by_name()
  • create_object_by_type()


我们只需要使用这些就可以了

4 覆盖方法

4.1 工厂提供的便利——覆盖(override)

4.1.1

从原来所属类型创建一个新的替换类型

  • 无需修改原始代码,保持了原始代码封装性
  • 新的替换类型必须与原有类型兼容

4.1.2 举例说明

你想要修改一个成型验证平台,你想修改driver

如果你修改driver很容易影响到别人,因此不能轻易修改代码

如果driver是VIP,内部代码不开放的


使用新的类型driver2替换driver

4.1.3 覆盖实现的要求

  • 原有类型与新类型都需要注册
  • 使用create()也就是工厂创建对象
  • 新的替换类型必须与原有类型兼容

  • 覆盖发生时,可以使用类型覆盖或实例覆盖

4.2 相关函数

4.2.1 set_type_override()//替换类型

  • 函数原型
1
static function void set_type_override(uvm_object_wrapper override_type, bit replace =1 );//一个静态函数
  • 函数形参
1
2
uvm_object_wrapper//注册过后的某一个类在工厂中注册时的句柄
new_type::get_type()//找到uvm_object_wrapper的方法
  • 调用方法
1
2
3
orig_type::type_id::set_type_override(new_type::get_type())
//orig_type:原始类型
//type_id:类型id

4.2.2 set_inst_override()//替换实例

  • 函数原型
1
static function void set_inst_override(uvm_object_wrapper override_type, string inst_path , uvm_component parent=null );//一个静态函数
  • 函数形参
1
2
inst_path//当前替换实例的路径
parent()//缺省时为绝对路径,有值传递是使用{parent.get_full_name(),'.',inst_path}为目标路径,实际代码中常常直接写入上面的inst_path
  • 调用方法
1
2
3
orig_type::type_id::set_inst_override(new_type::get_type(), "orig_inst_path")
//orig_type:原始类型
//type_id:类型id
  • 路径解释
1
2
3
string inst_path = "root.test.env.checker"//法一直接写
//法二通过句柄直接拿到路径,去掉双引号

句柄所代表的结构层级


4.3 如何使用覆盖相关的函数

不止一个类提供与覆盖有关的函数,然而名称与参数列表可能各不相同:

1
2
3
4
uvm_component::set_{type, inst}_override{_by_type}
uvm_component_registry::set_{type, inst}_override
uvm_object_registry::set{type, inst}_override
uvm_factory::set{type, inst}_override

但我们推荐的就是使用typeid来调用set_{type, inst}_override

1
orig_type::type_id

4.4 覆盖实例

定义例子comp1类

1
2
import uvm_pkg::*; //想要使用uvm类型必须import这个,questa已经自动编译了这个pkg,vcs则需要实现编译一下
`include "uvm_macros,svh"

定义例子2comp2类

后面将1使用comp2替代comp1


进行覆盖

  • c1通过new实例化的没有受到影响,没有经过工厂
  • c2通过工厂create实例化的,因此成为了替换后的类型

如果没有comp1::type_id::set_type_override(comp2::get_type());进行覆盖

则c1,c2指向的都是comp1类型对象

4.5 解释为什么comp2要继承comp1

例化时:

  1. 去找comp1的查找表

  1. 发现覆盖类型不为空,把comp1的类型替换为comp2的类型

能不能comp2与comp1没有继承关系:不能,你的override会有问题

因此,必须要有继承关系在


定义成员函数(除了new)能不能不用virtual:不能

例化时comp2仍然是父类comp1的句柄,调用方法则会调用父类的方法

4.6 确保正确覆盖的代码要求

4.7 parent wins

  • 覆盖发生在例化之前,即
1
2
comp1::type_id::set_type_override(comp2::get_type());
c2 = comp1::type_id::create("c2",null);
  • 多次对同一对象override:谁的层次高听谁的(顶层test起作用了),即在配置过程中,谁层次高听谁的

4.8 总结:factory三要素:注册、创建和覆盖