1.概述
面向对象编程使用户能够建立复杂的用户类型,将他们跟使用这些数据类型的程序紧密地联系在一起,用户可以在更加抽象的层次上建立测试平台和系统级模型,通过调用函数来执行一个动作而不是改变电平信号。测试平台和设计细节分来,提高复用和健壮性。
基本概念说明:
class :包含变量和子程序的基本构建块;
object:类的一个实例;
handle:指向对象的指针;
property:存储数据的变量,对应verilog中的reg和wire;
method:任务或者函数中操作变量的程序性代码,对应verilog中的initial和always部分,以及函数和任务;
prototype:程序;
构造函数:分配内存,初始化变量,在默认情况下,它将变量设置为默认数值,二值变量为0,四值变量为Z,通过在类中定义new()函数自定义初始变量,但不能有返回值,因为构造函数总是返回一个指向类对象的句柄,其类型就是类本身;
2.类中的静态变量:
在SV中,可以在一个类中创建一个静态变量,这个变量将被这个类的所有实例所共享,并且共享范围仅限于这个类。用static来声明变量,要在声明时初始化,不可以用构造函数:
class transactiong ;
static int count=0;
int id;
endclass;
transactiong tr1,tr2;
id 不是静态变量tr1 tr2都有自己的id,count是静态变量,所有对象只有一个count变量;无需句柄也可以使用静态变量,例如 transaction::count
3.静态方法:
可以在类中创建静态方法,用于读写静态变量,设置可以再第一个实例产生之前读写静态变量。sv不允许静态方法读写非静态变量;
eg.
classTransaction;staticConfig cfg;static int count=0;intid;static function voiddiaplay_static();
$display("",cfg.mode.name(),count);
endfunction;
endclass;
Config cfg;
initial begin
cfg=new(MODE_ON);
transaction::cfg=cfg;
transaction::display_static();
end
4.类之外定义方法:
使用extern,并在方法名前加类名和::
extern
classtransaction;extern function voiddisplay();
...
endclass;
functionvoidtransaction::display();
....
endfunction
5.作用域:
类应当在porgram或者module外的package中定义。这是所有测试平台应当该遵守的,将类移入package中,那么类就看不到program中的变量,避免错误的调用。
this用于引用类一级的变量,this.oname
6.在类中引用另一个类的举例:
statistics
classstatistics;
time startT,stopT;static int ntrans=o;static time total_elapsed_time=0;
function time how_log;
how_long=stopT-startT;
ntrans++;
total_elapsed_time+=how_long;
endfunction
functionvoidstart;
startT=$time;
endfunction
endclassclassTransaction;
bit[31:0] addr,crc,data[8];
statistics stats;
functionnew();
stats=new();
endfunction
task create_packet();
...
stats.start();
endtask;
endclass;
7.动态对象(??):
用ref修饰参数,则参数传递的是地址,若无ref,传递的是复制后的值,方法内部对参数的修改不会被调用参数的代码看到。方法的参数是句柄时,句柄前有ref,传递的是句柄的地址,可以再方法中new()该句柄的对象;无ref,传递是句柄,在方法中不能new()该句柄的对象。
8 对象的复制:
(1).简单复制,使用new操作符,不会调用构造函数,所以只有最高一级的对象被复制。
dst=new src;
(2).简单类的复制函数
View Code
classTransaction;
bit [31:0] addr,crc,data[8];
function Transacton copy;
copy=new();
copy.addr=addr;
copy.crc=crc;
copy.data=data;
endfunction
endclass
(3)深层复制函数,要为层次结构中每个类增加一个copy方法:
copy
classtransaction ;
bit [31:0] addr,data[8],crc;
statistics stats ;static int count=0;intid;
functionnew;
stats=new();
id=count++;
endfunction;
functon transation copy ;
copy=new;
copy.addr=addr;
copy.data=data;
....
copy.stats=stats.copy()
9.继承:
继承允许从一个现存的类得到一个新的类,并共享其变量和子程序。原始类被称为基类或超类,新类扩展了它基类的功能,被称为扩展类。基类中的程序定义为virtual,可以再需要的时候重新定义,在扩展类中用super调用基类中的函数,但是不允许supe.super的调用方式。new函数不可以重新定义。一个简单的例子如下:
eg
classtransaction;
rand bit [31:0] src,dst,data[8];
bit[31:0] crc;virtual function voidcalc_crc;
ctc=src^dst^data^.xor;
endfunction;virtual function void display(input string prefix="");
$display (..);
endfunction
enclassclassBadTr extends transaction;
rand bit bad_crc;virtual function voidcalc_crc;
super.calc_crc();if(bad_crc)crc=~crc;
endfunction
endclass:BadTr
若基类的构造函数含有参数,那么扩展类必须有一个构造函数而且必须在其构造函数的第一行调用构造函数:指向基类的句柄也可以指向扩展类的对象,这点在错误注入方面极为重要。
View Code
classEX extends Base1;
functionnew (input int var);
super.new(var);
...
endfunction
10.蓝图模式(blueprint):
不同于构建并立即使用一个对象不同,将对象的构建和随机化分离:先构建一个对象的blueprint,修改约束,甚至可以用一个扩展对象替换它。接着复制这个对象,并把它的拷贝发送给下游的事务处理器。蓝图对象在一个地方new,在另一个地方run.简单的示例如下:
blueprint_generator
blueprint_env
test1
View Code
使用参数化的类,可以使该蓝图更具通用性,产生任何随即对象:
View Code
class Generator # (type T=BaseTr);
mailbox gen2drv;
T buleprint;
functionnew(input mailbox gen2drv);this.gen2drv=gen2drv;
blueprint=new();
endfunction;
task run ();
T tr;
forever begin
assert(blueprint.randomize);
tr=blueprint.copy();
gen2drv.put(tr);
end
endtask
endclass
program automatic test;
intital begin
Generator # (Transaction) gen;
mailbox gen2drv;
gen2drv=new(1);
gen=new(gen2drv);
fork
gen.run();
repeat (5) begin
transaction tr;
gen2drv.peak(tr);
tr.display();
gen2drv.get(tr);
end
join_any;
end
endprogram
11.类型向下转换:
将一个派生类句柄赋值给一个基类句柄,并不需要任何特殊的代码(参考在蓝图下注入错误的例子)但是将一个基类对象拷贝到一个扩展类的句柄中,操作可能会失败,因为有些属性仅存在基类中。使用$cast检查赋值是否正确。
bad=new();
tr=bad;
若把$cast当做任务,$cast(bad,tr) 不匹配会产生错误报告;
若把$cast当做函数,正确匹配时返回非零;
12.虚方法与多态:
在调用虚方法时,SV根据对象的类型,而非句柄的类型来决定调用什么方法;
如果没有定义为虚方法,则根据句柄判断方法;
多态即为OOP中多个子程序使用一个共同的名字的现象叫做多态;
签名,一旦定义了虚拟的子程序,所有带有该子程序的扩展类必须使用相同的签名,即相同类型和个数的参数。
13。对象的复制,参考233~
14.使用纯虚方法的抽象类,可以声明其句柄,但是不能创建对象。需要先扩展该类,并对所有纯虚方法提供具体实现;
15.回调:
通过虚拟子程序和程序回调入口,测试可以在dui代码不做任何改变的情况下更改测试平台的行为,留下一个“钩子”,通过它测试可以自己增加行为.增加回调任务无需修改源代码的构成,回调一种常见用法是用来注入干扰,例如引起一个延迟或错误,也可以用来向记分板发送数据或是收集功能覆盖率。
回调基类是一个抽象类,使用前必须先进行扩展。
cbs
virtual classDrive_cbs;virtual task pre_tx(ref transaction,refbit drop);//默认情况下无动作
endtaskvirtual task post_tx(ref transaction,refbit drop);//默认无动作
endtask
endclass
使用回调的驱动类,设下钩子;
drv
classDriver;
Driver_cbs cbs[$];
task run();
bit drop;
transaction tr;
forever begin
drop=0;
agt2drv.get(tr);foreach(cbs[i]) cbs[i].pre_tx(tr,drop);if(!drop) continue;
transmit(tr);foreach(cbs[i])cbs[i].post_tx(tr);
end
endtask
endclass
对基类扩展,注入缺包错误;
extend
classDriver_cbs_drop extends Driver_cbs;virtual task pre_tx(ref transaction,refbit drop);
drop=(($urandom_range(0,99)==0);
endtask;
endclass;
program automatic test;
Environment env;
intital beign
env=new();
env.gen_cfg();
env.build();
begin
Driver_cbs_drop dcd=new();
env.drv.cbs.push_back(dcd);//把实例化的扩展积累放入队列中去
end
env.run();
env.wrap_up();
end
endprogram
15.1 使用回调调用记分板:
首先给出用于原子事物的记分板
scoreboard
classScoreboadt;
Transaction scb[$];
functionvoidsave_expect(inout transaction tr);
scb.push_back(tr);//保存期望的事物队列
endfunction
functionvoidcompare_actual(input transaction tr);intq[$];
q=scb.find_index(x)with (x.src==tr.src); //查找队列中匹配的元素
case(q.size)0:$display ("no match");1:scb.delete(q[0]);default:
$display("error multi match");
endcase
endfunction;
endclass
extend
classDriver_cbs_scoreboad extends Driver_cbs;
Scoreboard scb;virtual task pre_tx(ref transaction,refbit drop);
scb_save_expect(tr);//加入回调事件
endtask
functionnew(inout Scoreboard scb);this.scb=scb; //复制对象
endfunction;
endclass
program automatic test;
Environment env;
initial beign
env=new();
env.gen_cfg();
env.build();
begin
Driver_cbs_scoreboard dcs=new(env.scb);
env.drv.cbs.push_back(dcs);//放入驱动器队列
end
env.run();
env.wrapup;
end
endprogram