背景:基类句柄可以指向扩展类对象,不需要额外的代码; 扩展类句柄指向基类对象,一般情况下会出错,但有时候是可以的,前提是基类句柄指向了它的一个扩展类对象。 作用:扩展类句柄指向基类对象时,使用$cast()函数。在非法的情况下,不会编译报错,会返回了一个0.
$cast做任务使用时,systemverilog会在运行时,检查源对象类型和目的对象类型不匹配,会报错;
$cast 做函数使用时,运行时,仍做类型检查,在不匹配时,不会报错,$函数返回0. 前面所述:基类句柄可以指向任何它的扩展类的对象、
1) 基类句柄指向扩展类对象——出现情况:修改蓝图,不改过多代码,增加功能 Transaction tr; //基类句柄 BadTr bad; //扩展类句柄 Bad = new();
Tr = bad; // 基类句柄指向扩展类对象 tr.display; //掉用的是扩展类的方法
2) 扩展类句柄指向基类对象——出现情况:基类virtual 方法copy函数,它的继承类中copy函数
将基类句柄赋值给扩展类句柄,使扩展类句柄指向基类对象,一般编译器会出错,不能运行,所以非常小心;只有基类句柄指向扩展类对象时,再将扩展类句柄指向基类对象时,不出错。为了检测基类句柄是否指向了扩展对象,并且不让编译器报错,可以使用$cast()函数检测。
当把扩展类句柄指向基类对象时,发生什么? Tr= new();
Bad = tr; //扩展类句柄指向基类句柄
上述会发生错误,编译不会被通过。因为有些属性在基类中不存在;但是扩展类句柄指向基类句柄不总是非法的(见下面代码,是可以的),当基类句柄指向一个扩展类对象时是允许的。
Transcation tr; BadTr bad,bad2;
Bad= new();
Tr = bad; //基类句柄指向扩展类对象 $cast(bad2,tr); //扩展类句柄指向基类对象 if(!$cast(bad2,tr);
$display(―cannot assign tr to bad2‖);
$display(bad2.bad_crc);
l 句柄类型和对象类型差异(书中翻译的不准,type of handdle 和 object) 个人理解:
Transaction tr; 句柄tr类型是transaction 句柄类型:关键字
对象类型:类中成员的类型差异
l 虚方法和多态
多态:多个程序使用一个共同的名字的现象。
多态解决问题:计算机建构面临的一个问题。让物理内存很小的情况下,让处理器能够对很大的地址空间寻址。针对这个问题引入了虚拟内存。
虚拟方法继承劣势:
基类使用了虚拟方法,扩展类也必须使用相同的―签名‖,扩展类中虚拟子程序不能增加或删除参数,这意味着必须提前做好规划。
l 对象复制
1) 因为是virtual 函数,扩展类中copy方法也必须是transaction型的, 但是要copy的是badtr类型的,所以要new一个bad 带有copy 的事物基类。 Class transaction ;
Rand bit[31:0] src,dst,data[8]; Bit[31:0] crc;
Virtual function transaction copy (); Copy = new(); Copy.src = s rc; Copy.dst = dst; Copy.data = data; Copy.crc = crc; Endfunction
Endclass
带有copy的扩展类 Calss badtr extends transaction Rand bit bad_crc;
Virtual function badtr copy(); //错误 Virtual function transaction copy(); Badtr bad; Bad = new(); Bad.src = src; bad.dst = dst; bad.data = data; bad.crc = crc; Bad.bad_crc = bad_crc; Return bad; Rendfunction endclass
2)优化途径一,创建一个独立的函数copy_data,这样每个类只负责copy其局部变量,即扩展类中的copy函数用super.copy_data(tr),代替了基类中变量的复制。代码的重用性提高。P8.22
$cast(bad,tr); //扩展类句柄指向基类句柄
使用的情况: 因为virtual 函数,在继承中,虚拟函数必须和基类中名称和参数也一致。这样扩展类中copy_data函数参数仍然是transaction类型的tr,这样出现了参数是基类句柄,但是copy_data函数内要作的确实扩展类的成员,就要将基类句柄参数赋值给扩展类句柄,
要将扩展类badtr类型的数据返回,所以必须用$cast(bad,tr)。
2) 优化途径二,最好的。前面的copy子程序都会创建一个新对象,改进的一种方法就是指定复制对象的存放地址。
Virtual function transaction copy(transaction to =null);
if(to == null) copy = new(); else copy = to; copy_data(copy); endfunction
l 抽象类和纯虚方法
背景:验证的目标之一是创建多个项目共享的代码。
目的:systemverilog 有两种方法创建共享的基类:抽象类和纯虚方法 Virtual class (抽象类):可以被扩展但是不能被直接例化。
Pure virtual function(纯虚方法):没有实体的方法原型,相当于一个声明。 1) 由抽象类扩展而来的类,只有在所以的虚拟方法都有实体的时候才能被例化, 2) 纯虚方法只能在抽象类中定义。
3)抽象类中,纯虚方法是没实体的,非纯虚方法最好也不写实体。 l 回调
背景:测试平台目的:创建一个不做任何修改就能在所有测试中使用的验证环境。要做到这点的关键是测试平台使用钩子,(什么是钩子?)钩子作用,在不修改原始类的情况下注入新的代码。采用virtual 方法,也可以在扩展类中覆盖基类方法,但是需要重复原方法的所有代码,并且它的修改将传播到它的所有扩展类中。
作用:回调就是一个钩子,在不修改原始类的情况下注入新的代码。
实现:回调任务在顶层中创建,在最低级即驱动器中调用。这样驱动器不需要知道测试的任何信息,它只需要使用一个可以在测试中扩展的通用类。
1) 使用回调注入干扰
回调的一个常见用法就是注入干扰,例如引入一个错误或者延迟。下面测试平台使用回调对象,随机地丢弃数据包。
扩展类是如何作用的?在扩展的回调类中注入错误,如何在驱动器中作用的? 关键是数据队列的作用,驱动器中使用了,回调基类的数据队列
回调基类是抽象类,在扩展的回调类中加入错误注入,而drive驱动类中,是回调基类的数据队列,在环境中将扩展类句柄让入驱动器类,回调基类的数据队列中。
begin // Create error injection callback Driver_cbs_drop dcd = new();
env.drv.cbs.push_back(dcd); // Put into driver
end
与前面扩展类作用的差异?
前面代码,要使扩展类中增加代码,需要使基类句柄指向扩展类句柄。
l 驱动器类: 下面的代码如何解释
2)回调也可以想scoreboard 发送数据或收集功能覆盖率。
优点:你可能想过将scoreboard和功能覆盖数据组置于一个事物处理器中,通过邮箱连接到测试平台中,这是一种笨拙的方法,原因如下:测试平台组件几乎都是被动和异步的,组件只有在测试平台给他数据的时候才被唤醒,而且不会主动地向下游事物处理器传递信息。麻烦:1)这样一个需要同时监视多个邮箱的事物处理器复杂了;2)你可能在多个地方采集数据,但是事物处理器设计用来处理单个数据源回调