面向对象的编程OOP
首先回顾SystemVerilog中class的三大特性——封装性、继承性和多态性。
封装性
SV中的class中的成员包括:
- 变量:用于数据建模
- 子程序:用于数据操作
继承性
class可以通过基类(父类)派生新类(子类),新类会继承基类的所有数据和数据处理(方法)。
派生类兼容基类
函数的形参为父类,同时也可以传入基类的子类。
父类和子类可以相互转换。
多态性
多态性涉及到虚方法(virtual method),实方法看句柄的类型,虚方法看对象的类型。
方法不带virtual,看句柄的类型,无论传入的实参是什么
如果是虚方法:
利用虚方法可以将传入的不同对象,调用不同的方法:
建议method都定义成virtual,这样才能利用多态的属性,只要基类的方法定义成virtual,子类的方法无所谓
在class之外定义子程序
4. 参数化类
参数化类声明例化对象时可以设置不同的数组大小或者数据类型(type)。参数化类的参数可以是一个变量、也可以是type关键词定义的数据类型。
eg:
class vector #(int size = 1);//参数是一个变量
bit [size-1:0] a;
endclass
vector #(10) vten; // object with vector of size 10
vector #(.size(2)) vtwo; // object with vector of size 2
typedef vector#(4) Vfour; // Class with vector of size 4
class stack #(type T = int);//参数是一个数据类型
local T items[];
task push( T a ); ... endtask
task pop( ref T a ); ... endtask
endclass
stack is; // default: a stack of ints
stack#(bit[1:10]) bs; // a stack of 10-bit vector
stack#(real) rs; // a stack of real numbers
注:任何类型(type)都可以作为一个参数,包括user定义的class或者struct等类型(type)。
一个参数化类可以被扩展成其他参数化累,例如:
class C #(type T = bit); ... endclass // base class
class D1 #(type P = real) extends C; // T is bit (the default)
class D2 #(type P = real) extends C #(integer); // T is integer
class D3 #(type P = real) extends C #(P); // T is P
class D4 #(type P = C#(real)) extends P; // for default T is real
4.1 参数化类中类作用域操作符的使用
当缺省参数化类作为类解析操作符前缀时,应该显式的使用#()。例如:
class C #(int p = 1);
parameter int q = 5; // local parameter
static task t;
int p;
int x = C::p; // C::p disambiguates p
// C::p is not p in the default specialization
endtask
endclass
int x = C::p; // illegal; C:: is not permitted in this context
int y = C#()::p; // legal; refers to parameter p in the default specialization of C
typedef C T; // T is a default specialization, not an alias to the name "C"
int z = T::p; // legal; T::p refers to p in the default specialization
int v = C#(3)::p; // legal; parameter p in the specialization of C#(3)
int w = C#()::q; // legal; refers to the local parameter
T obj = new();
int u = obj.q; // legal; refers to the local parameter
bit arr[obj.q]; // illegal: local parameter is not a constant expression
参数化类使用extern关键在在类外声明方法。
class C #(int p = 1, type T = int);
extern static function T f();
endclass
function C::T C::f();
return p + C::p;
endfunction
initial $display(“%0d %0d”, C#()::f(),C#(5)::f()); // output is "2 10"
自定义的class
Static property 静态变量/数据
静态方法
类名::静态方法可以直接调用静态方法。
Singleton class单例类
- 用于定义全局行为,如打印,工厂。
- 单例类不存在对象(不能new和create)
- 只包含静态变量/子函数,不允许有非静态的成员变量
单例类的打印函数(统计错误次数)
class print;
static int err_count = 0, max_error = 10;
static function void error(string msg);
$display("@%t:ERROR %s", $realtime, msg);
if(err_count++ > max_errors) $finish;
endfunction
endclass
调用打印函数
if(expect != actual)
print::error("Actual did not match expected");
单例对象
- 单例对象是一个全局可见/可操作的对象,提供定制化服务/函数。
(1) 有且仅有一个对象(在编译时创建)。
(2) 在仿真时全局可见/可操作
(3) 可以有静态或者非静态成员
class factory;
static singleton me = get();
static function singleton get(); //反回singleton类型的函数
if(me == null) me = new(); return me;
endfunction
local function new();
endfunction
extern function void print();
endclass
factory f = factory::get(); //使用get()方法保证单例对象全局只有一个对象
由于factory对象中new函数是local,因此不能在外部调用:factory fa = new(),只能调用其get()
代理类
- 提供**creat()**等通用型的子函数
class proxy_class #(type T = base);
typedef proxy_class #(T) this_type;
static this_type me = get(); //构建一个代理类的对象
static function this_type get();
if(me = null) me = new(); return me; //返回代理类的一个单例对象
endfunction
static function T create(); //创建T的对象,即组件的对象
create = new();
endfunction
endclass
代理类产生的是单例对象。
调用create可以创建一个T类型的对象。
class transaction extends base;
typedef proxy_class #(transaction) proxy;
endclass
//-------------------------------------------
class driver extends base;
typedef proxy_class #(driver) proxy;
endclass
class environment;
transaction tr; driver drv;
function new();
tr = transaction::proxy::create();//利用代理类创建一个transaction对象
drv = driver::proxy::create(); //创建一个driver对象
endfunction
endclass
用create函数代替了new函数
工厂机制
- 创建工厂,需要两层代理类
(1) 虚代理类(Virtual proxy base class)(空壳)
(2) 派生代理类,实现全部功能
虚代理基类
(1) 代理基类要具有多态属性
(2) 为代理服务提供抽象的接口应用程序
i 创建对象(create_object)
ii 获取类型名称(get_typename)
//虚代理基类
virtual class proxy_base;
virtual function base create_object(string type_name); //虚方法可实现多态
return null;
endfunction
pure virtual function string get_typename();
endclass
pure virtual function 是只有定义,没有实体的方法,只可以在virtual class中定义。pure virtual function 只有在扩展类中定义了实体,才可以使用
virtual class, 可被声明,可以被extend, 但是不能被实例化。
class factory;
/* 0. 代理基类的注册表 */
static proxy_base registery[string]; //数组的索引是字符串
/* 1. 获得工厂的单例对象 */
static factory me = get();
static function factory get();
if(me == null) me = new(); return me; //factory是单例对象
endfunction
/* 2. 代理基类的注册机制 */
function void register(proxy_base proxy);
registry[proxy.get_typename()] = proxy; //类型名称对应其代理类
endfunction
/* 3. 分配每个组件的对象 */
function base create_object_by_type(proxy_base proxy,string name);
proxy = find_override(proxy);
return proxy.create_object(name);//分配每个组件的对象
endfunction
- 维护被覆盖的代理登记表
(1) 如果代理类被覆盖,创建覆盖后的类的对象
/* 4. 替换列表 */
static string override[string]; //组件原来名字和替换的组件名字
static function void override_type(string type_name, override_typename);
//type_name原来的名字,override_typename替换的名字
override[type_name] = override_typename;
endfunction
/* 5.找组件是否有替换 */
function proxy_base find_override(proxy_base proxy);
if(override.exists(proxy.get_typename())) //查看组件是否被注册
return registry[override[proxy.get_typename()]]; //注册返回其替换的组件的基类
return proxy; //没有注册返回本省的基类
endfunction
endclass
Proxy class代理类(完善虚代理类的功能)
- 完成抽象的接口函数
- 在工厂中登记
- 利用工厂创建对象
class proxy_class #(type T=base, string Tname="none") extends proxy_base;//传参:组件和组件名字
typedef proxy_class #(T,Tname) this_type;
static string type_name = Tname; //传入的名字
static this_type me = get(); //获得代理类的一个单例对象
static function this_type get();
factory f = factory::get(); //获得一个工厂对象me
if(me=null) begin
me = new(); //获得this_type的对象
f.register(me); //注册,每个class都会注册
end
return me;//返回一个this_type对象
endfunction
static function T creat(string name); //分配对象
factory f = factory::get();
$cast(creat, f.create_object_by_type(me,name));
endfunction
virtual function base create_object(string name);
T object_represented = new(name);
return object_represented;
endfunction
endclass
测试案例可以使用工厂机制替换验证平台创建的任何对象,但却不要修改验证平台的代码,提高了代码的可重用性