UVM入门和进阶2:核心基类_阶段_配置_消息机制

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

1 核心基类(uvm_object)

1.1 核心基类指的就是uvm_object

  • uvm_void只是一个虚类(virtual class),等待将来继承于他的子类去开垦
  • 有两类uvm_objectuvm_port_base类继承于uvm_void
  • uvm世界里面十大类八大类继承于uvm_object
  • 除了uvm_port_base``类以外uvm里面的类都可以溯源到uvm_object类

1.2 uvm_object

uvm_object核心方法:不用自己实现,uvm自动去实现

1
2
3
4
5
copy;
clone;
compare;
print
pack/unpack

uvm里面把sv的句柄拷贝和对象拷贝区分成了copy与clone,且同样需要src,dst object

1.3 主要机制:域的自动化(field automation)

域:UVM里任何的成员变量都可以称之为域(field),在sv里面称之为属性property

域的自动化:使得在注册时,声明之后可能会用到的对象拷贝、克隆、打印等操作的成员变量

1.3.2 代码

代码原文

上文代码解释

第一点:开始域自动化声明和结束声明

1
2
`uvm_object_utils_begin(box)
`uvm_object_utils_end(box)

第二点:域的自动化声明方式:如下添加参与到自动化声明的变量(也是使用宏,提醒一下,宏是不用分号的

宏函数参考表:P275页 表10.2

1
2
3
`uvm_field_int(volume, UVM_ALL_ON)
`uvm_field_enum(color_t, color, UVM_ALL_ON)
`uvm_field_string(name, UVM_ALL_ON)

补充点:域自动化数据操作宏参数UVM_ALL_ON:是与之前数据变量对应的,数据操作的一个宏

数据操作宏参考表P276页 表10.3

对于小白来讲默认采取UVM_ALL_ONUVM_ALL_DEFAULT就可以将所有数据操作(copy,compare,print,record,pack)都打开

1.3.3 代码续


调用未定义的copy函数

1
b2.copy(b1);

b1的成员变量都拷贝到b2

SV的copy是自己实现的,uvm不需要

1.3.3 总结

1.4 核心方法之一:拷贝(copy) 默认deepcopy=copy()+do_copy()

  • SV里面的copy先做对象的创建再做数据的copy —-> UVM里面的clone
  • SV里面的数据copy拷贝 —> UVM里面的copy拷贝即对象已经创建好就进行数据的拷贝
  • 默认执行deepcopy即先执行copy()再调用回调函数do_copy(),do_copy()可在类里定义
1
function void do_copy(uvm_object obj);

相同点都会对数据进行复制

graph LR
SV里面的copy先做对象的创建再做数据的copy ---> UVM里面的clone
SV里面的数据copy拷贝 --> UVM里面的copy拷贝即对象已经创建好就进行数据的拷贝

1.4.2 代码

定义ball类,在box类中对ball进行例化


  • box类中的成员变量完全拷贝过去了
  • 内部成员ball类b的成员变量没有完全拷贝
    • 原因:
      • 对于color_t 声明时使用UVM_NOCOPY不执行拷贝
      • 对于diameter从10变成20,因为uvm的拷贝执行了do_copy

1.5 核心方法之一:比较(compare)

1
function bit compare (uvm_object rhs, uvm_comparer comparer=null);

1.5.2 例子

  • 第三点当比较器为空uvm_comparer=null则会使用全局的默认比较器:uvm_default_comparer(详细参考1.6),最大的错误次数为1,只要失败一次就返回

1.7 打印:print()直接打印,spring()返回字符串,do_print()回调

1.7.1

1.7.2 例子

声明了一个uvm_table_printer句柄local_printer
并使用 ;p’p’p’p’p’p’p’p’p’p’p’p’p’p’p’p’p’p’p

  • 第一次print,没对打印机printer指定,uvm使用默认uvm_default_printer打印
  • 第二次print,指定uvm_default_printer=uvm_default_line_printer,uvm使用uvm_default_line_printer打印
  • 第三次print,指定uvm_default_printer=uvm_default_tree_printer,uvm使用uvm_default_tree_printer打印
  • 第四次print,配置local_printerlocal_printer.knobs.full_name=1,并指定使用前面使用的local_printer

输出结果

1.8 打包和解包(pack&unpack)

1.8.1

一般在纯粹的UVM环境里是用不到的,一般用不到

把域(标量)打包成一个比特流动态数组

1
2
function int pack (ref bit bitstream[], input uvm_packer packer=null);
function int unpack (ref bit bitstream[], input uvm_packer packer=null);

如下:volume-32位、color-32位、box-24位 —->最终打包成32+32+24=88位比特流的数组

1.8.2 为什么这么做

仿真器与FPGA(或其他模型)连接

sim UVM仿真器与FPGA(或其他模型)连接通信,到具体的物理接口上,数据以bitstream形式,通过PCIE/USB/JTAG/UART等协议传输

UVM的pack&unpack是simUVM(软件侧)对数据打包的方法

对应FPGA或SystemC模型或不同语言也是通过相关硬件模块实现

1.6 全局对象

uvm_top为全局的顶层,与其他全局对象处于并列位置

如果不想用默认比较器uvm_default_comparer可以创建一个uvm_comparer对象

(补充:uvm_factory不要去修改)

1.9 使用到的方法总结

1
2
class.sprint
`uvm_info

2 phase机制

2.1 概述

解决的问题:

  • 例化先后关系
  • 例化之前进行配置
  • UVM仿真阶段层次化

2.2 执行机制

2.2.1

一共有九个phase,对应九个方法,只有uvm_component具备,即如果你的类型是个组件的话一定会有phase机制

主要用到build, connect, run, report

  • 执行顺序自顶向下,从build到final
  • 只有run是任务,其他的是函数,函数意味着要立即返回

2.2.2 uvm_phase形参、例子、工厂创建对象的实例

uvm_phase形参用于传入当前phase


执行方法名:

1
function void T_phase(uvm_phase phase);

uvm_phase phase指的传入当前phase


工厂机制创建:

1
handle = subcomp::type_id::create("c1",this);

this指的是c1或c2的parent,这里是创建这些对象的外部类,对应的topcomp


该例子例化的类结构图

2.2.2.2 执行结果

从结果看,执行顺序:自顶向下(先创建顶层实例,在创建底层实例)

2.3 九个主要phase:run_phase的功能

2.3.1

  • 上电
  • 复位
  • 寄存器配置
  • 发送主要测试内容
  • 等待DUT完成测试

2.3.2 进一步细分run_phase为12个分支phase

  • pre_reset_phase
  • reset_phase
  • post_reset_phase
  • pre_configure_phase
  • post_comfigure_phase
  • pre_main_phase
  • main_phase
  • post_main_phase
  • pre_shutdown_phase
  • shutdown_phase
  • post_shutdown_phase

2.3.3 run_phase与12分支phase关系

建议不要使用12个分支phase

当环境中同时存在run_phase与分支phase时:

run_phase和定义的分支会同时运行为两个线程,且两个线程都结束时才进入下一个phase

2.4 UVM编译和运行顺序

run是task所以可以消耗时间

2.5 UVM仿真开始:run_test()

2.5.1

类比SV中仿真开始

  • 方法一:run_test(“xxxx-test”)

  • 方法二:run_test()参数为空,仿真时传递参数+UVM_TESTNAME=<test_name>

2.5.2 UVM世界的诞生:run_test()

run_test()从uvm_root创建了一个UVM世界,是唯一的顶层

run_test(string test_name=””);源码如下:

  • 先拿到一个coreservice_t

  • 再用拿到的coreservice_t拿到全局顶层top

  • 再调用顶层的run_test(test_name)

即run_test(str)调用顶层,顶层top创建test,test再创建下面的哥哥结构

2.5.3 uvm_top承担的责任

uvm_top承担的责任非常重要,但无需我们去实现

  • uvm_top是uvm_root类的全局唯一一个实例
  • uvm_top是uvm里面例化的任何一个实例的顶层
    • 对于component来讲,parent定为null则默认顶层为uvm_top,称为其子组件
  • uvm_top控制各个phase的执行顺序
  • uvm_top完成使用层次名索引的功能;配置部分都在uvm_top里面

2.5.4 uvm_top调用run_test,uvm_top做了什么

  • objection机制是用来控制仿真退出的
  • 创建uvm_test_top是为了调用各个组件的phase方法,进行安排

2.6 UVM仿真结束:我们的组件需要至少有一个能够挂起run_phase

2.6.1

控制(实现阻碍仿真退出)仿真退出只有一种方式,就是objection机制

uvm_objection机制实际上就是个计数器,在SV中是semapho旗语

2.6.2 uvm_objection用来实现反停止的方法

2.6.1raise挂起、drop落下

我们一般在run的阶段会把仿真挂起,对各个组件来说至少有一个需要把objection挂起防止uvm退出,如下:

如果uvm在run_phase时没有任何组件挂起,则会直接跳到report阶段

2.6.2.2 例子代码

1
phase.raise_objection(this);//this指的是把当前组件挂起phase.drop_objection(this);//this指的是把当前组件落下

2.6.2.3 例子代码2:你需要把你的挂起代码放在run_phase()的第一行执行

错误的,直接全部跳过了

你需要把你的挂起代码放在run_phase()的第一行执行

2.6.4 你可以在component、test、sequence中进行挂起操作

3 config机制

3.1 概述

3.1.1

config不同参数来选择:组件类型、组件实例数目、组件之间的连接、组件的运行模式;或是修改更细节的内容

3.1.2 uvm_config_db配置类

可以把一个变量从任何一个层次传递到其他层次,甚至底层组件没有创建也可以传递

可传递内容:

  • interface指针(SV中set_interface函数传递的)
  • 单一变量或句柄的配置

传递形式(成功返回1,失败返回0):

1
2
uvm_config_db#(T)::set(uvm_component cntxt, string inst_name, string field_name, T value);
uvm_config_db#(T)::get(uvm_component cntxt, string inst_name, string field_name, inout T value);
  • 号对应参数类内容(参考SV第七讲)

  • 由层次索引

    • string inst_name没有例化则就直接写空字符串“”
  • (后面是错误的笔记)先set再创建,就会生成相应设置后的类

3.3 interface传递

3.3.1

SV中使用层次化的interface所以完成传递,即调用每一层的set_interface进行层层传递,非常不好

  • 一定要对interface与vitural interface区分开,传递的过程中应该是virtual interface即传递指针

3.3.2 例子代码

下面例子

  • 在build_phase里get vif

  • 在test1的initial中进行set,且set一定要发生在run_test()之前确保了可以成功传递

注意virtual interface,在软件中都得使用virtual interface


get(this,””,”vif”,vif)前三个参数完成的层次为“root.test.c1.vif”。其中第二个参数代表的时实例名,由于这里还没有进行例化所以用空字符串代替

3.3.3 例子代码续


uvm_config_db里面就是一些关系数组,做了中间的变量存放,通过set,get改变值:

3.4 变量设置

3.4.1 , 3.4.2 例子代码

下面例子

  • 在build_phase里get vif

  • 在test1的initial中进行set,再进行create

3.4.3 例子代码续

3.5 object传递

3.5.1

当变量比较多的时候,一个一个写不方便

可以把这些变量封装到uvm_object类里面,类例化,传递该对象句柄

  • 先set再create

3.5.2 例子代码

  • 可以父类uvm_oject传递:此时注意get时父类句柄转换为子类
  • 可以直接以继承过来的子类进行传递
  • set , get 严格类型一致

注意代码void`($cast(cfg, tmp))把父类句柄转成子类句柄,从而访问子类句柄里的成员变量

3.5.2 例子代码续

类型要完全一致:底层get是父类句柄(uvm_objection)顶层set也得是该父类句柄

3.6 总结与建议

3.6.1 总结

  • set,get成对出现

  • 类型严格一致

  • 路径中的*星号表通配符,意思是任意路径

3.6.1 建议

4 消息管理

4.1 概述

一个好的验证系统应具有消息管理特性:

  • 一种标准化的方式打印
  • 过滤信息(按重要级别)
  • 打印通道

这些特性UVM都有支持,UVM提供一系列丰富的类和方法来生成和过滤消息:

  • 消息方法
  • 消息处理
  • 消息机制

类似的SV中的report package

4.2 消息方法

4.2.1

在UVM环境中或者环境外,只要引入uvm_pkg,均可以通过下面的方法来按照消息的严重级别和冗余度来打印消息

1
2
3
4
function void uvm_report_info(string id, string message, int verbosity = UVM_MEDIUM, string filename = "", int line = 0);
function void uvm_report_warning(string id, string message, int verbosity = UVM_MEDIUM, string filename = "", int line = 0);
function void uvm_report_error(string id, string message, int verbosity = UVM_LOW, string filename = "", int line = 0);
function void uvm_report_fatal(string id, string message, int verbosity = UVM_NONE, string filename = "", int line = 0);
  • id:消息的名称是什么

  • message:消息的内容是什么

  • verbosity:消息的重要性/冗余程度

  • filename:消息发生时该程序文件的名称(留空就行,一般系统自动添加)

  • line:消息发生时该程序的行号(留空就行,一般系统自动添加)

4.2.2 verbosity冗余度:UVM_NONE

冗余度:代表消息重要不重要,与我们的直观认识相反

  • UVM_NONE:最重要,没有任何bur/filter可以过滤
  • UVM_LOW
  • UVM_MEDIUM
  • UVM_HIGH
  • UVM_FULL:更容易被过滤
  • UVM_DEBUG:更容易被过滤

4.3 消息处理

4.4.1 处理方式

4.4.2 消息宏

谁提供的这些消息?:uvm_report_object

  • 我们可以用组件的方法进行调用
  • 使用宏进行调用(更推荐)
1
2
3
4
`uvm_info(ID, MESSAGE, VERBOSITY)
`uvm_warning(ID, MESSAGE)
`uvm_error(ID, MESSAGE)
`uvm_fatal(ID, MESSAGE)

对于w,e,f不用传verbosity,他们默认是UVM_LOW,较高等级了

4.4 消息机制

消息处理是uvm_report_handler类完成的

  • set_max_quit_count对ERROR消息计数停止上限

4.5 消息处理中的回调函数

回调函数有如下固定形式,在任何地方实现都可以

一般来说一定要定义report_hook,其他看细分要求

4.5.2 例子代码

  • build_phase
    • set_report_severity_action(UVM_ERROR, UVM_DISPLAY | UVM_CALL_HOOK);//只有error会打印调用回调函数
    • set_report_verbosity_level(UVM_LOW);//只有冗余为uvm_low会被打印1
      • 此处error2为HIGH则不会打印也不会调用回调函数

4.5.3 回调函数返回值:

report_hook返回值为1时,继续执行分类report_error_hook否则不再继续调用

一般来说一定要定义report_hook

4.5.3 例子代码续

4.5.4 隐藏在后台的uvm_report_handler与uvm_report_server

对每一个组件都有report_handler实例,它们把消息申请到全局的实例report_server

report_server和factory一样的,放在coserver下面