为什么需要继承?
在面对一系列的设计时,有的类的功能需求是相近的,如果重写,加入一些新的属性方法,显然会增加工作量。因此使用继承就能够很好地发挥原本类的作用,同时拓展其功能。
如何继承类?
首先需要一个基类,然后定义派生类。
/*基类*/
class Transaction;
rand bit[31:0] src,dst,data[8];
bit[31:0] crc;
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
endclass
/*派生类*/
class BadTr extends Transaction;
rand bit bad_crc;
virtual function void calc_crc;
/*调用基类方法*/
super.calc_crc();
if (bad_crc) crc=~crc;
endfunction
virtual function void display(input string prefix="");
$write("%sBadTr:bad_crc=%b,",prefix,bad_crc);
super.display();
endfunction
endclass:BadTr
为什么需要蓝图模式?
可以方便地改变发生器创建的类型对象,扩展类的属性和方法,而又不需要修改发生器代码。原本情况下,立即构建并随机化对象后,后续想扩展类的属性和方法会非常困难。使用蓝图模式,相当于额外拷贝了一份对象,在这个对象上修改合适后,才将其传送出去。
/*这个旧发生器的问题在于,无法把new与randomize分开,导致扩展性及重用性很差*/
class Bad_Generator;
AXI_TR tx;
task run();
repeat(5) begin
tx = new();
if (!tx.randomize()) $fatal(...);
$display("%p", tx);
end
endtask
endclass
/*使用蓝图模式,把一份拷贝放在发生器的new函数内,后面每次随机化tr前,复制一份拷贝,提高了扩展性。使用时,在test类中,为bluepoint赋上扩展类的对象,即可达到扩展的目的*/
class Generator;
AXI_TR blueprint, tx;
function new();
blueprint = new();
endfunction
task run();
repeat(5) begin
tx = blueprint.clone();
if (!tx.randomize()) $fatal(1, "randomize() failed");
$display("%p", tx);
end
endtask
endclass
基类与派生类间的转换
使用$cast可以将派生类指针转换成指向基类的指针;基类句柄可以直接被赋予派生类句柄;如果类的方法没有使用virtual修饰符,SystemVerilog会根据句柄类型,而不是对象类型调用方法。
为什么需要回调?
当想要在一个测试中注入新的错误时,往往需要给原始类加入新的代码。使用回调,则可以为原始类增加一个扩展类,而免去修改原始类。原始类中,回调方法一般没有内容,而在扩展类中在回调方法内编写新的代码,达到扩展功能的目的。所以回调其实就是预留了修改的位置。
为什么需要参数化的类?
参数化的类可以定义一个类来处理多种数据类型。
class generator #(type T=BadTr);/*用T代替数据类型,默认BadTr类*/
T blueprint;
...
endclass
program automatic test;
initial begin
generator #(transaction) gen;
...
end
endprogram