SystemVerilog 类和对象(三)

类和对象

一、类的基本概念

  1. 类class:是一种用来进行数据抽象的工具,将数据和对数据的操作封装在一起,提供建立对象的模板,可以看做是一种数据结构。
  2. 对象object:是所属类class的某一特定实体(也称实例)。
  3. 句柄handle:指向对象的指针,即内存的基地址
  4. 属性property:类class的实体object中所包含的各种变量variable。
  5. 方法method:操作变量的任务task和函数function

SystemVerilog 类和对象(三)

二、对象的创建和销毁

class word
    byte nb[];
    function new(int n);
        nb = new[n];        //动态数组空间开辟
    endfunction
endclass
 
initial begin : initial_1
    word wd;//声明句柄
    for(int i=1;i<=4;i++) wd = new(i);   //创建了4个对象
end
initial begin : initial_2
    #1ps
    $display("How many Bytes are allocated for word instance?")
end

若wd = new(1)所需开辟的空间为1B,那么执行完开辟空间:

wd执行完,句柄指向第四个对象,4B。由于initial中是静态变量,即使initial执行完了,那么变量还在。

SystemVerilog 类和对象(三)

三、对象句柄的深拷贝 与 浅拷贝

1)、 浅拷贝:只拷贝对象中的数据变量,浅拷贝前后的数据变量使用不同的内存空间;而对于对象中的**数据操作(任务和函数)**和其中定义的其它类的句柄,采取类似“引用操作”的方式,浅拷贝前后共用同一内存空间。

BusTran    bt0, bt1;      //声明两个句柄
b1 = new();                 //b1创建对象
b2 = new b1;              //b2创建对象,同时对b1进行浅拷贝

2)、 深拷贝:对于对象中的所有成员统一分配新的内存空间,区别于浅拷贝。

BusTran    bt0, bt1;      //声明两个句柄
b1 = new();                 //b1创建对象
b2 = new();                 //b2创建对象
b2.copy(b1);              //深拷贝,自定义copy函数

四、类的特性

SystemVerilog 类和对象(三)

class clock 
    local bit is_summer = 0;
    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
 
 
clock ck;
    initial begin
        ck = new();
        $display("now tome is %0d", ck.get_clock());
        ck.set_summer(1);
        $display("noe time is %0d", ck.nclock);
end

第一个打印为6;第二个编译出错。外部句柄ck不能访问nclock变量(加了限定local)。但可以访问没加限制的function。

4.1.封装

SystemVerilog 类和对象(三)

4.2.继承

子类继承了父类所有的成员方法和属性,并且可以拥有自己特性。通过关键字extends实现继承解决了代码的重用问题。

子类既包含继承父类的成员方法和属性,也有自己独特的个性方法(青出于蓝而胜于蓝),所以如果想要将一个指向父类的指针转化为指向子类的指针,无法直接转换,(父亲大人,时代变了!)必须通过$cast(),在system veilog中被称为向下类型转换(downcasting)。

  • 向上类型转换
transaction_class       tr = new();       //父类
subtransaction_class    sc = new();       //子类
tr = sc;               //子类(右)指向父类(左),这是正确的可以直接转换
  • $cast向下类型转换
transaction_class      tr;//父类
subtransaction_class   sc; //子类
sc = new();
tr = sc;        //句柄类型转化为同一类型,父句柄类型转化为子句柄类型
$cast(sc,tr);   // 通过cast方式可以实现,可以看到tr的句柄类型虽然是父类,但其指的对象类型是子类

子类继承父类时新增加一个函数,该函数与父类中的某函数同名,调用时会调用新增的函数,而不是继承下来的函数。

  1. 如果父类与子类的函数同名,但是参数不同,此时不论有无关键字virtual,父类的函数都将被隐藏。
  2. 如果父类与子类的函数同名,参数也相同,但是基类函数无关键字virtual,父类的函数都将被隐藏。(如果父类函数有virtual,则会形成多态)

对于子类类新增同名函数成员的访问:“对象名.成员名”;
对于父类中同名函数成员的访问:“(子类)对象名.基类名::成员名”

4.3.多态

调用同一个函数,实现不同的行为就是多态。但要满足两点要求:

  1. 必须通过父类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且子类必须对基类的虚函数进行重写

虚函数virtual function

被virtual修饰的类成员函数称为虚函数。虚函数是动态绑定的,如果子类需要修改父类的行为(即重写与基类函数同名的函数),就应该在父类中将相应的函数声明为虚函数。使用虚函数应当注意:

  • 父类中某一成员函数声明为virtual虚函数后,子类中的同名函数(同名、同参、同类型)自动生成虚函数。
  • 子类的同名虚函数会重写或覆盖原来父类中的同名虚函数.。

虚类virtual class

虚类通过关键字virtual声明,不能被例化(不能创建对象),但可以通过派生,生成有用的子类,也可声明一个虚类的指针,通过抽象类的指针指向不同的子类对象。进而访问子类对象的虚函数,实现多态性。

正常情况下,父类是不可以访问子类中方法的,但是通过虚父类可以实现。

module OOP_EXTENDS();
  class BusTran;
     bit [31:0] addr,crc,data[7];
      
     virtual function bit[31:0] calc_crc;      //虚方法目的——实现多态
       crc=addr^data.xor;            //异或:奇数个1异或,结果为1;偶数个1异或,结果为0
       calc_crc=crc;         //返回值 
     endfunction
     
     function void dsp_addr(input string handle);
        $display("*****%s.ADDR = %0h",handle,addr);
     endfunction

     function void dsp_crc(input string handle);
        $display("*****BusTran %s.crc = %h",handle,crc);
     endfunction

   endclass
    
   class BadBusTran extends BusTran;       //继承
     bit  bad_crc;
      
     virtual function  bit[31:0]  calc_crc();   //虚方法目的——实现多态; 派生类中的同名函数的virtual可以省略
        super.calc_crc();                //通过关键字super,实现子类对父类成员的调用
        if(bad_crc)  crc = ~crc;
           calc_crc = crc;
     endfunction

   endclass

   BusTrans   bt0;       //声明父类句柄
   BadBusTran bbt0;      //声明父类句柄
 
   initial begin
      bt0 = new;
      $display("*****handle bt0 is : %0h",bt0);
      bbt0 = new;
      $display("*****handle bbt0 is : %0h",bbt0);
       
      bt0.addr = 32'hFFFF_FFFF;
      bt0.dsp_addr("bt0");
      bbt0.addr = 32'h1111_1111;
      bt0.dsp_addr("bbt0");      
       
      foreach(bt0.data[i]) begin
        bt0.data[i] = 32'h0000_FFFF;
      end
      bt0.calc_crc;
      bt0.dsp_crc("bt0");

      foreach(bbt0.data[i]) begin
        bbt0.data[i] = 32'hFFFF_FFFF;
      end
      bbt0.calc_crc;           //此时未设置bad_crc,二值变量默认初始值为0
      bbt0.dsp_crc("bbt0");
      bbt0.bad_crc = 1;
      $display("*****After bad crc set to 1");
      bbt0.calc_crc;
      bbt0.dsp_crc("bbt0");
   end  

//Polymorphism——多态
   function bit[31:0]  crc(BusTran  bt);          //多态——句柄传递过程中,会根据句柄自动识别所属类
      crc = bt.calc_crc();
   endfunction
    
   bit [31:0] crc_value;
   initial begin 
      #10;
      crc_value = crc(bt0);
      $display("*****crc from bt0 = %0h",crc_value);
      crc_value = crc(bbt0);
      $display("*****crc from bt0 = %0h",crc_value);      
   end

endmodule

SystemVerilog 类和对象(三)

包的使用

SystemVerilog 类和对象(三)

参考:

Mr.翟的博客

Systemverilog(绿皮书)

上一篇:mfc crc校验工具


下一篇:瞎琢磨的dcc+ollvm【网络校验码】