zoukankan      html  css  js  c++  java
  • SV -- Class 类

    SV -- Class 类

    0. 基础

    定义: class name;
    实例化: .new()
    类中可以包含function, task

    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
    
    • 当类内的成员函数的输入变量跟类内的成员变量同名时,会有歧义,可以使用this.来指定类的成员变量

    1. static

    可以指定类的成员变量或函数任务为静态。
    多个类的实例共享静态变量。

    class packet;
       
      //class properties
      byte packet_id;
         
      //static property to keep track of number of pkt's created
      static byte no_of_pkts_created;
       
      //constructor
      function new();
        //incrementing pkt count on creating an object
        no_of_pkts_created++;
        packet_id = no_of_pkts_created;
      endfunction
       
      //method to display class prperties
      function void display();
        $display("--------------------------------------");
        $display("	 packet_id  = %0d",packet_id);
        $display("--------------------------------------");
      endfunction
    endclass
     
    module static_properties;
      packet pkt[3];
     
      initial begin
        foreach(pkt[i]) begin
          pkt[i] = new();
          pkt[i].display();
        end
      end 
    endmodule
    

    输出:

    --------------------------------------
    packet_id  = 1
    --------------------------------------
    --------------------------------------
    packet_id  = 2
    --------------------------------------
    --------------------------------------
    packet_id  = 3
    --------------------------------------
    

    注意:

    • 如果是静态function,则调用的变量也需要是静态的。
    • 静态的变量和function可以直接通过句柄访问,无需实例化

    2. shallow copy

    语法:

    packet   pkt_1;
    pkt_1  = new();
    packet   pkt_2;
    pkt_2  = new pkt_1; //shallow copy
    

    浅复制会复制所有类成员,开辟新的存储空间。但类中的类(内部类)不会被复制,而是会共享同一个内存空间:

    相比于类的赋值:

    类的赋值只是将句柄指向实例,所以内存空间是共享的,改变其中一个会作用到另一个身上。而浅赋值则不会。

    3. deep 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 = 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("	 addr  = %0h",addr);
        $display("	 data  = %0h",data);
        $display("	 start_address  = %0d",ar.start_address);
        $display("	 end_address  = %0d",ar.end_address);
        $display("---------------------------------------------------------");
      endfunction
     
      //copy method
      function packet 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("	****  calling pkt_1 display  ****");
        pkt_1.display();
        pkt_2 = new();   //creating pkt_2 object
        $display("	****  calling pkt_2 display  ****");
        pkt_2.display();
        pkt_2 = pkt_1.copy(); //calling copy method
        //changing values with pkt_2 handle
        pkt_2.addr = 32'h68;
        pkt_2.ar.start_address = 60;
        pkt_2.ar.end_address = 80;
        $display("	****  calling pkt_1 display after changing pkt_2 properties ****");
        pkt_1.display();
        $display("	****  calling pkt_2 display after changing pkt_2 properties ****");
        pkt_2.display();
      end
    endmodule
    

    输出:

    **** calling pkt_1 display ****
    ---------------------------------------------------------
    addr = 10
    data = ff
    start_address = 10
    end_address = 50
    ---------------------------------------------------------
    **** calling pkt_2 display ****
    ---------------------------------------------------------
    addr = 10
    data = ff
    start_address = 10
    end_address = 50
    ---------------------------------------------------------
    **** calling pkt_1 display after changing pkt_2 properties ****
    ---------------------------------------------------------
    addr = 10
    data = ff
    start_address = 10
    end_address = 50
    ---------------------------------------------------------
    **** calling pkt_2 display after changing pkt_2 properties ****
    ---------------------------------------------------------
    addr = 68
    data = ff
    start_address = 60
    end_address = 80
    ---------------------------------------------------------
    

    5. parameterized class

    类似参数化的module.

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

    实例化的时候packet #(32,64) pkt;

    也可以将一个变量类型作为class的paramter传入:

    class packet #(parameter type T = int);
      T address;
      T data   ;
     
      function new();
        address = 10;
        data    = 20;
      endfunction
    endclass
    

    实例化的时候packet #(bit [31:0]) pkt;

    6. 继承

    子类继承父类的所有成员变量和方法。子类中可以使用父类定义的变量和方法。

    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
    

    7. 多态(polymorphism)

    父类的句柄指向子类实例时,此时调用该句柄的方法实际上只会调用父类的方法,除非使用virtual关键字。将父类中的方法定义为virtual,则指向子类的父类句柄就会根据所指向的子类调用子类方法,这一特性表现为超类句柄的多态

    // base class
    class base_class;
      virtual function void display();
        $display("Inside base class");
      endfunction
    endclass
     
    // extended class 1
    class ext_class_1 extends base_class;
      function void display();
        $display("Inside extended class 1");
      endfunction
    endclass
     
    // extended class 2
    class ext_class_2 extends base_class;
      function void display();
        $display("Inside extended class 2");
      endfunction
    endclass
     
    // extended class 3
    class ext_class_3 extends base_class;
      function void display();
        $display("Inside extended class 3");
      endfunction
    endclass
     
    // module
    module class_polymorphism;
     
      initial begin
         
        //declare and create extended class
        ext_class_1 ec_1 = new();
        ext_class_2 ec_2 = new();
        ext_class_3 ec_3 = new();
         
        //base class handle
        base_class b_c[3];
         
        //assigning extended class to base class
        b_c[0] = ec_1;
        b_c[1] = ec_2;
        b_c[2] = ec_3;
         
        //accessing extended class methods using base class handle
        b_c[0].display();
        b_c[1].display();
        b_c[2].display();
      end
     
    endmodule
    

    输出:

    Inside base class 1
    Inside base class 2
    Inside base class 3
    

    上面例子中使用了基类句柄指向派生类,此时调用display方法则是使用指向的派生类中的方法。
    同样,直接调用派生类的display方法也可以得到正确结果,但是这只是体现了子类对父类函数的修改,并不是多态的概念。

    8. 修改成员函数

    上面多态的例子其实已经提到了,如果希望子类有独特的自己的父类同名方法,只需要在子类中重新定义该方法,这样实例化子类后调用的方法即为子类重定义的方法。无论父类中该方法是否使用virtual关键字。
    所以这里再次强调下,多态只是针对父类句柄而言的。当这类句柄配合virtual关键字后,指向不同的子类可以调用不同的方法。

    9. super

    如果在子类中重定义了父类发成员函数或变量,此时还想使用父类的成员函数,则使用super.function实现

    10. casting

    主要分为静态转换和动态转换:

    • 静态cast: i_a = int'(2.1 * 3.2);
    • 动态cast:
      • 动态类型转换用于将超类指针(引用)安全地转换为类层次结构中的子类指针(引用)
      • 动态转换在运行过程中检查,而静态转换是编译时检查
      • 语法: $cast(destination, source)
        例子:
    class parent_class;
     ...
    endclass
     
    class child_class extends parent_class;
    ...
    endclass
     
    module inheritence;
      initial begin
        parent_class p=new();
        child_class  c=new();
        c.addr = 10;
        c.data = 20;
        p = c;        //assigning child class handle to parent class handle
        c.display();
      end
    endmodule
    

    子类句柄赋值给父类是可以的。
    但是如果换一下:

    module inheritence;
      initial begin
        parent_class p=new();
        child_class  c=new();
        c.addr = 10;
        c.data = 20;
        c = p;        //assigning child class handle to parent class handle
        c.display();
      end
    endmodule
    

    父类句柄赋值给子类会报编译错误。
    这也会导致下面这个例子不通过:

    module inheritence;
      initial begin
        parent_class p;
        child_class  c=new();
        child_class  c1;
        c.addr = 10;
        c.data = 20;
        p  = c;        //p is pointing to child class handle c.
        c1 = p;        //type check fails during compile time.
        c1.display();
      end
    endmodule
    

    p作为父类句柄,可以指向子类,而下面第二个子类句柄c1指向父类句柄p(实际指向的是c),按理说这样的赋值是没问题的。
    但是在编译过程,这样的赋值不会通过,要让他通过,需要改成:
    $cast(c1,p);
    将c1强制转换成p。这一语句相当于跳过了编译过程中对该处的报错,而转到运行过程中检查错误。只要运行过程中,p确实可以被赋值给c1(p已经指向c),则不会报错。

    11. 公有和私有

    在SV中,所以成员都是公有的,除非标记为local或者protected。应该尽量使用默认值。

    • local: local bit [31:0] tmp_addr;
      • 只供类内部访问
      • 子类也无法访问
    • protected: protected bit [31:0] tmp_addr;
      • 不能供外部访问
      • 子类可以访问

    12. 抽象类(abstract class)

    • 抽象类为子类设置原型(prototype )
    • 抽象类不能实例化,只能被继承
    • 抽象类可以包含只有一个原型的函数,只做一个方法的定义(纯虚函数)
      实例:
    //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_class;
      initial begin
        extended_packet p;
        p = new();
        p.addr = 10;
        p.display();
      end
    endmodule
    

    12. 域分辨符::

    域分辨符号可以访问类内的static变量,同时在子类中也可以通过这个操作符访问父类的公有和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 sro_class;
      int id=10;
      initial begin
        packet p;
        p = new();
        packet::id = 20;
        p.display(packet::id,id);
      end
    endmodule
    

    13. External

    配合域分辨符可以支持在类之外定义function或task.

    • 在定义时需要指定一切关键词(virtual,local, protected)以及所有的变量列表。
    • 注意external定义函数的变量名需要和下面详细定义的函数的变量名一模一样
    class packet;
       
      //function declaration - extern indicates out-of-body declaration
      extern virtual function void display(bit [31:0] addr, data );
    endclass
     
       //function implementation outside class body
        function void packet::display(bit [31:0] addr_t, data_t);
          $display("Addr = %0d Data = %0d",addr_t,data_t);
        endfunction
         
    module extern_method;
      initial begin
        packet p;
        p = new();
        p.display(20,30);
      end
    endmodule
    

    上面代码会报错,因为变量名不一致。

    14. typedef

    为后面的类提供一个预先的定义

    typedef class c2;
    
    //class-1
    class c1;
      c2 c;    //using class c2 handle before declaring it.
    endclass
     
    //class-2
    class c2;
      c1 c;
    endclass
      
    module typedef_class;
      initial begin
        c1 class1;
        c2 class2;
        $display("Inside typedef_class");
      end
    endmodule
    

    上面的代码中,c1和c2互相依赖,如果没有第一句先对c2进行预定义,则会编译不通过。

  • 相关阅读:
    CC3000 SmartConfig
    谈谈几个月以来开发android蓝牙4.0 BLE低功耗应用的感受
    CC3000 SPI接口编程介绍
    cc3000+LM3S9B96
    CC3000 主机驱动API介绍
    Wi-FiR CC3000 模块
    修改远程桌面连接端口及修改端口号后如何连接!
    电脑网线/水晶头的连接方法(A类,B类)
    快速切换IP的批处理!
    IE打开报错,提示该内存不能为read的解决办法!
  • 原文地址:https://www.cnblogs.com/lyc-seu/p/12788324.html
Copyright © 2011-2022 走看看