目录
一、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、print、pack/unpack。
二、域的自动化
域:类的成员变量
- UVM通过域的自动化,使得用户在注册UVM类的同时也可以声明今后会参与到对象拷贝、克隆、打印等操作的成员变量。
- 域的自动化使得在使用uvm_object提供的一些预定义方法时,非常便捷,无需再实现自定义方法。
- 熟悉域的自动化常用的宏之后,还需要考虑哪些成员变量在注册UVM类(`uvm_{component,object}_utils)的时候,也一并将它们归置到对应的域列表中,以便为稍后的域方法提供可以自动实现的基础。
class box extends uvm_object;
int volume = 120; //int类型
color_t color = WHITE; //枚举类型
string name = "box"; //string类型
`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_end
...
endclass
box b1, b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b2 = new();
b2.copy(b1); //copy会把b1的成员变量也拷贝给b2,copy方法是UVM自动实现的,前提是做域的自动化声明 SV中copy方法是自己实现的,而UVM中是自动实现的
b1.name = "box2";
end
三、拷贝copy
在UVM的数据操作中,copy默认已经创建好了对象,只需要对数据进行拷贝,而clone则会自动创建对象并对source object进行数据拷贝,再返回target object句柄。无论是copy还是clone都需要对数据进行复制。在进行copy时,默认进行的是深拷贝,即会执行copy()和do_copy()。
class ball extends uvm_object;
int diameter = 10;
color_t color = RED;
`uvm_object_utils_begin(ball)
`uvm_field_int(diameter, UVM_DEFAULT)
`uvm_field_enum(color_t, color, UVM_NOCOPY)
`uvm_object_utils_end
...
function void do_copy(uvm_object rhs);
ball b;
$cast(b, rhs);
$display("ball::do_copy entered...");
if(b.diameter <= 20) begin
diameter = 20;
end
endfunction
endclass
class box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
ball b;
`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_field_object(b, UVM_ALL_ON) //深拷贝
`uvm_object_utils_end
...
endclass
box b1, b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b2 = new();
b2.copy(b1);
b1.name = "box2";
$display("%s", b1.sprint());
$display("%s", b2.sprint());
end
四、比较compare
function bit compare(uvm_object rhs, uvm_comparer comparer=null);
默认情况下,如果不对比较的情况作出额外配置,可以在调用compare()方法时,省略第二项参数,即采用默认的比较配置。比较方法经常会在两个数据类中进行,例如从generator产生的一个transaction(数据类),和在设计输出上捕捉的transaction(数据类),如果它们为同一种类型,除了可以自定义数据比较之外,也可以直接使用uvm_object::compare()函数来实现数据比较和消息打印。
class box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
ball b;
`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_end
...
endclass
box b1, b2;
initial begin
b1 = new("box1");
b1.volume = 80;
b1.color = BLACK;
b2 = new("box2");
b2.volume = 90;
if(!b2.compare(b1)) begin
`uvm_info("COMPARE", "b2 compared with b1 failure", UVM_LOW)
end
else begin
`uvm_info("COMPARE", "b2 compared with b1 success", UVM_LOW)
end
end
在上面的两个对象比较中,会将每一个自动化的域进行比较,所以在执行compare()函数时,内置的比较方法也会将比较错误输出。默认的比较器,即uvm_package::UVM_default_comparer最大输出的错误比较信息是1,也就是说当比较错误发生时,不会再进行后续的比较。实际上,在uvm_object使用到的方法compare()、print()和pack(),如果没有指定数据操作配置对象作为参数时,会使用在uvm_pkg中例化的全局数据操作配置成员。
五、打印print
打印方法是核心基类提供的一种便于开发和调试的功能,通过field automation,使得声明之后的各个成员域会在调用uvm_object::print()函数时自动打印出来。相比于在仿真中设置断点,逐步调试,打印是另一种调试方式,好处在于可以让仿真继续进行,会在最终回顾执行过程中,从全局理解执行的轨迹和逻辑。
class box extends uvm_object;
int volume = 120;
color_t color = WHITE;
string name = "box";
ball b;
`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_end
...
endclass
box b1;
uvm_table_printer local_printer;
initial begin
b1 = new("box1");
local_printer = new();
$display("default table printer format");
b1.print();
$display("default line printer format");
uvm_default_printer = uvm_default_line_printer;
b1.print();
$display("default tree printer format");
uvm_default_printer = uvm_default_tree_printer;
b1.print();
$display("customized printer format");
local_printer.knobs.full_name = 1;
b1.print(local_printer);
end
六、打包和解包pack&unpack
function int pack(ref bit bitstream[], input uvm_packer packer=null);
function int unpack(ref bit bitstream[], input uvm_packer packer=null);
pack是为了将自动化声明后的域(标量)打包为比特流,即将各个散乱的数据,整理到bit数据串中,类似于struct packed的整理方式,但又能充分利用数据空间,也更容易与硬件之间进行数据传递和比对。
unpack则是将串行数据解包变为原有的各自域,该操作适用于从硬件一侧接受串行数据,进行校验之后,还原为软件一侧对象中各自对应的成员变量。
box b1, b2;
bit packed_bits[];
initial begin
b1 = new("box1");
b2 = new("box2");
b1.volume = 100;
b1.height = 40;
b1.color = RED;
b1.print();
b1.pack(packed_bits);
$display("packed bits stream size is %d\n", packed_bits.size());
b2.unpack(packed_bits);
b2.print();
end