在没有factory机制之前, 要创建一个类的实例, 只能使用new函数。但是有了factory机制之后, 除了使用new函数外, 还可以根据类名创建这个类的一个实例;
bird_inst = bird::type_id::create("bird_inst");
另外, 还可以在创建类的实例时根据是否有重载记录来决定是创建原始的类, 还是创建重载的类的实例。所以, 从本质上来看, factory机制其实是对SystemVerilog中new函数的重载。
因为System Verilog本身就是一种面向对象的语言,即本身就支持重载(override),重载即当父类中定义一个virtual类型的函数或任务时,在子类中可以重载这个函数或任务。所以我们来先看一下System Verilog对重载的支持。
首先我们定义两个class,bird和parrot,其中parrot派生自bird。每个类中都定义两个同名的函数hungry()和hungry2()。但是hungry为virtual类型的函数,hungry2为普通函数。
24 class bird extends uvm_object;
25 virtual function void hungry();
26 $display("I am a bird, I am hungry");
27 endfunction
28 function void hungry2();
29 $display("I am a bird, I am hungry2");
30 endfunction
…
36 endclass
37
38 class parrot extends bird;
39 virtual function void hungry();
40 $display("I am a parrot, I am hungry");
41 endfunction
42 function void hungry2();
43 $display("I am a parrot, I am hungry2");
44 endfunction
…
50 endclass
然后我们定义print函数,来调用hungry和hungry2函数。
62 function void my_case0::print_hungry(bird b_ptr);
63 b_ptr.hungry();
64 b_ptr.hungry2();
65 endfunction
66
67 function void my_case0::build_phase(uvm_phase phase);
68 bird bird_inst;
69 parrot parrot_inst;
70 super.build_phase(phase);
71
72 bird_inst = bird::type_id::create("bird_inst");
73 parrot_inst = parrot::type_id::create("parrot_inst");
74 print_hungry(bird_inst);
75 print_hungry(parrot_inst);
76 endfunction
这时74行的输出为:
"I am a bird, I am hungry"
"I am a bird, I am hungry2"
75行的输出为:
"I am a parrot, I am hungry"
"I am a bird, I am hungry2"
74行比较好理解,因为print_hungry函数定义的时候传入的参数就是bird类型的指针,而这里输入的就是bird类型的指针,所有输出的一定是bird类里的内容。但75行的输出就要难理解一点,这里传入的parrot类型的指针,但是在调用hungry函数的时候,parrot指针会被隐式地转换成bird类型,这时因为hungry是virtual类型的函数,所以转换成bird类型后输出的仍然是parrot,而hungry2不是virtual类型,不能被重载,所以输出的仍然是bird。重载的最大优势就是使得一个子类的指针以父类的类型传递时,其表现出的行为依然是子类的行为。
System Verilog还有一个非常有用的特性是支持约束的重载。像我们已经在transaction里定义了某些变量的约束,如果我们需要修改约束的时候只需要派生一个新的transaction,然后直接重载约束即可。
4 class new_transaction extends my_transaction;
5 `uvm_object_utils(new_transaction)
6 function new(string name= "new_transaction");
7 super.new(name);
8 endfunction
9
10 constraint crc_err_cons{
11 crc_err dist {0 := 2, 1 := 1};
12 }
13 endclass
变量crc_err已经在my_transacton里给定了约束,但是我们想更改这个约束,就从my_transaction里派生一个新的transaction,然后直接重载crc_err的约束即可。
接下来我们来看factory机制式的重载。factory机制最伟大的地方在于其具有重载功能。
67 function void my_case0::build_phase(uvm_phase phase);
…
72 set_type_override_by_type(bird::get_type(), parrot::get_type());
73
74 bird_inst = bird::type_id::create("bird_inst");
75 parrot_inst = parrot::type_id::create("parrot_inst");
76 print_hungry(bird_inst);
77 print_hungry(parrot_inst);
78 endfunction
看起来和system Verilog的重载机制差不多,但多了一个注册函数,set_type_override_by_type(),我们先来看输出结果:
"I am a parrot, I am hungry"
"I am a bird, I am hungry2"
"I am a parrot, I am hungry"
"I am a bird, I am hungry2"
现在换成第二次print好理解,但第一次又难以理解了,但这就是factory的重在功能,也是它和System Verilog的重载不同的地方。
factory重载的原理如图:
![41882743d8e7f9730a75fc80de457147.png](https://i-blog.csdnimg.cn/blog_migrate/50bc42cc3b1ae55f6ad0e735527a0643.jpeg)
![22230d89cbc2a1174eb7258e1e9aca6c.png](https://i-blog.csdnimg.cn/blog_migrate/41dce02575a6c80ca3e75045025cc66e.jpeg)
在实例化时,实例化语句并不会立马生效, UVM会通过factory机制在自己内部的一张表格中查看是否有相关的重载记录。 set_type_override_by_type语句相当于在factory机制的表格中加入了一条记录。 当查到有重载记录时, 会使用新的类型来替代旧的类型。 所以虽然在build_phase中写明创建bird的实例, 但是最终却创建了parrot的实例。
但factory机制的重载是有前提的,需要满足四个条件:
一,无论是重载的类(parrot) 还是被重载的类(bird) , 都要在定义时注册到factory机制中。
二,被重载的类(bird) 在实例化时, 要使用factory机制式的实例化方式, 而不能使用传统的new方式。
三,最重要的是, 重载的类(parrot) 要与被重载的类(bird) 有派生关系。 重载的类必须派生自被重载的类, 被重载的类必须是重载类的父类。
四,component与object之间互相不能重载。