目录
1.类(class)
类是成员变量和成员方法的载体,一个类的功能应该尽可能简单,不应当承担过多的职责,更不应该承担不符合它的职责,这在设计模式中称之为单一职责原则(SRP Single Responsibility Principle)
1.1验证中为什么需要面向对象(OOP)?
在Testbench包含但不限于以下组件:
①激励生成器(stimulus generator):生成激励内容
②驱动器(driver):将激励以时序形式发送至DUT
③监测器(monitor):监测信号并记录数据
④比较器(checker):比较数据
而对于同一验证环境中,不同组件的功能和数据是不一样的;不同验证环境中,同一类型的组件的功能和数据是相似的(这提供了继承的可能性,使新的项目组件可以继承旧的项目组件)。而面向对象编程和验证世界的构建原则十分符合,所以我们需要OOP。
1.1.1 OOP的三大特性
1.封装:封装就是把属性和方法封装在一个class中
2.继承:一个类型获取另外一个类型的属性的方式;父类可以生成很多子类,子类可继承父类所有的属性和方法。
3.多态:同一个行为具有多个不同表现形式或形态的能力。
1.2术语
Class类:包含变量(属性)和子程序(方法)的基本构建块,就像Verilog中的Module一样,只不过Module属于硬件盒子,而class属于软件盒子(class就像一张图纸)。类可以定义在任何地方,既可以定义在program/module/package中,也可以定义在这之外的任何地方甚至单独的一个文件中。
Object对象:类通过实例化成为一个对象;类似于Verilog中的module例化,只不过那是硬件的例化,在SV中使用class例化,这属于软件的例化。(通过class实例化出的许多对象就像根据图纸建造出的许多房子)
Handle句柄:指向对象的指针;在SV的对象索引时,需要通过句柄来索引对象 的变量和方法。(句柄就像每个房子的地址,但句柄是指针,不是地址!)
Property属性:存储数据的变量
Method方法:类中使用task或者function来定义方法以便处理自身或者外部传入的数据。
通过以下的代码可以对上述术语有更直观的理解:
class Transaction;//定义一个类
//在实例化类的时候为变量分配空间和初始化,当然初始化值可在new构造函数中指定
logic [31:0] addr;//类的属性
logic kind;
logic [63:0] data;
function new(logic [31:0] addr);//类的方法,每个类的new函数也叫构造函数,因为在示例化这个类的时候,会通过new的方式来实例化,其实调用的就是这个函数,可以在这个函数中添加一些初始化的信息
this.addr = addr;
this.kind = 'x;
this.data = 'x;
endfunction
function void display();//类的方法
$display(“addr=%h, kind=%b, data=%h”,addr, kind, data);
endfunction
endclass
program class;
Transaction tr_gen;//声明一个句柄(指针),此时是一个空指针,系统也没有为其开辟物理内存
Transaction tr_drv;//为此类声明另一个指针
Transaction tr[10];//句柄数组
initial begin
tr_gen = new(32’b7788);//通过new创建一个对象obj1,并让上述的空指针指向这个对象,此时系统为该对象开辟物理内存
tr_drv = new(32’b8877);//再创建一个新的对象obj2
//通过以上两步后才能使用对象
foreach(tr[i])begin//将句柄数组中的每一个句柄实例化
tr[i]=new;
end
tr_gen.kind = 1’b1;//通过句柄为obj1的属性kind赋值
tr_drv.data = ’b0;//通过句柄为obj2的属性data赋值
tr_gen.display();//通过句柄调用obj1的display函数
tr_drv.display();//通过句柄调用obj2的display函数
end
endprogram
2.类的成员操作
2.1类的成员
类作为载体,也具备了天生的闭合属性,即将其属性和方法封装在内部, 不会直接将成员变量暴露给外部,通过protected和local关键词来设置成 员变量和方法的外部访问权限
类的成员有属性和方法。类的功能应该尽可能的单一,方便复用和维护。对于类的变量(属性)来说,分为public/protected/local三种访问类型的变量:
1.如果没有指明访问类型,那么成员的默认类型是public,子类和外部均可以访问成员,随意使用
2.如果指明了访问类型是protected,那么只有该类或者子类可以访问成员,而外部无法访问
3.如果指明了访问类型是local,那么只有该类可以访问成员,子类和外部均无法访问
以local为例看下列代码:
class clock
local bit is_summer = 0;//local变量只能在当前类中操作,子类和外部没有权限
local int nclock = 6;
function int get_clock();
if(!is_summer)
return this.nclock;
else
return this.nclock+1;
endfunction
function bit set_summer(bit s);
this.is_summer = s;
endfunction
endclass
function print_data(string message);
if(message=="is_summer")
$display("is_summer=%0d",this.is_summer);
else
$display("nclock=%0d",this.nclock);
endfunction
program example;
clock ck;
initial begin
ck = new();
$display(“now nclock is %0d”,ck.get_clock());//通过内置函数访问
ck.is_summer=1;//无法通过外部直接改变,会编译错误
ck.set_summer(1);//通过内置函数改变
$display(“now nclock is %0d”,ck.get_clock());
$display(“now nclock is %0d”,ck.nclock);//通过外部不能访问,会编译错误
ck.print_data("nclock");//通过类的内置打印函数访问
end
endprogram
编译错误提示信息:
可见如果直接访问local中的变量,编译器会报错,如果通过类中定义的print函数访问,则可以。
2.2定制构造函数
当我们将创建对象,也就是让句柄指向对象的时候,通常会这么写:
Trans t1;
t1=new();
其中的new()函数,也称构造函数,若定义的类中没有自己构建new函数,则调用new函数的时候则是系统的new函数;当然,我们可以通过自定义new函数将初始化的默认值改成想要的值:
class Transaction;
logic [31:0] addr,crc,data[8];
function new(logic[31:0]a=3,d=5);//当外部new的时候,由于这里自定义了new函数在,则外部使用的是这个new函数
addr=a;
foreach(data[i]) data[i]=d;
endfunction
endclass
initial begin
Transaction tr;
tr=new(10);//a=10,d=5,调用谁的new函数取决于new函数左边的句柄类型
end
tr = new(.a(10));//a=10,d uses default of 5 tr=new(10),等价于上面的
一般在搭建验证环境的时候,除了用new函数修改默认值之外,类中自定义的new函数还用来连接接口或者信箱的,具体可见: SV小项目—异步fifo的简单验证环境搭建(全)
2.3静态变量
class Transaction;
static int count=0;
int id;
function new();
id=count++;//id首先使用count=0的值,再++
endfunction
endclass
program example;
Transaction t1,t2;
initial begin
t1=new;//第一个实例,id=0,count=1
t2=new;//第二个实例,id=1,count=2
end
endprogram
可见不管创建多少个object,静态变量count只有一个,可以认为count是保存在类中而非object中的,而id不是静态的,所以每个object都有自己的id变量
一般我们可以通过句柄。的方式引用类中的变量,对于静态变量也可以通过类作用域操作符的方式引用:
class Transaction;
static int count=0;//在定义时初始化而不能在new中初始化
······
endclass
program example;
run_test();
$display("%d transaction were created",Transaction::count);//引用静态句柄
endprogram