类型转换
类型转换分为动态转换和静态转换,静态转换只需要在转换的表达式之前加上单引号就可以了,静态转换并不会对得到的转换值进行检查,所以即使发生错误,我们也不得而知;动态转换需要使用系统函数$cast(tgt , src)做转换。静态转换和动态转换都需要加上操作符后者系统函数,所以被称之为显示转换。
什么是隐式转换?不需要进转换的操作。很经典的例子就是在进行赋值操作的时候等式左边是四位的矢量,等式右边是五位的矢量,这种赋值操作会进行位宽拓展,然后再进行赋值。
什么时候用到动态转换?在使用类的时候,类句柄的向下转换(父类句柄转换为子类句柄)需要进行动态转换,否则会发生编译错误。动态转换会显示1/0来表达转换成功或者失败
tr.display 最终会采用Transaction::display的方式来打印,因为tr是父类的句柄,父类的句柄里面只能看见父类的方法,父类是小三角形,子类的大三角形,父类的视野是小于子类的。将一个父类句柄赋值给一个子类句柄并不总是法的。 但是SV编译器对这种直接赋值的做法是禁止的。也就是说无论父类句柄是否真正指向了一个子类对象,赋值给子类句柄时, 编译都将出现错误。 因此需要实现句柄类型的动态转换。 $cast(tgt, src)会检查句柄所指向的对象类型,而不仅仅检查句柄本身。 一旦源对象跟目的句柄是同一类型, 或者是目的句柄的扩展, $cast()函数执行即会成功,返回1,否则返回0。
虚方法
类的继承是主要是继承成员方法和成员变量这两个方面,由于类的多态性,是的用户在设计和实现类的时候,不用太过担心句柄指向的对象是父类还是子类,只要通过虚方法就可以实现动态绑定(也称之为动态方法查找)。对比一下非虚方法调用和虚方法调用之间的差别:
首先是非虚方法的调用,t是父类句柄,wr是子类句柄,例化子类句柄然后传递给父类句柄,所以顺序是首先打印第一个display,然后wr.test(),首先索引到的应该子类的test,但是由于super.new()显示的调用了父类的中的方法,所以会调用父类的display再执行子类的方法。然后执行第2和第3个display,随后t.test()调用父类的句柄。调用父类的方法这是因为局限性(大小三角形)。需要注意的是子类覆盖的方法并不会继承父类同名的方法,只有通过super.new()显式的调用才会达到继承父类的效果。
静态绑定就是在编译阶段就可以确定下来的调用方法所处的作用域,与之对应的就是动态绑定,是在调用方法时,会在运行阶段才确定句柄指向对象的具体类型,在动态的指向应该调用的方法,为了实现动态绑定,我们加入关键词virtual。
发现加了virtual以后子类和父类的句柄调用的都是子类的函数。声明了basic_test为虚方法,在执行t.test()的时候就会发现t指向的是子类,从而会执行子类的方法。所以给出的建议是:在为父类定义方法时,如果该方法日后可能会被覆盖或者继承, 那么应该声明为虚方法 ;虚方法如果要定义,应该尽量定义在底层父类中,这是因为如果 virtual是声明在类继承关系的中间层类中,那么只有从该中间类 到其子类的调用链中会遵循动态查找,而最底层类到该中间类的方法调用仍然会遵循静态查找; 虚方法通过virtual声明,只需要声明一次即可,当然再次声明来表明该方法的特性也是可以的; 虚方法的继承也需要遵循相同的参数和返回类型,否则,子类定义的方法须归为同名不同参的其它方法。
对象拷贝
赋值:声明变量和创建对象
pocket p1;
p1=new;
如果把p1赋值给p2,最终的对象还是一个,只不过是增加了一个句柄而已。如果需要产生两个不同的对象就需要浅拷贝
pocket p1;
pocket p2;
p1=new;
p2=new p1;
普通的变量拷贝通过等号就可以实现:
对象的拷贝则需要首先创建一个新的对象开辟出新的空间,然后将目标对象的成员和变量拷贝给新的对象,可以使得目标对象和新对象的成员变量和数值保持一致。
将成员拷贝函数copy_data()和新对象生成函数copy()分为两个方法,这样使得子类继承和方法复用较为容易。 为了保证父类和子类的成员均可以完成拷贝,将拷贝方声明为虚方法, 并且遵循只拷贝该类的域成员的原则,父类的成员拷贝应由父类的拷贝方法完成。 在实现copy_ata()过程中应该注意句柄的类到转换,保证转换后的句柄可以访问类成员变量。
回调函数
使用回调函数的目的?为了使得我们的验证环境可以做到更好地复用,在较少的改动或者外部少量的配置情况下就可以嵌入到新的验证环境里面。为达到这个目的可以有两种方法可以选择,第一种是通过顶层环境的配置对象自顶向下的进行参数配置;第二个就是在不更改原始代码的基础上注入新的代码,回调函数就是属于第二类中的。回调函数的使用分为三个步骤:预留入口、定义回调函数、例化添加回调实例。
预留入口:
定义回调函数:这里drop的值为1的概率是百分之1,为0的概率是百分之99
添加、例化实例,在这里回调函数的作用是用来修改drop的值,来决定是否发送tr:
参数化的类
参数化的使用目的也是为了提高代码的复用率,在硬件设计中,参数往往是整型、端口数目或者位宽,而在验证环境中,参数的运用就更加灵活,可以使用各种类型来做类定义时的参数。