systemVerilog基础9——类的继承

1、 描述子类继承父类的关键词:extends

        之前定义过的类Packet,可以进一步扩展构成一个它的子类LinkedPacket。类Packet的定义如下:

class Packet ;//class定义类 类名 packet
    //类 packet的成员
    //数据或类属性
    bit [3:0] command; bit [40:0] address; bit [4:0] master_id;
    integer time_requested ;
    integer time_ issued;
    integer status;
    typedef enum { ERR_OVERFLOW= 10,ERR_UNDERFLOW = 1123} PCKT_TYPE;
    const integer buffer_size = 100;
    const integer header_size;
    //类 packet的成员方法new ();clean() ;issue_request; current_status
    //初始化
    function new ();
        command = 4' d0; address = 41'b0; master_id = 5'bx;   header_size = 10;
    endfunction
     //方法
    //公共通道入口
    task clean() ;
        command = 0; address = 0; master_id = 5'bx ;
    endtask
    task issue_request( int delay ) ;
        //向总线发送请求
    endtask
    function integer current_status ( ;
        current_status = status;
    endfunction
endclass

通过extends,子类LinkedPacket继承于其父类Packet,包括继承其所有的成员(变量/方法)。

class LinkedPacket extends Packet;//父类名Packet,子类名LinkedPacket
//在子类继承父类的成员变量和方法基础上,定义新的成员next和成员函数get_next()
    LinkedPacket next ;
    function LinkedPacket get_next();
        get_next = next;
    endfunction
endclass

2、成员覆盖

         子类可以继承父类的成员,所以LinkedPacket对象中也包含Packet类的成员。由此,父类句柄可以指向子类的对象

LinkedPacket lp = new ;
Packet p = lp;

        注意: 父类句柄如果指向子类对象 ,那么将无法访问子类的成员变量(这是由于父类句柄可访问范围所限制的)

        注意: 虽然在子类中可以新定义子类成员 ,但是如果子类中声明了与父类同名的成员(变量/方法),那么子类对其同名成员的访问都将指向子类,而父类的成员将被隐藏

        示例: 下面的代码经过简化,父类和子类只保留了各自同名成员变量i和成员变量get 

//创建父类Packet
class Packet;
    integer i = 1;
    function integer get() ;
        get = i ;
    endfunction
endclass
//创建子类LinkedPacket
class LinkedPacket extends Packet;
    integer i = 2;
    function integer get() ;
        get = -i ;
    endfunction
endclass

tb.sv文件内容:


`include "class_test.sv"
module tb;

initial begin

LinkedPacket lp = new;
Packet p = lp; //将子类句柄lp赋值给父类句柄p
//虽然子类句柄和父类句柄此时都指向同一个子类,但是
//由于句柄类型,限制了其访问范围
//所以通过父类句柄p,可以访问的成员变量和成员方法都是父类范围的
integer i,j;
j = p.i; // j = 1,不是2
$display("j is %d\n",j);

i = p.get() ; //i = 1,不是-1或者-2
$display("i is %d\n",i);

end
endmodule

运行结果如下:

        虽然子类句柄和父类句柄此时都指向同一个子类,但是由于句柄类型,限制了其访问范围
,所以通过父类句柄p,可以访问的成员变量和成员方法都是父类范围的 ,而不是子类范围。 如果使用子类句柄lp,去访问同名成员,访问的都是子类范围的

3、子类继承父类成员方法——super

        由于继承的关系,如果子类成员方法想要继承父类成员方法,那么可以在子类代码中通过super关键词来访问当前对象的父类成员。 

        super是用来访问当前对象其父类的成员尤其当子类的成员如果与父类的成员同名,那么需要使用super来指定访问其父类成员,而非默认的子类成员

class Packet; //父类
    integer value;
    function integer delay () ;
        delay = value * value;
    endfunction
endclass
 
class LinkedPacket extends Packet; //子类integer value;
    function integer delay () ;
        delay = super.delay ()+ value * super.value;
//子类通过super,访问与父类同名的delay函数,和同名成员变量value
    endfunction
endclass

super对于类的继承和对域作用范围的指定都有帮助 。

 4、验证环境中的案例

这个验证环境已经有了generator和driver的两个组件:

        generator(激励产生器):是单纯产生激励数据,
        driver(驱动器):是单纯使用激励数据来发送时序激励。
     这种单一职责的划分,使得各个组件的任务十分明确,那么一个简单环境构建就有了。

        在验证环境中,描述测试环境的部分由test来实现。按照test要求来生成激励,并且传递给driver组件由generator实现。在最后,通过激励的数据内容,将其按照时钟周期,将数据驱动到硬件接口上的是driver。最后激励数据通过信号的方式,抵达了DUT边界端口。

        如果要将数据发送到DUT,那么需要有以下的基本元素和数据处理方法,我们将它们封装到Transaction类中。

        从generator产生的激励中,包括source、dst、data和crc等变量,将它们和有关的处理函数calc_crc()和display()封装在 Transaction类中。  driver获取这个类之后,将从中提取变量值,再将其驱动到DUT端口上面。

class Transaction;
    rand bit [31:0] src, dst, data[8] ;//随机成员变量
    bit [31 :0] crc; //二次处理后的成员数据
    virtual function void calc_crc () ;
        crc = src ^ dst ^ data.xor;
    endfunction
    virtual function void display(input string prefix="");
        $display("%sTr: src=%h, dst=%h, crc=%h",prefix,src, dst,crc);
    endfunction
endclass
 

5、测试DUT的稳定性 

         如果我们为了测试DUT的稳定性,需要加入一些错误的数据来测试DUT的反馈,但我们又想尽量复用原有的验证环境(也包括已经定义好的类),那么我们就可以考虑使用继承的方式来新创建一个类BadTr。

// BadTr是Transaction的子类,Transaction是BadTr的父类。
class BadTr extends Transaction;
    rand bit bad_crc;
// BadTr重新定义了函数calc_crc()和display(),
//而在其内部通过super来索引父类的同名函数。
    virtual function void calc_crc;
        super.calc_crc() ; // 计算crc
        if (bad_crc) crc = ~crc; //破坏crc
    endfunction
 
    virtual function void display(input string prefi x="");
        $write("%sBadTr: bad_crc=%b, ", prefix, bad_crc);
        super.display () ;
    endfunction
 
endclass : BadTr

        在定义了这两个激励类型以后,它们将可以被激励发生器产生,并送往驱动器。

观察以下继承关系图,可以看到:

        BadTr是Transaction的子类,Transaction是BadTr的父类。

        BadTr重新定义了函数calc_crc()和display(),而在其内部通过super来索引父类的同名函数。

        如果创建一个子类对象,那么该对象的空间可以划分为父类成员的空间和子类成员的空间。即便父类和子类有同名的成员变量和成员方法,但是它们的空间都是独立的。

        需要注意的是,如果是父类的句柄指向了子类的对象,那么它访问的空间将会受到父类句柄可访问范围的影响,只能访问父类成员 
 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值