在OOP中我们通过继承的方式给子类赋值或者增加新的功能:
class cat;
protected color_t color;
local bit is_good;
function set_good(bit s);
this.is_good = s;
endfunction
endclass
/声明黑猫类/
class black_cat extends cat;
function new();
this.color = BLACK;
endfunction
endclass
/声明白猫类/
class while_cat extends cat;
function new();
this.color = WHITE;
endfunction
endclass
black_cat bk;
white_cat wt;
initial begin
bk = new(); //初始化黑猫对象
wt = new(); //初始化白猫对象
bk.set_good(1);
wt.set_good(1);
end
在验证的过程中我们通过单一的职责划分,使得各个组件的任务明确,那么一个 简单的环境构建就有了。
案例1:对于数据发送,我们希望把数据都封装到类里面,因为类中可以封装 很多的数据和方法。
class Transaction;
rand bit [31:0] src, dst, data[8]; //随机变量
bit [31:0] crc //二次处理后的成员变量
virtual function void calc_crc();
crc = src^dst^data.xor;
endfunction
virtual function void display(input string prefix="");
$display("%sTr: src=%h, dst=%h, crc=%h",prefix,src, dst, crc);
endfunction
endclass
但是,此时我们在此 基类的基础上又需要增加新的功能 , 生成一个新的类型。 此时就可以考虑通过继承的方式,来实现此功能。
class BadTr extends Transaction;
rand bit bad_crc;
virtual function void calc_crc;
super.calc_crc(); //计算好的CRC值
if (bad_crc) crc = ~crc; //CRC值取反
endfunction
virtual function void display(input string prefix="");
$write("%sBadTr: bad_crc = %b, ", prefix, bad_crc);
super.display();
endfunction
endclass : BadTr
在子类中重新定义了新的成员变量和 成员方法,新定义的成员方法和原方法之间没有关系。
super来实现子类的方法来继承父类的方法。super.calc_crc()函数可以实现对父类中函数的调用, 之后再去添加子类的方法。在其内部通过super来索引父类的同名函数。
但是,子类中要是想索引父类中的变量
案例2:在验证环境中,也有类似的类trans和一个initiator类 stm_ini,在test中通过继承于basic_test的两个子类test_wr和test_rd,分别用来对DUT发起写测试和读测试。
class basic_test;
int def = 100; //成员变量赋予默认值
int fin;
task test(stm_ini ini);
$display("basic_test::tset");
endtask
function new(int val);
endfunction
endclass
class test_wr extends basic_test;
function new();
super.new(def);
$display("test_wr::new");
endfunction
task test(stm_ini ini);
super.test(ini);
$display("tast_wr::test");
endtask
endclass
class basic_test;
int def = 100; //成员变量赋予默认值
int fin;
task test(stm_ini ini);
$display("basic_test::tset");
endtask
function new(int val);
endfunction
endclass
class test_rd extends basic_test;
function new();
super.new(def);
$display("test_rd::new");
endfunction
task test(stm_ini ini);
super.test(ini);
$display("tast_rd::test");
endtask
endclass
子类在定义new 函数的时候,应该首先调用父类 的new函数---super.new()。若父类new函数没有参数,那么省略该调用,系统编译时可以自动添加super.new。
对象创建和初始化的顺序来看,用户注意一下规则:
对于成员的覆盖问题:
class basic_test;
int def = 100; //成员变量赋予默认值
int fin;
task test(stm_ini ini);
$display("basic_test::tset");
endtask
function new(int val);
endfunction
endclass
class test_wr extends basic_test;
int def = 200;
function new();
super.new(def);
$display("test_wr::new");
$display("test_wr::super.def = %0d", super.def);
$display("test_wr::this.def = %0d", this.def);
endfunction
endclass
module tb;
basic_test t;
test_wr wr;
initial begin
wr = new();
t = wr;
$display("wr.def = %0d", wr.def);
$display("t.def = %0d", t.def);
end
创建了子类对象,子类对象调用的def值为多少?200
把子类句柄的值赋值给父类,通过父类索引到def为多少?100
父类和子类句柄都指向了对象,但是子类句柄指向范围更广,父类只能访问父类那部分成员变量和方法。
子类的访问:
wr.def : 访问子类的变量
wr.super.def:通过子类访问父类的变量
拿到一个父类句柄,该句柄可能指向子类对象(被一个子类对象赋值之后)也可能指向父类对象。但是, 只能访问子类对象中属于父类的成员。
但是不能把父类赋值给子类对象,编译时会报错。编译时默认按照最差的情况运行。父类句柄转化为子类句柄只能通过
$cast(T,S)运行。
若子类中没有def,通过子类对象索引:wr.def时会发现子类中没有def成员变量值,接着就回去访问父类中的同名def=100。
而此时,父类中访问t.def也是只能访问100。
而子类中super.new(def)中的def调用的是子类中的def, 优先查找顺序是由近到远。
若程序中,定义wr=new();t=new();定义了两个对象,则wr指向子类对象,t指向父类对象。调用成员变量和函数的时候,只从自己的类中调用。此时,使用$cast(T,S)将父类 赋值给子类也是错误的。大的类可以缩小访问(子类可以访问父类),但是小的类不能访问大类(父类不能赋值给子类)。