在看绿皮书时,对 $cast 理解的很浅显,在查阅了更多相关内容,甚至发现理解还有误,所以下面对它进行详细的总结。
1.句柄与对象
1.句柄:指向对象的指针
2.对象:对象是类的一个实例
BadTr bad;//创建类的句柄
bad = new();//创建对象
2.静态转换和动态转换
SV类型转换分为两种方法,一种是静态类型转换,另一种是动态类型转换。
2.1 静态转换
静态转换即需要在转换的表达式前加上单引号即可,该方式不会对转换值做检查。如果发生转换失败,我们也无从得知。但是 $cast 的 task 却不是这样,它在运行时将进行类型检查,如果转换失败,会产生运行时错误。
int a = 2;
real b;
b = real'(a);
2.2 动态转换
动态转换即需要使用系统函数 $cast(tgt,src) 做转换,为加深理解可以将 , 等价于 =,即 tgt = src 将 src 类型转换成 tgr 类型。
父类站的高,子类在底下,从父类向子类的转换,称为向下类型转换;
子类向父类的转换称为向上类型转换。
由于子类继承了父类,拥有了父类的一切属性,除此之外,子类还有自己独特的个性,这是父类没有的。
- 向上类型转换:合法类型转换,父类句柄指向子类对象,句柄仍然能对子类对象与父类相同的属性进行访问。
- 向下类型转换:非法类型转换,因为子类含有比父类更丰富的属性,有可能访问父类并不包含的资源,导致越界。
向上类型转换:
base_class bc;
sub_class sc = new();
bc = sc;//父类句柄指向子类对象这是正确的
class Transaction;
rand bit [31:0] src;
virtual function void display(input string prefix="");
$display();
endfunction
endclass
class BadTr extends Transaction;
bit crc;
virtual function void display()
super.display(prefix);
$display();
endfunction
endclass
Transaction tr;
BadTr bad,bad2;
bad = new();//构建BadTr扩展对象
tr = bad;//基类句柄指向扩展对象
$display(tr.src);//显示基类对象的变量成员
tr.display;//调用BadTr::display
如果display未用virtual修饰
tr.display;//调用Transaction::display
调用display函数时,调用的是父类还是派生类的diaplay函数
调用时,首先通过句柄类型找到该函数,查看有无virtual修饰符
- 有virtual修饰符,那么就调用实际对象类型的display函数;
- 无virtual修饰符,那么就调用句柄类型的display函数;
注意: tr.crc错误,由于tr句柄是Transaction类型的,所以看不到crc变量
将一个父类句柄赋值给一个子类句柄并不总是非法的。
满足以下条件是合法的: - 父类句柄指向一个子类对象
- 源对象和目的句柄是同一类或者是目的句柄类型的子类,转换合法
使用 $cast(tgt,src) 来实现句柄类型的动态转换。
而且 $cast(tgt,src) 会检查句柄所指的对象类型,而不仅仅检查句柄本身。
一旦源对象跟目的句柄是同一类型,或者是目的句柄的扩展类, $cast() 函数执行即会成功,返回1,否则返回0
向下类型转换
base_class bc;
sub_class sc1,sc2;
sc2 = new();
bc = sc2;//父类句柄指向子类对象
$cast(sc1,bc);//通过cast方式可以实现,可以看到bc的句柄类型虽然是父类,但其指的对象类型是子类
这样的类型转换好处:
要处理具体的内容就需要将父类句柄类型转换成子类类型才能访问子类特有的资源。