| |
OVM重要概念之——Factory模式
当你希望改变Testbench结构但又不想改变Testbench代码时,例如想换一个driver,Factory模式可以满足你的需求。
.Factory模式提供了一个 不改变testbench代码替换object的方法。Factory保留了一个registered objects列表。当你创建新的对象时,列表被访问。如果已经存在,则返回override object,否则返回registered object。
想要仔细了解factory机制有点费工夫,(但是想要知道怎样使用还是很容易的).下面的例子展示factory机制:
class object_base;
virtual function void print();
$display("object_base");
endfunction
endclass
//----------------------------------------------------------------------
// wrapper_base
//
// Wrapper base for all objects registered with the factory. Since
// wrappers are parmaeterized, this base class lets us deal with the all
// the paramerized derived classes polymorphically.
//----------------------------------------------------------------------
class wrapper_base;
virtual function object_base create_object();
return null;
endfunction
endclass
//----------------------------------------------------------------------
// factory
//
// Singleton object that contains the core data structure for the
// factory and the methods for accessing the data structure. The data
// structure is an associative array of overrides indexed by type
// handle.
//----------------------------------------------------------------------
class factory;
static factory f;
wrapper_base override_map[wrapper_base];
local function new();
endfunction
static function factory get();
if(f == null)
f = new();
return f;
endfunction
function void register(wrapper_base w);
override_map[w] = w;
endfunction
function void set_override(wrapper_base requested_type,
wrapper_base override_type);
override_map[requested_type] = override_type;
endfunction
function object_base create(wrapper_base
requested_type);
object_base obj;
wrapper_base override_type =
override_map[requested_type];
bj = override_type.create_object();
return obj;
endfunction
endclass
//----------------------------------------------------------------------
// parameterized wrapper
//
// Each specialization of the parameterized wrapper represents a type.
// These objects are singletons so there can never be more than one
// handle for each type.
//
// The constructor, new(), is a private function. The only way to
// create a new wrapper is by using the get_type() function, the
// constructor cannot be called directly.
//----------------------------------------------------------------------
class wrapper #(type T=object_base) extends wrapper_base;
typedef wrapper#(T) this_type;
static this_type type_handle = get_type();
local function new();
endfunction
function object_base create_object();
T t = new();
return t;
endfunction
static function T create();
T obj;
factory f = factory::get();
assert($cast(obj, f.create(get_type())));
return obj;
endfunction
static function this_type get_type();
factory f;
if(type_handle == null) begin
type_handle = new();
f = factory::get();
f.register(type_handle);
end
return type_handle;
endfunction
static function void set_override(wrapper_base
override_type);
factory f = factory::get();
f.set_override(type_handle, override_type);
endfunction
endclass
//----------------------------------------------------------------------
// family of classes derived from a single base class
//----------------------------------------------------------------------
class family_base extends object_base;
typedef wrapper#(family_base) type_id;
virtual function void print();
$display("family_base");
endfunction
endclass
class A extends family_base;
typedef wrapper#(A) type_id;
virtual function void print();
$display("A");
endfunction
endclass
class B extends family_base;
typedef wrapper#(B) type_id;
virtual function void print();
$display("B");
endfunction
endclass
class C extends family_base;
typedef wrapper#(C) type_id;
virtual function void print();
$display("C");
endfunction
endclass
//----------------------------------------------------------------------
// env
//
// top-level environment where we execute the example
//----------------------------------------------------------------------
class env;
factory f;
family_base h;
function void run();
f = factory::get();
h = family_base::type_id::create();
h.print();
family_base::type_id::set_override(B::type_id::get_type());
h = family_base::type_id::create();
h.print();
endfunction
endclass
//----------------------------------------------------------------------
// top
//----------------------------------------------------------------------
module top;
env e;
initial begin
e = new();
e.run();
end
endmodule
引用网上牛人对此例的解释
OVM Cookbook中对OVM中的工厂模式进行了详细的介绍,其中使用了一个toy factory的例子来阐述工厂模式的实现原理,让读者的理解更加深入。
几个重要的概念需要强调一下:
1. 单例模式实现的重点:
· 将构造函数申明为私有成员函数,防止外部调用构造函数来创建对象
· 申明一个类型为本身的静态对象句柄,通过静态函数来创建唯一的一个对象,并将其赋值给此对象句柄。此对象句柄就可以作为访问此单例的一个窗口,也可以用静态函数返回值来访问此单例的对象句柄。
2. 工厂中可以互相overide的类型必须来自于同一个基类。
· 由于工厂中保存的是对象的关联数组,在有些编程语言中也叫做字典(python中好像就这么称呼),此关联数组的关键字(key)是对象句柄。而关联数组要求关键字必须是同一个类型,所以overrige的类型必须从属于同一个基类,以保证他们是同一个类型。
· 对每一个类,都生成一个wrapper类,此wrapper类也是一个单例,其对象被静态创建,这个对象一旦被创建,其对象句柄就是唯一的(因为只有一个对象实例)。因此很容易定位此对象句柄,从而通过这个句柄来访问关联数组。
3. 静态函数,静态变量
· 静态函数和静态变量不会和具体的对象实例相关联,实在运行时初始化阶段被创建的,某一种类型只有唯一的一套。因此可以通过类型修饰符"::"访问,而非静态函数和变量是不能用类型修饰符进行访问的。比如OVM Cookbook中实例04-07中:
将 h = family_base::type_id::create();语句换成如下语句: |
· 静态函数是静态创建的,那么他也只能访问类型的静态变量和静态函数,因为其他变量并没有被创建。
4. OVM除了提供以对象句柄作为关键字的关联数组来实现工厂,同时也提供了以字符串作为关键字的关联数组来实现工厂。
· +OVM_TESTNAME="testcase1" 这种命令行参数中,字符串工厂比较实用,仅此而已
· 字符串工厂实现更简单,不需要引入wrapper类。直接通过factory.create_component_by_name()函数来创建对象,返回类型必须通过向下转型(downcast)来赋值到实际的具体类型。
· 字符串工厂并没有类型检查,没有对象句柄方式安全。
5 wrapper的设计思想
· wrapper#(T)是单例的,因此外部不能调用其构造函数,只能通过get_type()来得到其唯一的一个静态对象
· get_type()除了用来生成wrapper类的唯一对象,同时还将自身注册到factory工厂中的关联数组中,注册也就是给关联数组增加一个元素,其关键字就是此wrapper#(T)的唯一静态对象的句柄。
· wrapper#(T)必须提供一个函数来生成类型T的对象,此函数就是create_object(), 类型T的对象实际中可能有多个,因此create_object()不同的调用将返回不同的类型T的对象,此函数不需要是静态的,此函数仅仅被工厂中的关联数组的值(关联数组的值就是具体的对象句柄)来调用。
· wrapper#(T)必须提供一个静态函数,通过类型方式进行调用,也就是create()函数。wrapper中的静态create()函数将通过调用工厂中的create函数来创建对象。