UVM验证—第二课(一):核心基类阶段机制
目录
1. 核心基类
1.1 uvm_object
1.2 域的自动化(filed automation)
1.3 copy和clone
1.4 compare
1.5 print
1.6 pack&unpack
2. phase机制
2.1 概述
2.2 运行阶段机制
2.3 执行顺序
2.3.1 不同 phase 在同一个 component 中的执行顺序
2.3.2 相同 phase 的在 uvm 树形结构的执行顺序
2.3.3 相同 phase 在 uvm 树形结构中的同级组件的执行顺序
2.4 objection 机制
1. 核心基类
1.1 uvm_object
- UVM世界中的类最初都是从一个uvm void根类(root class)继承来的,而实际上这个类并没有成员变量和方法。
- uvm_void只是一个虚类(virtual class),还在等待将来继承于它的子类去开垦。在继承于uvm_void的子类中,有两个类,一个为uvm_object类,另外一个为uvm_port_base类。
- 在UVM世界的类库地图中除过事务接口(transaction interface)类继承于uvm_port base,其它所有的类都是从uvm_object类一步步继承而来的。
- 从uvm_object提供的方法和相关的宏操作来看,它的核心方法主要提供与数据操作的相关服务:
- Copy
- Clone
- Compare
- Pack/Unpack
- 在SV模块的学习中,我们懂得了什么是句柄拷贝和对象拷贝。因此,无论是copy或者clone,都需要确保在操作的过程中,需要有source object和target object。
1.2 域的自动化(filed automation)
- UVM通过域的自动化,使得用户在注册UVM类的同时也可以声名今后会参与到对象复制、克隆、打印等操作的成员变量。
- uvm_field_int(变量名,FLAG) 除了int,还有real,enum(enum名称,实例名,FLAG),object,event,string
动态数组 uvm_field_array_enum,int,object,string
静态数组 uvm_field_sarray_enum,int,object,string
队列 uvm_field_queue_enum,int,object,string
联合数组 uvm_field_aa_int_string(ARG,FLAG) aa_object_string,aa_object_int等 aa后面第一个是存储数据类型,第二个是索引类型 UVM_DEFAULT和UVM_ALL_ON表示将所有的数据操作方式都打开。UVM推荐使用UVM_DEFAULT将全部数据操作方法打开
class box extends uvm_object;int volume = 120;color_t color = WHITE;string name = "box";`uvm_object_utils_begin(box)`uvm_field_int(volume, UVM_ALL_ON)`uvm_field_enum(color_t, color, UVM_ALL_ON)`uvm_field_string(name, UVM_ALL_ON)`uvm_object_utils_endfunction new(string name="box");super.new(name);this.name = name;endfunctionendclass
1.3 copy和clone
copy
和clone
的基本区别copy
方法:默认使用已经创建好的对象,仅对数据进行拷贝。也就是说,它利用现有的对象实例,将源对象的数据复制到这个已有对象中。clone
方法:会自动创建新对象,并对source object
(源对象)进行数据拷贝,然后返回target object
(目标对象)句柄。即它会先新建一个对象,再把源对象的数据复制到新对象中,最后返回新对象的引用。
- 数据复制的共性
无论是copy
还是clone
,都需要对数据进行复制操作。这是它们的核心功能,确保目标对象能够拥有与源对象相同或经过特定处理的数据。 - 包含句柄的数据成员拷贝问题
当数据成员包含句柄时,在拷贝过程中存在疑问:是只拷贝该成员句柄本身,还是额外创建新的对象,并拷贝该句柄指向的对象。这是因为句柄本质上是指向某个对象的引用,直接拷贝句柄可能导致多个对象共享同一个目标对象,而创建新对象并拷贝指向对象的内容则可以避免这种情况,实现数据的独立性和隔离性。 copy
的深拷贝示例
从示例可以看出,在进行copy
时,默认进行的是深拷贝(deep copy),即会执行copy()
和do_copy()
方法。深拷贝会递归地复制对象及其所引用的所有对象,确保目标对象与源对象在内存中是完全独立的,修改其中一个不会影响另一个。
copy代码
module object_copy2; // 定义一个模块名为 object_copy2,作为测试平台使用import uvm_pkg::*; // 导入 UVM 标准包中的所有内容,以便在模块中使用 UVM 类和方法`include "uvm_macros.svh" // 包含 UVM 的宏定义文件,用于支持如 `uvm_object_utils 等宏typedef enum {RED, WHITE, BLACK} color_t; // 定义枚举类型 color_t,包含三种颜色:RED、WHITE、BLACK// 定义 ball 类,继承自 uvm_object,表示一个球类对象class ball extends uvm_object;int diameter = 10; // 直径,默认值为 10color_t color = RED; // 颜色,默认为 RED// 使用 UVM 工厂机制注册 ball 类,并开始声明字段`uvm_object_utils_begin(ball)`uvm_field_int(diameter, UVM_DEFAULT) // 注册 diameter 字段,采用默认处理方式(复制、比较等)`uvm_field_enum(color_t, color, UVM_NOCOPY) // 注册 color 字段,但禁止复制操作`uvm_object_utils_end// 构造函数,创建 ball 实例时调用function new(string name="ball");super.new(name); // 调用父类 uvm_object 的构造函数endfunction// 自定义 do_copy 方法,用于控制对象拷贝行为virtual function void do_copy(uvm_object rhs);ball b;$cast(b, rhs); // 将 rhs 转换为 ball 类型$display("ball::do_copy entered.."); // 打印进入 do_copy 的提示信息if(b.diameter <= 20) begindiameter = 20; // 如果源对象直径小于等于 20,则目标对象直径设置为 20endendfunctionendclass// 定义 box 类,继承自 uvm_object,表示一个盒子类对象class box extends uvm_object;int volume = 120; // 体积,默认值为 120color_t color = WHITE; // 颜色,默认为 WHITEstring name = "box"; // 名字,默认为 "box"ball b; // 包含一个 ball 对象的实例// 使用 UVM 工厂机制注册 box 类,并开始声明字段`uvm_object_utils_begin(box)`uvm_field_int(volume, UVM_ALL_ON) // 注册 volume 字段,启用所有操作(复制、比较、打印等)`uvm_field_enum(color_t, color, UVM_ALL_ON) // 注册 color 字段,启用所有操作`uvm_field_string(name, UVM_ALL_ON) // 注册 name 字段,启用所有操作`uvm_field_object(b, UVM_SHALLOW) // 注册 ball 对象 b,使用浅拷贝策略`uvm_object_utils_end// 构造函数,创建 box 实例时调用function new(string name="box");super.new(name); // 调用父类 uvm_object 的构造函数this.name = name; // 设置当前对象的名字b = new(); // 创建 ball 子对象实例endfunctionendclass// 声明两个 box 类的实例 b1 和 b2box b1, b2;initial beginb1 = new("box1"); // 创建 box1 实例b1.volume = 80; // 设置 box1 的体积为 80b1.color = BLACK; // 设置 box1 的颜色为 BLACKb1.b.color = WHITE; // 设置 box1 中 ball 的颜色为 WHITEb2 = new(); // 创建新的 box 实例 b2b2.copy(b1); // 使用 copy 方法将 b1 的内容复制到 b2b2.name = "box2"; // 修改 b2 的名字为 "box2"$display("%s", b1.sprint()); // 打印 b1 的内容(包括自身及其子对象)$display("%s", b2.sprint()); // 打印 b2 的内容(注意 b 是浅拷贝,指向同一个 ball 对象)endendmodule
输出结果:
ball::do_copy entered..
-----------------------------------
Name Type Size Value
-----------------------------------
box1 box - @336
volume integral 32 'h50
color color_t 32 BLACK
name string 4 box1
b ball - @337
diameter integral 32 'ha
color color_t 32 WHITE
-----------------------------------
-----------------------------------
Name Type Size Value
-----------------------------------
box box - @338
volume integral 32 'h50
color color_t 32 BLACK
name string 4 box2
b ball - @340
diameter integral 32 'h14
color color_t 32 RED
-----------------------------------
- 新添加了一个类ball,并且在box中例化了一个ball的对象。在拷贝过程中,box的其它成员都正常拷贝了,但对于box:b的拷贝则通过了ball的深拷贝方式进行。
- 即先执行自动拷贝copy(),来拷贝允许拷贝的域,由于ball::color不允许拷贝,所以只拷贝了ball::diameter。
- 接下来,再执行do_copy()函数,这个函数是需要用户定义的回调函数(callback function),即在copy(执行完后会执行do_copy()。
- 如果用户没有定义该函数,那么则不会执行额外的数据操作。从ball::do_copy(函数可以看到,如果被拷贝对象的diameter小于20,那么则将自身的diameter设置为20。因此,最后对象b2.b的成员与b1.b的成员数值不同。
1.4 compare
1.5 print
- uvm_default printer = uvm_default_line_printer; b1.print();
- uvm_default _printer = uvm_default_tree_printer ; b1.print() ;
- uvm_default_tree_printer:可以将对象按照树状结构打印。
- uvm_default_line_printer : 可以将对象数据打印到一行上面。
- uvm_default_table_printer : 可以将对象按照表格的方式打印。
- uvm_default_printer: UVM环境默认的打印设置,该句柄默认指向了uvm_default_table_printer。
1.6 pack&unpack
- tr.pack_bytes(data_q) 将所有字段打包成byte流,放入data_q中
- unpack_bytes 将一个byte流逐一恢复到某个类的实例中 按照`uvm_field_int注册时的顺序从上到下依次打包
- pack() unpack() 将所有字段打包成bit流
- pack_ints() unpack_ints() 将所有字段打包成int(4个byte,或者dword)流
2. phase机制
2.1 概述
- SV的验证环境构建中,我们可以发现,传统的硬件设计模型在仿真开始前,已经完成例化和连接了;而SV的软件部分对象例化则需要在仿真开始后执行。
- 虽然对象例化通过调用构建函数new()来实现,但是单单通过new()函数无法解决一个重要问题,那就是验证环境在实现层次化时,如何保证例化的先后关系,以及各个组件在例化后的连接。
- 此外如果需要实现高级功能,例如在顶层到底层的配置时,SV也无法在底层组件例化之前完成对底层的配置逻辑。
- 因此UVM在验证环境构建时,引入了phase机制,通过该机制我们可以很清晰地将UVM仿真阶段层次化。
- 这里的层次化,不单单是各个phase的先后执行顺序,而且处于同一phase中的层次化组件之间的phase也有先后关系。
2.2 运行阶段机制
- 构造阶段(Construction phase):在此阶段,验证环境的各个组件被创建并连接。包括创建和配置模型、生成各种信号和时钟,还有其他必要的前期准备工作。
- 配置阶段(configuration phase):在此阶段,各个组件的配置参数被设置,各种需要的连接关系建立起来。这些配置和连接可以通过 UVM 自带的配置机制进行设置。
- 主阶段(Main phase):在此阶段,主要进行测试的执行过程。此阶段使用各种事务 (transaction)进行通信,发送和接收交互信息,执行具体的测试功能,包括输入和输出数据的处理、引发事件等。
- 收尾阶段(Shutdown phase):在此阶段,进行一些清理工作,释放资源,存储统计信息和自检等。通常,这个阶段运行在主测试程序结束之后,以确保所有的资源被正确释放。
2.3 执行顺序
2.3.1 不同 phase 在同一个 component 中的执行顺序
2.3.2 相同 phase 的在 uvm 树形结构的执行顺序
- 自上而下执行的 phase 有:build_phase,final_phase
- 自 下 而 上 执 行 的 phase 有 : connect_phase , end_of_elaboration_phase , start_of_simulation_phase,reset phase,configuration phase,run_phase,main phase,shutdown phase ,extact_phase,check_phase,report_phase。
2.3.3 相同 phase 在 uvm 树形结构中的同级组件的执行顺序
- 如 driver 与 monitor,它们的执行顺序是按照字典序的,这里的字典序是依据 new 时指定的名字,例如有两个 agent,有一个 agent new 的名字为 a_agent,另一个 agent new 的名字为 b_agent,那么最先执行的是 a_agent 里的 build_phase 再是 b_agent 的 build_phase。
- 同样的 dadd_driver,dadd_imonitor, dadd_sequencer 是同级的 component,在 agent 中 new 的名字为 drv,imon,sqr,build_phase 执行的顺序是 dadd_driver,dadd_imonitor,dadd_sequencer
2.4 objection 机制
- UVM 通过 phase 的 objection 机制来控制验证平台的关闭。在每个 phase 中,UVM会检查是否有 objection 被提起(raise_objection),如果有,那么等待这个 objection 被撤销(drop_objection)后停止仿真,如果没有则马上结束当前 phase。
- raise_objection和drop_objection 要成双成对出现了,注意:raise_objection 必须放在 main_phase 第一个消耗仿真时间的语句之前。
- object 机制会遍历所有 component 里的 phase 的 objection 是否被撤销,如果全部被 撤销才能进入下一个 phase。
例如下图所示,在执行到 task reset_phase 中会等到 component A,component B,component C 的 objection 全部被撤销之后才能进入 configure phase。
objection 机制的好处在于可有效的控制平台的执行和关闭,提高了验证平台的灵活性和可控性