【路科V0】systemVerilog基础9——类的继承

继承和子类

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

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

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

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

成员覆盖

        子类可以继承父类的成员,所以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
//创建一个子类对象,并将句柄赋值给lp
LinkedPacket lp = new;
Packet p = lp; //将子类句柄lp赋值给父类句柄p
//虽然子类句柄和父类句柄此时都指向同一个子类,但是
//由于句柄类型,限制了其访问范围
//所以通过父类句柄p,可以访问的成员变量和成员方法都是父类范围的
j = p.i; // j = 1,不是2
i = p.get() : //i = 1,不是-1或者-2

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

        如果使用子类句柄lp,去访问同名成员,访问的都是子类范围的。

子类继承父类成员方法——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对于类的继承和对域作用范围的指定都有帮助 。

验证环境中的案例

 这个验证环境已经有了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

测试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来索引父类的同名函数。

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值