1、原始类与扩展类
(1)原始类被称为父类或者超类,扩展类被称为派生类或者子类。扩展类可以直接访问原始类和其本身的所有变量,应该将原始类中的子程序定义成虚拟的,这样它们就可以在扩展类中重定义。new函数无法扩展,SV始终基于句柄类型来调用new函数。
(2)扩展类的构造函数
如果基类构造函数有参数,那么扩展类必须有一个构造函数而且必须在其构造函数的第一行调用基类的构造函数。
(3)关于句柄
指向基类(Transaction)的句柄也可以用来指向派生类(BadTr)的对象。当调用tr.calc_crc函数的时候,到底是调用基类中的虚拟函数,还是调用扩展类中扩展的calc_crc,这取决于tr中的对象类型:如果对象是Transaction类型,那么调用Transaction::calc_crc,否则调用BadTr::calc_crc。
(4)关于约束
如果在扩展类中定义了一个约束,并且扩展后的约束名和基类里的约束名相同,那么扩展类的约束会替代基类中的约束。
2、蓝图
先构建一个对象的蓝图,然后修改它的约束,甚至使用一个扩展对象替换它,然后当你随机化这个蓝图的时候,他就会具有你想赋予的随机值,接着复制这个对象,并将拷贝值发送给下游的事务处理器。此技术出色的地方在于:如果你改变了蓝图对象,你的发生器就会创建一个不同类型的对象。
3*、类型向下转换(downcasting)和虚方法
(1)句柄能够指向一个类的对象或者任何它的扩展类的对象,那么,当一个基类句柄指向一个扩展类对象的时候会发生什么?当你调用一个同时存在于基类和扩展类中的方法的时候将会发生什么?
(2)类型向下转换或者类型变换是指将一个指向基类的指针转换成一个指向派生类的指针。
(3)将一个基类对象拷贝到一个扩展类的句柄会失败,但是将一个基类句柄赋值给一个扩展类句柄并不总是非法的,当基类句柄确实指向一个派生类对象时是允许的,需要用$cast函数转换。
(4)当需要决定调用哪个虚方法的时候,SV根据对象的类型,而非句柄的类型来决定调用什么方法。但是对于非虚方法,SV会根据句柄的类型tr,而不是对象的类型。OOP中多个子程序使用一个共同的名字的现象叫做“多态”。使用虚方法的缺点:一旦定义了一个虚拟的子程序,所有带有该虚拟子程序的扩展类就必须使用相同的签名,例如相同类型和个数的参数。
4、合成、继承和其他替代方法
(1)合成就是在类中嵌套类,将几个小类组合成一个更大的类。
(2)将类层次化的经典OOP方法是根据功能将类划分成易于理解的小块。
(3)合成会导致层次结构变复杂,继承需要额外的代码和设计来处理所有的不同类,而且两者的创建和初始化都很困难。可以考虑创建一个单一的不分层的类,包含所有的变量和子程序,然后通过判别变量去采用不同的约束
5、对象的复制
(1)使用copy()方法:扩展类的虚函数必须跟基类的相匹配,包括所有的参数和返回类型
(2)使用copy_data()方法;
(3)指定复制的目标
6、抽象类和纯虚方法
(1)抽象类(virtual class):可以被扩展但是不能被直接实例化
(2)纯虚(pure virtual)方法:没有实体的方法原型
(3)一个由抽象类扩展而得来的类只有在所有虚方法都有实体的时候才能被例化,纯虚方法只能在抽象类中定义,但是抽象类中也可以定义非纯虚方法。
7、回调
(1)是指使用回调的方法,驱动器“回调”一个在顶层测试中定义的子程序。这项技术的好处在于这种回调子程序可以在每个测试中做不同的定义,这样就可以使用回调来为驱动器增加新的功能而不需要编辑Driver类。
(2)包括前回调(pre_callback)和后回调(post_callback)
(3)回调的一种常见用法是用来注入干扰,例如引起一个错误或者延迟。回调也可以用来向记分板发送数据或者收集功能覆盖率。
8、参数化的类
(1)SV的类参数化近似于C++中的模板。比如一个堆栈就可以引入类参数,使得它支持多种不同数据类型。
(2)在SV中,可以为类增加一个数据类型参数,并在声明类句柄的时候指定类型。例如:class Stack #(type T=int)
9、结论
(1)继承使得现有的类可以在原始类的基础上增加新的功能,并且与之前的设计保有兼容性
(2)可以通过“升级”现有的驱动器类来注入错误以创建一个新的测试,如果驱动器中已有的回调,无须对测试平台的架构做任何改变。
(3)使用OOP技术需要提前做好计划。通过使用虚拟子程序和提供足够的程序回调入口,测试可以在对代码不作任何改变的情况下更改平台的行为。
(4)使用参数化的类
10、示例程序
(1)类定义
package class_define; //事务基类 class Transaction; rand bit[31:0] src,dst,data[8]; bit [31:0] crc; // 基类的构造函数 function new(input bit[31:0] src = 0); this.src = src; $display("construction function of base class"); endfunction // 基类复制函数 virtual function Transaction copy(); copy=new(); copy.src=src; copy.dst=dst; copy.data=data; copy.crc=crc; endfunction 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 function void fun1(); $display("execute non-virtual Transaction::fun1"); endfunction endclass //使用继承来增加一个约束 class Nearby extends Transaction; constraint c_nearby{ dst inside {[src-100:src+100]}; } endclass //扩展的Transaction类 class BadTr extends Transaction; rand bit bad_crc; bit badtr_var1; //扩展类的构造函数 function new(input bit[31:0] src = 0, bit badtr_var1 = 0); super.new(src); this.badtr_var1 = badtr_var1; endfunction // 扩展类的复制函数 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; endfunction 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 function void fun1(); $display("execute non-virtual BadTr::fun1"); endfunction endclass // 发生器 class Generator; mailbox gen2drv; Transaction blueprint; //蓝图 function new(input mailbox gen2drv); this.gen2drv = gen2drv; blueprint = new(); endfunction task run; Transaction tr; forever begin assert(blueprint.randomize); tr=blueprint.copy(); gen2drv.put(tr); end endtask endclass //驱动类 class Driver; mailbox gen2drv; function new(input mailbox gen2drv); this.gen2drv = gen2drv; endfunction task run; Transaction tr; forever begin gen2drv.get(tr); //从发生器获得事务 tr.calc_crc(); //处理事务 //发送事务 end endtask endclass // 环境类 class Environment; Generator gen; Driver drv; mailbox gen2drv; function void build(); //通过构建邮箱、发生器和驱动器来创建环境 gen2drv = new(); gen = new(gen2drv); drv = new(gen2drv); endfunction task run(); fork gen.run(); drv.run(); join_none endtask task wrap_up(); endtask endclass endpackage
(2)测试程序1
import class_define::*; program test8; initial begin Transaction tr; BadTr badtr,badtr2; tr = new(3); badtr = new(3,1); tr.display(); // 调用 Transaction::display() tr.fun1(); // 调用 Transaction::fun1() //badtr = tr; // 错误,不能把基类对象赋给扩展类句柄 //badtr.display(); //错误 tr = badtr; // 正确,可以把扩展类对象赋给基类句柄 tr.display(); // tr指向扩展类对象,调用BadTr::display() tr.fun1(); // 仍然调用 Transaction::fun1(),因为fun1为不是虚函数 //badtr2 = tr; // 报错,虽然tr指向了扩展类对象,需要用$cast $cast(badtr2,tr); // 正确,cast函数会检查句柄所指向的对象类型,而不仅仅检查句柄本身 badtr2.display(); end endprogram
输出:
construction function of base class # construction function of base class # Tr:src=00000003,dst=00000000,crc=00000000 # execute non-virtual Transaction::fun1 # BadTr:bad_crc=0,Tr:src=00000003,dst=00000000,crc=00000000 # execute non-virtual Transaction::fun1 # BadTr:bad_crc=0,Tr:src=00000003,dst=00000000,crc=00000000
(3)测试程序2
import class_define::*; program test8_2; Environment env; initial begin env = new(); env.build(); begin BadTr bad = new(3,1); //以扩展类的bad对象取代蓝图 env.gen.blueprint=bad; end env.run(); env.wrap_up(); end endprogram
输出:
# construction function of base class # construction function of base class