在systemverilog中,可以使用关键字virtual来声明一个类,说明这个类是抽象类。
virtual class abc;
//Class defination
endclass
接下来展示一个抽象类的例子:
//abstract class
virtual class packet;
bit [31:0] addr;
endclass
module virtual_address;
initial begin
packet p;
p = new();
end
endmodule
在上面这个例子中,创建了一个虚拟类的对象,但是一个虚拟类只可以被继承,不可以被实例化,因此编译器会报错。
因此需要对虚拟类进行继承才可以。
//abstract class
virtual class packet;
bit [31:0] addr;
endclass
class extended_packet extends packet;
function void display;
$display("Value of addr is %0d", addr);
endfunction
endclass
module virtual_address;
initial begin
extended_packet p;
p = new();
p.addr = 10;
p.display();
end
endmodule
其输出结果是:
在systemverilog中方法也可以被抽象,可以在方法的前面加上virtual的关键字,,因为方法分为任务和函数两类,因此抽象方法也分为两类:
在虚拟化的方法中,如果基类的句柄指向子类。那么子类的方法句柄将会被赋值给基类句柄。如下所示:
base_class b_c;
extended_class e_c;
假设这两个类都有display的方法,此时我们把b_c = e_c。
那么当我们访问b_c.display()
虚方法体现在当我们把子类句柄赋值给父类句柄时,通过给父类句柄就可以访问子类中重名的方法。
虚拟函数和虚拟任务的声明形式如下:
//vitural function
virtual function function_name;
//function definition
endfunction
//virtual task
virtual task task_namel
// task definition
endtask
下面举一个例子:
class base_class;
function void display;
$display("Inside base_class");
endfunction
endclass
class extended_class extends base_class;
function void display();
$display("Inside extended class");
endfunction
endclass
module virtual_address;
initial begin
base_class b_c;
extended_class e_c;
e_c = new();
b_c = e_c;
b_c.display();
end
endmodule
在上面这个例子中,我们发现在基类中的dispaly方法并没有用virtual关键字修饰,因此尽管父类对象已经指向了子类,但是调用的让然是父类的方法。
但是若是如下代码:
其输出结果是:
程序设计中,不可避免出现重名方法,sv也可以通过域控制不同版本的重名方法。域解析操作符由两个冒号组成,即::
我们通过类域解析操作符,可以在类外访问静态成员,也可以在子类中访问父类的公有public成员和保护protected成员。
//class
class packet;
bit [31:0] addr;
static bit [31:0] id;
function display(bit[31:0] a, b);
$display("Values are %0d %0d", a, b);
endfunction
endclass
//module
module sro_class;
int id = 10;
initial begin
packet p;
packet::id = 20;
p.display(packet::id, id);
end
endmodule
在上面的例子中,一个类中的静态成员被使用::来进行访问。
其输出结果是:
如果一个方法的定义卸载类的外面,那么这个方法就做external method。但是,类内部还是要留个位置,即需要声明该方法,和c++类似,我们要在类内给出该方法的修饰(local/protected/virtual)以及完整的参数列表。在类外实现方法时,我们还要加上域操作符,说明这是哪个类的方法。
//class with extern function
class packet;
bit [31:0] addr;
bit [31:0] data;
//function declaration - extern indicates out of body declaration
extern virtual function void display();
endclass
//function implementation outside class body
function void packet::display();
$display("Addr = %0d Data = %0d", addr, data);
endfunction
module extern_method;
initial begin
packet p;
p = new();
p.addr = 10;
p.data = 20;
p.display();
end
endmodule
其模拟输出结果是:
此外类中和类外的函数声明应该时一样的。否则编译器会报错。
即:
Error-[ECMDSMPD] Mismatched method definition
testbench.sv, 11
External class method definition should match prototype declaration.
Argument names do not match. The signature of function 'packet::display'
should match the corresponding prototype declaration at: "testbench.sv", 7.
typedef被用来为一个类提供前向声明。在一些情况下,这个类需要在类声明前被实例化。其声明形式如下:
typedef class class_name;
如下面一个例子:
//class - 1
class c1;
//using class c2 handle before declaring it
c2 c;
endclass
//class - 2
class c2;
c1 c;
endclass
module typedef_class;
initial begin
c1 class1;
c2 class2;
$display("Inside typedef_class");
end
endmodule
在上面这个例子中,c2 在c1中被实例化,c1在c2中被实例化。因此会导致编译器错误:
可以发现,类c1和c2,成员变量有对方的示例,不可避免出现未声明就实例化。解决办法使用typedef,通过typedeg我们可以声明一个实例化可以在声明前的类。
其仿真结果是: