package data_pkg;
class transction;
int unsigned data;
int unsigned addr;
function new(int unsigned val);//带有参数的构造函数,则例化的时候new()要传参,比如t1 = new(10);
data = val;
addr = 'h4;
endfunction
virtual function bit get_data_xored();//virtual作用:即虚方法,使得指向子类对象的父类句柄真正能调用子类对象,除了new函数,父类和子类的其他函数都加下虚方法,此时子类与父类保持动态绑定,三个地方要一致,即函数名、参数和返回值
return ^data;//^a操作就是将a中的每一位按位逐一进行异或,例如a=4'b1010,则b=1^0^1^0=0,由此可以判断a中为1的位数是奇数还是偶数,是一个便捷的操作。
endfunction
virtual function int unsigned get_addr_shifted(int bits = 2);
return addr << bits;//左移两位,相当于*d4,d2112(即h210)*d4=h840(或b0010 0001 0000左移两位=h840),继续左移4位是h8400,最后一个h0是b0000,或者d2112(即h840)*d16=h8400
endfunction
endclass
class user_trans extends transction;
int unsigned shifted = 3;
function new(int unsigned val);//可以将int unsigned val去掉,例化不传new(20),但是最好与父类保持一致,包括new函数名、函数参数、返回值,加虚方法就要保持一致,不加就不用
super.new(val);//先执行父类对象的初始化
data = val<<1;//再执行子类对象的初始化
//super.new();
endfunction
function bit get_data_xored();
return !(^data);
endfunction
function int unsigned get_addr_shifted(int bits = 2);//int bits = 2与父类保持一致,sv同一个类不能有多个同名不同参数的函数
addr <<= shifted;//即addr = addr << shifted,因为这里没用return,所以要赋值给自身
return super.get_addr_shifted(bits);
endfunction
endclass
endpackage
module tb;
import data_pkg::*;
transction t1, t2;
user_trans u1, u2;
initial begin
int unsigned addr;
bit xored;
int unsigned addr_u2;
bit xored_u2;
t1 = new(10);
t1.addr = 'h210;
t1.data = 'h111;
xored = t1.get_data_xored();
addr = t1.get_addr_shifted();
u1 = new(20);
u1.addr = t1.addr;
u1.data = t1.data;
u1.shifted = 4;
xored = u1.get_data_xored();
addr = u1.get_addr_shifted();
//u1 = t2 父类句柄赋给(转为)子类,子类句柄指向父类对象(错误)
t2 = u1;//等同于$cast(t2, u1),子类句柄可直接赋值(转为)给父类句柄,父类句柄指向子类对象(正确),此时都指向子类的对象,父类句柄t2也指向子类对象,但是不加虚方法只能调用父类对象,加虚方法才能调用子类对象
t2.addr = 'h120;
t2.data = 'h345;
//t2.shifted = 1;//但是父类句柄找不到子类的变量shifted,只能找到父类的成员变量
xored = t2.get_data_xored();//也是调用父类的方法,要在父类方法前加virtual即虚方法,才会调用子类方法,只有子类方法没有时才会调用父类的
addr = t2.get_addr_shifted();
//不加虚方法virtural的情况,可以将父类句柄转化为子类句柄访问子类对象(这么理解而已),(实际上)此时t2是父类句柄指向子类对象(正确),u2是子类句柄指向子类对象
if(!$cast(u2, t2))//$cast(u2, t2)转化成功/失败返回1/0,注意不能直接把$cast(u2, t2),前提是子类赋给(转化为)父类,即t2=u1,这是向上类型转换,安全,将独特的子类转换为父类的类型,更注重通用性,共性越多,重用性越好。
//父类句柄赋给(转为)子类类型,才能访问子类特有的资源。
$fatal("TYPE CASTING ERROR!");
u2.addr = 'h230;
u2.data = 'h456;
u2.shifted = 1;
xored_u2 = u2.get_data_xored();
addr_u2 = u2.get_addr_shifted();//为啥不是xored_t2 = t2.get_addr_shifted?$cast(u2, t2)类似于u2 = t2,当然是用u2,但questasim中的t2、u2变量确实都指向u1的子类对象,但不加虚方法t2依然进入父类中调用父类对象。在68行addr = t2.get_addr_shifted()那里,如果没虚方法,那调用的是自己父类的对象,和此处调用子类对象不一致不同
$finish();
end
endmodule