资料:
-UVM Primer(英文原版)
-UVM Primer(中译版)
前言:
-本部分将包括UVM Primer的第4章-第9章,仅整理OOP特性,不会涉及DUT及验证平台构建的相关内容。
正文:
本书的4-9章以一个动物园为例,讲述了OOP的不同特性,包括:
-类
-继承
-多态
-静态变量和方法
-factory模式
第四章:OOP概述
OOP的优点:
-代码重用:
较易理解,由于方法封装在类中,如果设计或者用例发生或需要更改(这是常有的事),那么只需要对类内部进行调整;此外,如果产生了功能的拓展,代码也无需做较大改动;
-代码维护
与上一条类似,如果一个项目中的代码由多个人完成,各写各的会导致代码的同步出现问题,而统一在封装好的类内进行操作,能够避免上述问题;
-内存管理
应结合实际用力进行理解,在此不赘述;
第五章:类与继承
1、结构体和类
常见的结构体如下:
// A Rectangle Sturct
typedef struct{
int width;
int length;
} rectangle_struct;
// A Square Sturct
typedef struct{
int side;
} square_struct;
我们在模块中可以例化出这两个图形,并使用其中的信息进行运算:
module top_struct;
// 2 Figure
rectangle_struct rectangle_s;
square_struct square_struct;
// Calculation
initial begin
rectangle_s.width = 10;
rectangle_s.length = 20;
display("Rectangel area = %0d",rectangle_s.width * rectangle_s.length);
end
endmodule
但在本例中,结构体也存在一些局限性,包括:
-两个结构体之间存在关系却无法体现;
(Though we know there is a relationship between rectangles and squares, our
program does not .)
-定义变量时仿真器就会为其分配内存;
-计算面积的操作需要了解长方体的面积公式;
因此,我们使用类(CLASS)来解决上述的问题:
class rectangle
int width;
int length;
// Explicit Allocate Memory
function new(int w, int l);
length = l;
width = w;
endfunction
// Calculate Area
function int area();
return length * width;
endfunction
endclass
类中含有不同的元素:
-We have declared class variables called length
and width
. In object-oriented terms
these are called the class's data members.
-We call a function or a task defined within a class a method.
此外,编译器不会自动为类分配内存,因此用户必须调用类的 new()构造函数来为对象显式分配内存。
在定义好了长方形这个类之后,我们可以在顶层模块中例化和使用:
module top_class;
// A handle
rectangle rectangle_h;
initial begin
rectangle_h = new(.l(50), .w(20));
// Method in class
display("Area = %0d", rectangle_h.area());
end
endmodule
这样,我们在计算面积时就无需知道矩形的面积,而是直接使用类内部的方法即可。
在上面的代码中,我们使用句柄(handle)指向一个矩形类。SV中的句柄和C中的指针类似,但是功能具有一些差别,包括:
1、句柄只能指向类例化出的对象,而指针对其指向的数据类型没有限制;
2、句柄不能做数值计算操作,而指针可以(*p++);
3、句柄需要使用$cast
操作以进行类型转换,而指针更加灵活;
4、当对象没有被句柄指向时,其对应的内存将会被回收(严格来说不应该放在句柄的知识里);
Anyway,在这部分中,我们成功定义、例化了一个类,采用句柄指向这个类,并使用了类内部的方法计算了面积;
2、类的继承
下面我们来看看类的另一个特性,继承。
// A Rectangle Sturct
typedef struct{
int width;
int length;
} rectangle_struct;
// A Square Sturct
typedef struct{
int side;
} square_struct;
现在我们知道,我们可以将两个结构体变成两个类:长方形和正方形,并将他们各自的计算面积公式作为方法进行封装,但我们知道,正方形就是特殊的长方形,这种联系可以作为分类的基础,对正方形进行进一步细分,我们使用extends
关键字进行重用:
class square extends rectangle;
// Override New Function
function new(int side);
super.new(.l(side), .w(side));
endfunction
endclass
我们使用extends
关键词定义了square类,编译器也会通过extends
关键字得知,square将会继承父类rectangle的所有数据成员和方法。接着,我们重载(Override)了父类中的new()
函数,super关键字将会指示编译器使用rectangle中的构造函数,并向长和宽均传入同一个值side。
这样,我们可以继续完善之前的代码了:
module top_class;
rectangle rectangle_h;
// A New Handler
square square_h;
initial begin
rectangle_h = new(.l(50), .w(20));
display("Area Rectangle = %0d", rectangle_h.area());
// A New Object
square_h = new(.side(25));
display("Area Square = %0d", square_h.area());
end
endmodule
尽管在这段代码中,我们两次调用了new()
函数,但是编译器能够知道,这是两种分属不同对象的方法。