第三章 UVM基础

uvm_component与uvm_object

uvm_component 派生自uvm_object

常见的派生自uvm_object 的类

基本上除了uvm_component 之外,其他的类本质上都是uvm_object

常见的派生自uvm_component 的类

之前提过了,uvm_driver,monitor(理论上要从uvm_monitor派生,但是直接从component派生也是可以的)

uvm_sequencer sequencer的功能就是组织管理sequence。

uvm_scoreboard 比较reference model和monitor分别发送来的数据,判断DUT是否正确工作。

reference model 模仿DUT。

uvm_agent 就是把driver和monitor封装在一起。

uvm_test 测试用例都要实例化env

与uvm_object相关的宏

uvm_object_utils:它用于把一个直接或间接派生自uvm_object的类注册到factory中。

uvm_object_param_utils:它用于把一个直接或间接派生自uvm_object的参数化的类注册到factory中。所谓参数化的类,是指类似于如下的类:

class A#(int WIDTH=32) extends uvm_object;

uvm_object_utils_begin :当需要使用field_automation机制时,需要使用此宏。

uvm_object_param_utils_begin :参数化的类。

uvm_object_utils_end :与begin成对出现,作为factory注册结束的标志。

与uvm_component相关的宏

uvm_component_utils :它用于把一个直接或间接派生自uvm_component的类注册到factory中。

uvm_component_param_utils :它用于把一个直接或间接派生自uvm_component的参数化的类注册到factory中。

uvm_component_utils_begin :它用于同时需要使用factory机制和field_automation机制注册的类。

uvm_component_param_utils_begin :参数化的,并且其中某些成员变量要使用field_automation 机制。

uvm_component_utils_end :与uvm_component_*_begin成对出现,作为factory注册结束的标志。

uvm_component的限制

uvm_component是从uvm_object派生来的。从理论上来说,uvm_component应该具有uvm_object的所有的行为特征。但是,由于uvm_component是作为UVM树的结点存在的,这一特性使得它失去了uvm_object的某些特征。

在uvm_object中有clone函数,它用于分配一块内存空间,并把另一个实例复制到这块新的内存空间中。clone函数的使用方式如下:

上述的clone函数无法用于uvm_component中,因为一旦使用后,新clone出来的类,其parent参数无法指定。

但是可以使用new+copy。

uvm_component的另外一个限制是,位于同一个父结点下的不同的component,在实例化时不能使用相同的名字。如下的方式中都使用名字“a1”是会出错的:

class A extends uvm_component;
…
endclass
class my_env extends uvm_env;
virtual function void build_phase(uvm_phase phase);
A a1;
A a2;
a1 = new("a1", this);
a2 = new("a1", this);
endfunction
endclass

uvm_component与uvm_object的二元结构

简单来说,即要有高级生命体,也要有分子这样的结构存在。

UVM的树形结构

uvm_component中的parent参数

在其他类别中实例化时,需要有一个m_children数组记录这个parent的孩子都是谁。

UVM树的根

UVM中真正的树根是一个称为uvm_top的东西。

Untitled

uvm_top是一个全局变量,它是uvm_root的一个实例(而且也是唯一的一个实例 [1],它的实现方式非常巧妙),而uvm_root 派生自uvm_component,所以uvm_top本质上是一个uvm_component,它是树的根。uvm_test_top的parent是uvm_top,而uvm_top的 parent则是null。UVM为什么不以uvm_test派生出来的测试用例(即uvm_test_top)作为树根,而是搞了这么一个奇怪的东西作为树根呢?

如果一个component在实例化时,其parent被设置为null,那么这个component的parent将会被系统设置为系统中唯一的uvm_root的实例uvm_top,如图3-3所示。

Untitled

可见,uvm_root的存在可以保证整个验证平台中只有一棵树,所有结点都是uvm_top的子结点。

层次结构相关函数

UVM提供了一系列的接口函数用于访问UVM树中的结点。这其中最主要的是以下几个:

get_parent函数,用于得到当前实例的parent,其函数原型为:

来源:UVM
源代码
extern virtual function uvm_component get_parent ();

相对的就是get_child:

来源:UVM
源代码
extern function uvm_component get_child (string name);

与get_parent不同的是,get_child需要一个string类型的参数name,表示此child实例在实例化时指定的名字。因为一个component只有一个parent,所以get_parent不需要指定参数;而可能有多个child,所以必须指定name参数。

如果要得到所有的child,可以使用get_children

来源:UVM
源代码
extern function void get_children(ref uvm_component children[$]);
uvm_component array[$];
my_comp.get_children(array);
foreach(array[i])
    do_something(array[i]);

除了一次性得到所有的child外,还可以使用get_first_child和get_next_child的组合依次得到所有的child:

string name;
uvm_component child;
if (comp.get_first_child(name))
    do begin
        child = comp.get_child(name);
        child.print();
    end while (comp.get_next_child(name));

field automation机制

field automation机制相关的宏

来源:UVM
源代码
`define uvm_field_int(ARG,FLAG)
`define uvm_field_real(ARG,FLAG)
`define uvm_field_enum(T,ARG,FLAG)
`define uvm_field_object(ARG,FLAG)
`define uvm_field_event(ARG,FLAG)
`define uvm_field_string(ARG,FLAG)

上述几个宏分别用于要注册的字段是整数、实数、枚举类型、直接或间接派生自uvm_object的类型、事件及字符串类型。这里除了枚举类型外,都是两个参数。对于枚举类型来说,需要有三个参数。假如有枚举类型tb_bool_e,同时有变量tb_flag,那么在使用field automation机制时应该使用如下方式实现:

typedef enum {TB_TRUE, TB_FALSE} tb_bool_e;
…
tb_bool_e tb_flag;
…
`uvm_field_enum(tb_bool_e, tb_flag, UVM_ALL_ON)

与动态数组有关的uvm_field系列宏有:

来源:UVM
源代码
`define uvm_field_array_enum(ARG,FLAG)
`define uvm_field_array_int(ARG,FLAG)
`define uvm_field_array_object(ARG,FLAG)
`define uvm_field_array_string(ARG,FLAG)

与静态数组相关的uvm_field系列宏有:

来源:UVM
源代码
`define uvm_field_sarray_int(ARG,FLAG)
`define uvm_field_sarray_enum(ARG,FLAG)
`define uvm_field_sarray_object(ARG,FLAG)
`define uvm_field_sarray_string(ARG,FLAG)

与队列相关的uvm_field系列宏有:

来源:UVM
源代码
`define uvm_field_queue_enum(ARG,FLAG)
`define uvm_field_queue_int(ARG,FLAG)
`define uvm_field_queue_object(ARG,FLAG)
`define uvm_field_queue_string(ARG,FLAG)

联合数组是SystemVerilog中定义的一种非常有用的数据类型,在验证平台中经常使用。UVM对其提供了良好的支持,与联合数组相关的uvm_field宏有:

来源:UVM
源代码
`define uvm_field_aa_int_string(ARG, FLAG)
`define uvm_field_aa_string_string(ARG, FLAG)
`define uvm_field_aa_object_string(ARG, FLAG)
`define uvm_field_aa_int_int(ARG, FLAG)
`define uvm_field_aa_int_int_unsigned(ARG, FLAG)
`define uvm_field_aa_int_integer(ARG, FLAG)
`define uvm_field_aa_int_integer_unsigned(ARG, FLAG)
`define uvm_field_aa_int_byte(ARG, FLAG)
`define uvm_field_aa_int_byte_unsigned(ARG, FLAG)
`define uvm_field_aa_int_shortint(ARG, FLAG)
`define uvm_field_aa_int_shortint_unsigned(ARG, FLAG)
`define uvm_field_aa_int_longint(ARG, FLAG)
`define uvm_field_aa_int_longint_unsigned(ARG, FLAG)
`define uvm_field_aa_string_int(ARG, FLAG)
`define uvm_field_aa_object_int(ARG, FLAG)

field automation机制的常用函数

copy

来源:UVM
源代码
extern function void copy (uvm_object rhs);

如果要把某个A实例复制到B实例中,那么应该使用B.copy(A)。在使用此函数前,B实例必须已经使用new函数分配好了内存空间。

compare :比较两个实例是否一样。

来源:UVM
源代码
extern function bit compare (uvm_object rhs, uvm_comparer comparer=null);

如果要比较A与B是否一样,可以使用A.compare(B),也可以使用B.compare(A)。当两者一致时,返回1;否则为0。

pack_bytes函数用于将所有的字段打包成byte流,其原型为:

来源:UVM
源代码
extern function int pack_bytes (ref byte unsigned bytestream[],
input uvm_packer packer=null);

unpack_bytes函数用于将一个byte流逐一恢复到某个类的实例中,其原型为:

来源:UVM
源代码
extern function int unpack_bytes (ref byte unsigned bytestream[],
input uvm_packer packer=null);

pack函数用于将所有的字段打包成bit流,其原型为:

来源:UVM
源代码
extern function int pack (ref bit bitstream[],
input uvm_packer packer=null);

unpack

来源:UVM
源代码
extern function int unpack (ref bit bitstream[],
input uvm_packer packer=null);

还有pack_ints基本一样,不赘述了。

print函数用于打印所有的字段。

clone函数

除了上述函数之外,field automation机制还提供自动得到使用config_db::set设置的参数的功能,这点请参照3.5.3节。

field automation机制中标志位的使用

CRC错误的激励。

field automation中宏与if的结合

在以太网中,有一种帧是VLAN帧,这种帧是在普通以太网帧基础上扩展而来的。而且并不是所有的以太网帧都是VLAN帧,如果一个帧是VLAN帧,那么其中就会有vlan_id等字段(具体可以详见以太网的相关协议),否则不会有这些字段。类似vlan_id等字段是属于帧结构的一部分,但是这个字段可能有,也可能没有。由于读者已经习惯了使用uvm_field系列宏来进行pack和unpack操作,那么很直观的想法是使用动态数组的形式来实现:

UVM中打印信息的控制

设置打印信息的冗余度阈值

LOW MEDIUM HIGH之前说过了,默认的冗余度阈值是UVM_MEDIUM。

重载打印信息的严重性

重载是深入到UVM骨子里的一个特性。UVM默认有四种信息严重性:UVM_INFO、UVM_WARNING、UVM_ERROR、UVM_FATAL。这四种严重性可以互相重载。如果要把driver中所有的UVM_WARNING显示为UVM_ERROR,可以使用如下的函数:

文件:src/ch3/section3.4/3.4.2/base_test.sv
16 virtual function void connect_phase(uvm_phase phase);
17 env.i_agt.drv.set_report_severity_override(UVM_WARNING, UVM_ERROR);
18 //env.i_agt.drv.set_report_severity_id_override(UVM_WARNING, "my_driver", UVM_ERROR);
19 endfunction

UVM_ERROR到达一定数量结束仿真

实现这个功能的是set_report_max_quit_count函数,其调用方式为:

文件:src/ch3/section3.4/3.4.3/base_test.sv
21 function void base_test::build_phase(uvm_phase phase);
22 super.build_phase(phase);
23 env = my_env::type_id::create("env", this);
24 set_report_max_quit_count(5);
25 endfunction

设置计数的目标

set_report_severity_action函数来把UVM_WARNING加入计数目标:

文件:src/ch3/section3.4/3.4.4/base_test.sv
16 virtual function void connect_phase(uvm_phase phase);
17 set_report_max_quit_count(5);
18 env.i_agt.drv.set_report_severity_action(UVM_WARNING, UVM_DISPLAY|UVM_COUNT);
…
24 endfunction

UVM断点功能

只要将其中的UVM_COUNT替换为UVM_STOP,就可以实现相应的断点功能,这里不多做介绍。

config_db机制

跨层次的多重设置

在前面的所有例子中,都是设置一次,获取一次。但是假如设置多次,而只获取一次,最终会得到哪个值呢?

在现实生活中,这可以理解成有好多人都给李四发了一封信,要求李四做某件事情,但是这些信是相互矛盾的。那么李四有两种方法来决定听谁的:一是以收到的时间为准,最近收到的信具有最高的权威,当同时收到两封信时,则看发信人的权威性,也即时间的优先级最高,发信人的优先级次之;二是先看发信人,哪个发信人最权威就听谁的,当同一个发信人先后发了两封信时,那么最近收到的一封权威高,也就是发信人的优先级最高,而时间的优先级低。UVM中采用类似第二种方法的机制。

set第一个参数为this的时候,越靠近根节点,优先级越高。如果不是this,则会看先后顺序。因为build_phase自上而下执行,所以越靠近叶子的地方,会成为最后的设置。

同一层次的多重设置

当跨层次来看待问题时,是高层次的set设置优先;当处于同一层次时,上节已经提过,是时间优先。

设置多个case,在base_test进行默认设置,然后对特殊情况幅值。

非直线的设置与获取

在图3-4所示的UVM树中,driver的路径为uvm_test_top.env.i_agt.drv。在uvm_test_top,env或者i_agt中,对driver中的某些变量通过config_db机制进行设置,称为直线的设置。但是若在其他component,如scoreboard中,对driver的某些变量使用config_db机制进行设置,则称为非直线的设置。

config_db机制对通配符的支持(不就是正则匹配嘛)

在以前所有的例子中,在config_db::set操作时,其第二个参数都提供了完整的路径,但实际上也可以不提供完整的路径。config_db机制提供对通配符的支持。

但是不建议使用,会使得去欸的那个的路径变得模糊。

check_config_usage

因为config_db能对不同层次的参数实现配置,但是如果第二个参数的字符串输错,仿真器不会给出任何报错提示。

针对这种情况,UVM提供一个函数check_config_usage,可以显示截止到此函数调用时,哪些参数是设置过但是没有被获取过。

set_config与get_config(过时了?)

获取参数值,config_db比set/get_config更强的地方在于,能设置的参数类型不局限于string,int和object,还包括常见的枚举类型、virtual interface、bit类型、队列等都可以成为config_db设置的数据类型。

Last modification:February 24, 2022
恰饭环节