面向对象的编程基础(下)
类的方法
类中的程序也称为方法,也就是在类的作用域内定义的内部task或者function。下例为类Transaction定义了display()方法。System Verilog会根据句柄的类型调用正确的display()方法。
class Transaction;
bit[31:0]addr,crc,data[8];
function void display();
$display("@%0t:TR addr=8h,crc=sh",Stime,addr,crc);
$write("\tdata[0-7]=");
foreach(data[i])$write(data[i]);
$display();
endfunction
endclass
initial begin
t=new();//创建一个Transaction对象
t.display();//调用Transaction的方法
end
类中的方法默认使用自动存储,所以并不需要担心忘记使用automatic修饰符。
在类之外定义方法
在System Verilog中可以将方法的原型定义(方法名和参数)放在类的内部,而方法的程序体(过程代码)放在类的后面定义。
下面是一个如何创建一个块外声明的例子。复制该方法的第一行,包描方法名和参数,然后在开始处添加关键词extern.然后将整个方法移至类定义的后面,并在方法名前加上类名和两个冒号(::作用域操作符)。上例中的类可以如下定义。
//
class Transaction;
bit[31:0]addr,crc,data[8];
extern function void display();
endclass
function void Transaction::display();
$display("@% 0t:Transaction addr=%h,crc=8h", Stime,addr,crc);
$write("\tdata[o-7]=");
foreach (data[i])Swrite(data[i]);
$display();
endfunction
注:常见两个错误:
1、方法的原型定义跟内容不相匹配是一个常见的编码错误。System Verilog要求除了多一个类名和作用域操作符之外,原型定义跟块外的方法定义一致。
2、另一个常见错误是在类的外部声明方法时忘记写类名。这样做的结果是它的作用范围高了一级,当某个任务试图访问类一级的变量和方法的时候,编译器就会报错。
作用域规则
作用域是一个代码块,例如:一个模块,一个程序、任务、函数、类或着begin-end块for 和foreach 循环自动创建一个块,所以下标变重可以作为该循环作用域的局部变量来声明和创建。
SystemVerilog中新增的特性是可以在一个没有名子的begin-end块中声明变量,例如for循环内定义索引变量
类应当在program或者module外的package中定义。这应当是所有测试平台都该遵守的,可以将临时变量在测试平台最内部的某处定义。
如果在一个块内使用了一个未声明的查量,碰巧在程序块中有一个、同名的变量,那么类就会使用程序块中的变量,不会给出任何的警告。
下例例子将类移到一个package中,那么类就看不到程序一级的变量了,由此就不会无意调用到它了。
//将类移入package来查找程序错误
package Mistake;
class Bad;
logic[31:0]data[];
//未定义i,不会被编译
function void display;
for(i=0;i< data.size();i++)
$display("data[%0d]=%x",i,data[i]);
endfunction
endclass
endpackage
this是什么
当使用一个变量名的时候,SystemVerilog将先在当前作用域内寻找,接着在上一级作用域内寻找,直到找到该变量为止。但是如果在类的很深的底层作用域,却想明确地引用类一级的对象呢。下例中“this”将oname赋给类一级变量oname
//使用this指针指向类一级变量
class Scoping;
string oname;
function new(string oname);
this.oname=oname;//类变量oname=局部变量oname
endfunction
endclass
在一个类中使用另一个类
通过使用指向对象的句柄,一个类内部可以包含另一个类的实例。这如同在Verilog中,在一个模块内部包含另一个模块的实例,以建立设计的层次结构。这样包含的目的通常是重用和控制复杂度。
理解动态对象
将对象传递给方法
将对象传递给一个方法的时候可能需要读取对象中的值、修改对象的值,不管是哪一种情形,调用方法的时候,传递的是对象的句柄而非对象
当调用一个带有标量变量(不是数组,也不是对象)的方法并且使用ref关键词的时候,SystemVerilog传递该标量的地址,所以方法也可以修改标量变量的值。如果不使用ref关键词,System Verilog将该标量的值复制到参数变量中,对该参数变量的任何改变不会影响原变量的值。
//将包传送到一个32位总线上
task transmit(Transactiont);
CBbus.rx data<=t.data;
t.stats.startT=$time;
...
endtask
Transaction t;
initial begin
t=new(); //为对象分配空间
t.addr=42; //初始化数值
transmit(t); //将对象传递给任务
end
在上例中,初始化块先产生一个Transaction对象,并且调用transmit任务,transmit任务的参数是指向该对象的句柄。通过使用句柄,transmit可以读写对象中的值。但是,如果transmit试图改变句柄,初始化块将不会得到结果、因为参数上没有使用ref修饰符。(就如同ref传递其他函数参数一样)
这篇笔记参考《system verilog验证》绿皮书整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。