SV之class

class

类包括数据以及操作数据的任务和函数,在类中任务和函数统称为方法。类可以通过对象句柄来动态创建、删除、分配和访问对象。

类的定义方式 

下面的例子中,类包括变量x和两个方法 。

class sv_class;
  //class properties
  int x;
 
  //method-1
  task set(int i);
    x = i;
  endtask
 
  //method-2
  function int get();
    return x;
  endfunction
endclass

类的实例化和对象的创建 

类可以认为是用户定义的一种数据类型,因此可以像定义其他数据类型那样来定义类:

sv_class class_1;

上面的代码相当于是定义了sv_class类的句柄class_1,这个句柄指向sv_class的一个对象。

类中的属性和方法只有创建类的对象之后才能被引用,下面的语句创建了一个对象,并把该对象的句柄赋给了class_1

class_1 = new();

也可以在创建句柄的同时创建对象,并将对象的句柄赋给创建的句柄

sv_class class_1 = new();

创建了对象后就可以引用类中的属性和方法

class_1.set(10); //calling set method to set value 10 to x
$display("Vlaue of x = %0d",class_1.get(););

 下面看一个完整的过程:

class sv_class;
  //class properties
  int x;
 
  //method-1
  task set(int i);
    x = i;
  endtask
 
  //method-2
  function int get();
    return x;
  endfunction
endclass
 
module sv_class_ex;
 sv_class class_1; //Creating Handle
 
  initial begin
    sv_class class_2 = new(); //Creating handle and Object
    class_1 = new(); //Creating Object for the Handle
 
    //Accessing Class methods
    class_1.set(10);
    class_2.set(20);
    $display("\tclass_1 :: Value of x = %0d",class_1.get());
    $display("\tclass_2 :: Value of x = %0d",class_2.get());
  end
endmodule

运行结果如下:

 SV之class

this关键字

this关键字用于引用类的属性。看下面例子:

class packet;
  //class properties
  bit [31:0] addr;
  bit [31:0] data;
  bit        write;
  string     pkt_type;
 
  //constructor
  function new(bit [31:0] addr,data,bit write,string pkt_type);//new是构造函数,用于对象的初始化
    addr  = addr;//方法中局部变量与类的成员变量同名
    data  = data;
    write = write;
    pkt_type = pkt_type;
  endfunction
 
  //method to display class prperties
  function void display();
    $display("---------------------------------------------------------");
    $display("\t addr  = %0h",addr);
    $display("\t data  = %0h",data);
    $display("\t write = %0h",write);
    $display("\t pkt_type  = %0s",pkt_type);
    $display("---------------------------------------------------------");
  endfunction
endclass
 
module sv_constructor;
  packet pkt;
  initial begin
    pkt = new(32'h10,32'hFF,1,"GOOD_PKT");
    pkt.display();
  end
endmodule

运行结果如下:

SV之class 

可以看出,参数输出了自己的默认值,这是由于方法的局部变量的变量名与类的成员变量名相同导致的,解决这一问题的办法就是使用this关键字。

class packet;
  //class properties
  bit [31:0] addr;
  bit [31:0] data;
  bit        write;
  string     pkt_type;
  //constructor
  function new(bit [31:0] addr,data,bit write,string pkt_type);
    this.addr  = addr;//关键字this其实就是类的指针,它指向了类的对象,所以可以通过this.成员变量名来访问类的成员变量
    this.data  = data;//this.data代表类的成员变量,而等号右边的data为方法的局部变量
    this.write = write;
    this.pkt_type = pkt_type;
  endfunction
 
  //method to display class prperties
  function void display();
    $display("---------------------------------------------------------");
    $display("\t addr  = %0h",addr);
    $display("\t data  = %0h",data);
    $display("\t write = %0h",write);
    $display("\t pkt_type  = %0s",pkt_type);
    $display("---------------------------------------------------------");
  endfunction
endclass
 
module sv_constructor;
  packet pkt;
  initial begin
    pkt = new(32'h10,32'hFF,1,"GOOD_PKT");
    pkt.display();
  end
endmodule

输出结果如下:

SV之class 

类的静态属性和静态方法

可以在类的属性和方法名前加上关键字static,这表示静态属性和静态方法。静态属性和静态方法归类所有,表明在内存中单独开辟了一块区域来存储这些静态成员。静态成员可以通过"类名.静态成员名"来访问,而不需要创建对象。

请注意,对于静态方法,它只能访问类的静态变量而不能访问非静态变量,否则会报错。

静态变量一般在定义时初始化,要保证在创建第一个对象之前静态变量就已经完成了初始化。

看下面例子:

class packet;
  //class properties
  bit [31:0] addr;
  bit [31:0] data;
  bit        write;
  string     pkt_type;
  bit        pkt_id;
 
  //static property to keep track of number of pkt's created
  static int no_of_pkts_created;//定义静态变量
 
  //constructor
  function new(bit [31:0] addr,data,bit write,string pkt_type);
    this.addr  = addr;
    this.data  = data;
    this.write = write;
    this.pkt_type = pkt_type;
    //increment pkt count on creating an object
    no_of_pkts_created++;
    pkt_id     = no_of_pkts_created;
  endfunction
 
  //method to display class properties
  function void display();
    $display("---------------------------------------------------------");
    $display("\t addr  = %0h",addr);
    $display("\t data  = %0h",data);
    $display("\t write = %0h",write);
    $display("\t pkt_type  = %0s",pkt_type);
    $display("---------------------------------------------------------");
  endfunction
 
  //static method to display next_pkt id 静态方法
  static function void next_pkt_id;
    $display("---------------------------------------------------------");
    $display("\t no of pkt's created  = %0d",no_of_pkts_created);
    $display("\t next pkt id  = %0d",no_of_pkts_created+1);
    $display("---------------------------------------------------------");
  endfunction
endclass
 
module static_properties_method;
  packet pkt[3];
  packet pkt_2;
 
  initial begin
    foreach(pkt[i]) begin
      pkt[i] = new(32'h10*i,32'hF*i,1,"GOOD_PKT");
      pkt[i].display();
    end
    pkt[1].next_pkt_id;
    //static class properties and methods are accessed without 
    //creating an object for pkt_2 handle.
    pkt_2.next_pkt_id;//调用静态方法
  end
endmodule

运行结果:

 SV之class

 Class Assignment 类的赋值

看下面代码:

 packet   pkt_1;//定义句柄
  pkt_1  = new();//实例化对象
  packet   pkt_2;
  pkt_2  = pkt_1;

由于将pkt_1赋值给了pkt_2,两个变量都是句柄,所以最后两个句柄都指向同一个对象;所以通过pkt_1改变对象的任何属性,都会立即反映到pkt_2上。看下图的解释:

 SV之class

 看下面例子:

class packet;
  //class properties
  bit [31:0] addr;
  bit [31:0] data;
  bit write;
  string pkt_type;
  //constructor
  function new();
    addr  = 32'h10;
    data  = 32'hFF;
    write = 1;
    pkt_type = "GOOD_PKT";
  endfunction
 
  //method to display class properties
  function void display();
    $display("---------------------------------------------------------");
    $display("\t addr  = %0d",addr);
    $display("\t data  = %0h",data);
    $display("\t write = %0d",write);
    $display("\t pkt_type  = %0s",pkt_type);
    $display("---------------------------------------------------------");
  endfunction
endclass
module class_assignment;
  packet pkt_1;
  packet pkt_2;
 
  initial begin
    pkt_1 = new();
    $display("\t****  calling pkt_1 display  ****");
    pkt_1.display();
    //assigning pkt_1 to pkt_2
    pkt_2 = pkt_1;//将句柄1赋值给pkt_2
    $display("\t****  calling pkt_2 display  ****");
    pkt_2.display();
    //changing values with pkt_2 handle
    pkt_2.addr = 32'hAB;
    pkt_2.pkt_type = "BAD_PKT";
 
    //changes made with pkt_2 handle will reflect on pkt_1 通过句柄2改变的属性会反映到句柄1上,因为两个句柄指向同一个对象
    $display("\t****  calling pkt_1 display  ****");
    pkt_1.display();
  end
endmodule

运行结果如下:

SV之class 

Shallow Copy 简单复制对象

 看下面代码:

packet   pkt_1;
pkt_1  = new();
packet   pkt_2;
pkt_2  = new pkt_1;//通过关键字new来复制句柄

请注意,对象并不能复制,只能复制它们的句柄。上面通过关键字new将pkt_1复制给pkt_2,从而创建了一个新的对象,只是这个对象的内容与pkt_1相同,但是两个句柄的值是不同的,这需要注意。

具体的操作情况看下面的图 :

SV之class

可以看出,复制之后改变pkt_2的属性并不会改变pkt_1的属性,即两者之间没有关联。

SV之class

 可以看出采用这种复制方式,只会对最高层次的类的对象进行复制;如果这个类中还包括指向其他类的句柄,那么下层的对象并不会复制。此时,复制之后的两个对象共享一个下层对象,如上图中的pkt_1.ad_r和pkt_2.ad_r位同一个对象。

看下面具体例子:

//-- class ---
class address_range;
  bit [31:0] start_address;
  bit [31:0] end_address  ;
  function new();
    start_address = 10;
    end_address   = 50;
  endfunction
endclass
 
//-- class ---  
class packet;
  //class properties
  bit [31:0] addr;
  bit [31:0] data;
  address_range ar; //class handle 类的句柄
 
  //constructor
  function new();
    addr  = 32'h10;
    data  = 32'hFF;
    ar = new(); //creating object
  endfunction
  //method to display class prperties
  function void display();
    $display("---------------------------------------------------------");
    $display("\t addr  = %0h",addr);
    $display("\t data  = %0h",data);
    $display("\t start_address  = %0d",ar.start_address);
    $display("\t end_address  = %0d",ar.end_address);
    $display("---------------------------------------------------------");
  endfunction
endclass
 
// -- module ---
module class_assignment;
  packet pkt_1;
  packet pkt_2;
 
  initial begin
    pkt_1 = new();   //creating pkt_1 object
    $display("\t****  calling pkt_1 display  ****");
    pkt_1.display();
  
    pkt_2 = new pkt_1;   //creating pkt_2 object and copying pkt_1 to pkt_2 复制对象
    $display("\t****  calling pkt_2 display  ****");
    pkt_2.display();
 
    //changing values with pkt_2 handle
    pkt_2.addr = 32'h68;
    pkt_2.ar.start_address = 60;//修改下层对象的属性
    pkt_2.ar.end_address = 80;
    $display("\t****  calling pkt_1 display after changing pkt_2 properties ****");
 
    //changes made to pkt_2.ar properties reflected on pkt_1.ar, so only handle of the object get copied, this is called shallow copy
    pkt_1.display();
    $display("\t****  calling pkt_2 display after changing pkt_2 properties ****");
    pkt_2.display(); //
  end
endmodule

运行结果如下:

 SV之class

可以看出在pkt_2修改了对象ar中的属性start_address后,pkt_1和pkt_2中的ar的属性都改变了,也就是说这种简单复制并没有将下层对象复制,只是将最高层次的对象进行了复制,此时下层对象为两者共享。

请注意这种简单复制与上面类的赋值的不同,看下图:

SV之class

类的赋值只是重新定义了一个句柄,该句柄与原来的句柄指向同一个对象;而上面的简单复制是重新创建了一个对象,只是这个对象的内容和原来对象的内容相同。

deep copy 深层复制

 对于简单复制,对象并没有复制,复制的只是句柄。为了进行深层复制,需要自定义复制函数,在自定义函数中,将创建新对象,并将所有类属性复制到新句柄,并返回新句柄。

SV之class

看下面这个例子,在这个例子中copy()方法被添加到每一层的类中,具体如下:

//-- class ---
class address_range;//定义下层类
  bit [31:0] start_address;
  bit [31:0] end_address  ;
 
  function new();//构造方法初始化对象
    start_address = 10;
    end_address   = 50;
  endfunction
  //copy method
  function address_range copy;//下层类中的copy函数
    copy = new();
    copy.start_address = this.start_address;
    copy.end_address   = this.end_address;
    return copy;//返回值的类型为该类
  endfunction
endclass
 
//-- class ---  
class packet;
  //class properties
  bit [31:0] addr;
  bit [31:0] data;
  address_range ar; //class handle
 
  //constructor
  function new();
    addr  = 32'h10;
    data  = 32'hFF;
    ar = new(); //creating object  例化下层类的对象,一般在上层类的构造方法中实现
  endfunction
 
  //method to display class prperties
  function void display();
    $display("---------------------------------------------------------");
    $display("\t addr  = %0h",addr);
    $display("\t data  = %0h",data);
    $display("\t start_address  = %0d",ar.start_address);
    $display("\t end_address  = %0d",ar.end_address);
    $display("---------------------------------------------------------");
  endfunction
 
  //copy method
  function packet copy();//上层类中的copy函数
    copy = new();
    copy.addr = this.addr;
    copy.data = this.data;
    copy.ar   = ar.copy;//calling copy function of tr
    return copy;
  endfunction
endclass
// -- module ---
module class_assignment;
  packet pkt_1;
  packet pkt_2;
  initial begin
    pkt_1 = new();   //creating pkt_1 object
    $display("\t****  calling pkt_1 display  ****");
    pkt_1.display();
    pkt_2 = new();   //creating pkt_2 object
    $display("\t****  calling pkt_2 display  ****");
    pkt_2.display();
    pkt_2 = pkt_1.copy(); //calling copy method 调用上层类的copy函数
    //changing values with pkt_2 handle
    pkt_2.addr = 32'h68;//修改赋值后的属性
    pkt_2.ar.start_address = 60;
    pkt_2.ar.end_address = 80;
    $display("\t****  calling pkt_1 display after changing pkt_2 properties ****");
    pkt_1.display();
    $display("\t****  calling pkt_2 display after changing pkt_2 properties ****");
    pkt_2.display();
  end
endmodule

运行结果:

SV之class 

可以看出,包括下层类在内都已经实现了复制,复制之后两个对象在不同的地址中,互不影响。

Parameterised Classes 常数类

 看下面例子:

class packet #(parameter int ADDR_WIDTH = 32,DATA_WIDTH = 32);
  bit [ADDR_WIDTH-1:0] address;
  bit [DATA_WIDTH-1:0] data   ;
 
  function new();
    address = 10;
    data    = 20;
  endfunction
endclass

常数类类似于verilog中的常数module,常数可以用于描述类中的属性,例如上面的位宽。常数值在类例化对象时可以进行修改并覆盖。

packet pkt;  //创建一个对象并且该对象的成员变量固定的位宽

packet #(32,64) pkt;  //用修改后的参数值改变固定的位宽

Classes Inheritance 类的继承 

 新的类可以在已存在的类的基础上被创建,也就是继承。被继承的类为父类,新产生的类为子类,子类可以继承并修改父类的所有属性和方法,也可以创建自己的属性和方法。看下面一个简单的例子:

class parent_class;
  bit [31:0] addr;
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
endclass
 
module inheritence;
  initial begin
    child_class c = new();
    c.addr = 10;
    c.data = 20;
    $display("Value of addr = %0d data = %0d",c.addr,c.data);
  end
endmodule

运行结果如下:

SV之class

Overriding class members 覆盖类的成员

父类的成员可以在子类中修改并覆盖,看下面的例子:

class parent_class;
  bit [31:0] addr;
 
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
  function display();//覆盖父类中的display方法
    $display("Data = %0d",data);
  endfunction
endclass
 
module inheritence;
  initial begin
    child_class c=new();
    c.addr = 10;
    c.data = 20;
    c.display();
  end
endmodule

 SV之class

super关键字

子类可以通过关键字super引用父类中的成员,当父类的成员被子类成员覆盖时,必须用super来访问父类成员。看下例:

class parent_class;
  bit [31:0] addr;
 
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
 
  function display();
    super.display();//获取父类的display方法
    $display("Data = %0d",data);
  endfunction
 
endclass
 
module inheritence;
  initial begin
    child_class c=new();
    c.addr = 10;
    c.data = 20;
    c.display();
  end
endmodule

运行结果:

SV之class 

cast对象转型

 动态转换关键字$cast可以强制将父类句柄(或引用)转换为子类的句柄(或引用)。如果强制转换无效,这是因为所指向的对象的实际类型不是所需子类的类型,则转换失败。

我们来看它的用法:

一般来说将一个子类的句柄赋给一个父类的句柄,这通常是合法的;但是将一个父类句柄赋值给一个子类的句柄,这通常是非法的。这时我们就可以利用关键字$cast将一个父类句柄强制转换为子类句柄。看下面的例子:

class parent_class;
  bit [31:0] addr;
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
  function display();
    super.display();
    $display("Data = %0d",data);
  endfunction
endclass
 
module inheritence;
  initial begin
    parent_class p=new();
    child_class  c=new();
    c.addr = 10;
    c.data = 20;
    p = c;        //将子类句柄赋给父类句柄,这不会有问题
    c.display();
  end
endmodule

SV之class 

class parent_class;
  bit [31:0] addr;
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
 
  function display();
    super.display();
    $display("Data = %0d",data);
  endfunction
endclass
 
module inheritence;
  initial begin
    parent_class p=new();
    child_class  c=new();
    c.addr = 10;
    c.data = 20;
    c = p;        //将父类句柄赋给子类句柄,这会报错
    c.display();
  end
endmodule

SV之class 

可以看出这样直接将父类句柄赋给子类,编译器会报错;

class parent_class;
  bit [31:0] addr;
 
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
  function display();
    super.display();
    $display("Data = %0d",data);
  endfunction
endclass
module inheritence;
  initial begin
    parent_class p;//注意这里并没有实例化对象,只是创建了句柄,注意这里和声明例子的区别
    child_class  c=new();
    child_class  c1;
    c.addr = 10;
    c.data = 20;
    p  = c;        //p 是父类的句柄,但是指向了子类的对象
    c1 = p;        //将父类句柄赋给子类
    c1.display();
  end
endmodule

将父类指向子类对象的句柄赋给子类句柄还是会报错,结果如下:

SV之class 

class parent_class;
  bit [31:0] addr;
 
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
class child_class extends parent_class;
  bit [31:0] data;
 
  function display();
    super.display();
    $display("Data = %0d",data);
  endfunction
endclass
 
module inheritence;
  initial begin
    parent_class p;
    child_class  c=new();
    child_class  c1;
    c.addr = 10;
    c.data = 20;
 
    p = c;         //p 指向子类对象
    $cast(c1,p);   //强制将p转换为子类句柄c1,此时c1指向p指向的对象
 
    c1.display();
  end
endmodule

 SV之class没有报错

Data Hiding and Encapsulation 数据隐藏和封装

 将类中的数据隐藏,并且只可以通过方法来访问的这种技术叫做封装。在默认的情况下,类中的所有成员都可以通过对象的句柄来访问,但是有些情况下这可能会破坏某些类成员的值。为了限制外部对象对类内部成员的访问,可以在成员名前加上前缀:

  • local
  • protected

(1)local

可以通过声明成员为本地成员来避免对类成员的外部访问(父类的对象也不能访问,这种访问也是属于外部访问),任何违规都可能导致编译错误。语法如下:

 local integer x;

看下例:

class parent_class;
  local bit [31:0] tmp_addr;//声明为本地成员
   
  function new(bit [31:0] r_addr);
    tmp_addr = r_addr + 10;
  endfunction
 
  function display();
    $display("tmp_addr = %0d",tmp_addr);
  endfunction
endclass
 
 
//   module
module encapsulation;
  initial begin
    parent_class p_c = new(5);
        
    p_c.tmp_addr = 20;  //修改本地成员是违法的
    p_c.display();
  end
endmodule

SV之class 编译错误,类的本地成员禁止外部访问;

本地成员只能在类的内部访问,看下例:

class parent_class;
  local bit [31:0] tmp_addr;
   
  function new(bit [31:0] r_addr);
    tmp_addr = r_addr + 10;//内部访问本地成员
  endfunction
 
  function display();
    $display("tmp_addr = %0d",tmp_addr);
  endfunction
endclass
 
//   module
module encapsulation;
  initial begin
    parent_class p_c = new(5);
    p_c.display();
  end
endmodule

SV之class 此时没有报错

(2)protected

有时候需要定义一些只能被子类访问的成员(父类的对象也不能访问,这种访问也是属于外部访问),这时需要在这些成员前面加上protected关键字,语法如下:

protected integer x;

 看下面例子:

class parent_class;
  protected bit [31:0] tmp_addr;//受保护的属性
   
  function new(bit [31:0] r_addr);
    tmp_addr = r_addr + 10;
  endfunction
 
  function display();
    $display("tmp_addr = %0d",tmp_addr);
  endfunction
endclass
 
class child_class extends parent_class;
  function new(bit [31:0] r_addr);
    super.new(r_addr);//调用父类的构造方法,一般这需要在子类的第一句声明
  endfunction
   
  function void incr_addr();
    tmp_addr++;//子类访问受保护对象
  endfunction 
endclass
 
//   module
module encapsulation;
  initial begin
    parent_class p_c = new(5);
    child_class  c_c = new(10);
         
    // 父类的对象访问受保护的属性,这是非法的
    p_c.tmp_addr = 10;
    p_c.display();
    
    c_c.incr_addr();  //Accessing protected variable in extended class
    c_c.display();
  end
endmodule

SV之class 在父类对象中访问受保护的属性是违法的,所以报错。

class parent_class;
  protected bit [31:0] tmp_addr;
   
  function new(bit [31:0] r_addr);
    tmp_addr = r_addr + 10;
  endfunction
 
  function display();
    $display("tmp_addr = %0d",tmp_addr);
  endfunction
endclass
 
class child_class extends parent_class;
  function new(bit [31:0] r_addr);
    super.new(r_addr);
  endfunction
   
  function void incr_addr();
    tmp_addr++;//通过子类来访问受保护的属性
  endfunction 
endclass
 
//   module
module encapsulation;
  initial begin
    child_class  c_c = new(10);
     
    c_c.incr_addr();  //Accessing protected variable in extended class
    c_c.display();
  end
endmodule

 运行结果:

SV之class

Abstract Classes and Virtual Methods 抽象类和虚方法

在类名前加上关键字virtual的类称为抽象类,抽象类不能实例化,只能被继承。语法如下:

virtual class packet;

抽象类中可以有虚方法,虚方法是一种基本的多态构造,它覆盖所有父类中的方法。 

//abstract class
virtual class packet;
  bit [31:0] addr;
 
  function display();
    $display("Addr = %0d",addr);
  endfunction
endclass
 
module virtual_class;
  initial begin
    packet p;
    p = new();//实例化抽象类会报错
    p.display();
  end
endmodule

 SV之class

Scope Resolution Operator ::  作用域操作符

类作用域操作符:: 用于唯一地标识特定类的成员 ,允许从类的外部访问·,以及在子类中访问public或protected成员。

//class
class packet;
         bit [31:0] addr;
  static bit [31:0] id;
 
  function display(bit [31:0] a,b);
    $display("Vlaues are %0d %0d",a,b);
  endfunction
endclass
 
module sro_class;
  int id=10;
  initial begin
    packet p;
    p = new();
    packet::id = 20;//访问静态成员
    p.display(packet::id,id);
  end
endmodule

SV之class

Extern Methods 外部方法

方法的方法体可以在类的外部定义;此时需要在类内部定义完整的方法名和参数列表以及加上关键字extern;在类的外部需要在方法名前加上类名和类作用域操作符。具体看下例:

//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

SV之class 

typedef class  定义类

有些情况下某个类需要在它被定义之前声明,此时因为编译器还不认识这个新的类就会报错,这种情况下就需要用到关键字typedef来声明这个类名。看下例:

typedef class c2;//声明类c2
//class-1
class c1;
  c2 c;    //using class c2 handle before declaring it.
endclass
 
//class-2定义类c2
class c2;
  c1 c;
endclass
  
module typedef_class;
  initial begin
    c1 class1;
    c2 class2;
  end
endmodule

 

上一篇:P2365 任务安排 / [FJOI2019]batch(斜率优化dp)


下一篇:sv函数中返回队列