Systemverilog(绿皮书)第五章——类与对象(三)类的继承

在OOP中我们通过继承的方式给子类赋值或者增加新的功能:

class cat;
    protected color_t color;
    local bit is_good;
    function set_good(bit s);
        this.is_good = s;
    endfunction
endclass

/声明黑猫类/
class black_cat extends cat;
    function new();
        this.color = BLACK;
    endfunction
endclass

/声明白猫类/
class while_cat extends cat;
    function new();
        this.color = WHITE;
    endfunction
endclass

black_cat bk;    
white_cat wt;    

initial begin
    bk = new();    //初始化黑猫对象
    wt = new();    //初始化白猫对象
    bk.set_good(1);
    wt.set_good(1);
end

在验证的过程中我们通过单一的职责划分,使得各个组件的任务明确,那么一个  简单的环境构建就有了。

案例1:对于数据发送,我们希望把数据都封装到类里面,因为类中可以封装 很多的数据和方法。

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

但是,此时我们在此 基类的基础上又需要增加新的功能 , 生成一个新的类型。 此时就可以考虑通过继承的方式,来实现此功能。

class BadTr extends Transaction;
    rand bit bad_crc;
    virtual function void calc_crc;
        super.calc_crc();            //计算好的CRC值
        if (bad_crc) crc = ~crc;     //CRC值取反
    endfunction

    virtual function void display(input string prefix="");
        $write("%sBadTr: bad_crc = %b, ", prefix, bad_crc);
        super.display();
    endfunction
endclass : BadTr

在子类中重新定义了新的成员变量和 成员方法,新定义的成员方法和原方法之间没有关系。

super来实现子类的方法来继承父类的方法。super.calc_crc()函数可以实现对父类中函数的调用, 之后再去添加子类的方法。在其内部通过super来索引父类的同名函数

但是,子类中要是想索引父类中的变量

案例2:在验证环境中,也有类似的类trans和一个initiator类 stm_ini,在test中通过继承于basic_test的两个子类test_wr和test_rd,分别用来对DUT发起写测试和读测试。

class basic_test;
    int def = 100;    //成员变量赋予默认值
    int fin;
    task test(stm_ini ini);
        $display("basic_test::tset");
    endtask
    function new(int val);
    endfunction
endclass


class test_wr extends basic_test;
    function new();
        super.new(def);
        $display("test_wr::new");
    endfunction

task test(stm_ini ini);
    super.test(ini);
    $display("tast_wr::test");
endtask
endclass
class basic_test;
    int def = 100;    //成员变量赋予默认值
    int fin;
    task test(stm_ini ini);
        $display("basic_test::tset");
    endtask
    function new(int val);
    endfunction
endclass


class test_rd extends basic_test;
    function new();
        super.new(def);
        $display("test_rd::new");
    endfunction
task test(stm_ini ini);
    super.test(ini);
    $display("tast_rd::test");
endtask
endclass

 子类在定义new 函数的时候,应该首先调用父类 的new函数---super.new()。若父类new函数没有参数,那么省略该调用,系统编译时可以自动添加super.new。 

对象创建和初始化的顺序来看,用户注意一下规则:

对于成员的覆盖问题:

class basic_test;
    int def = 100;    //成员变量赋予默认值
    int fin;
    task test(stm_ini ini);
        $display("basic_test::tset");
    endtask
    function new(int val);
    endfunction
endclass


class test_wr extends basic_test;
    int def = 200;
    function new();
        super.new(def);
        $display("test_wr::new");
        $display("test_wr::super.def = %0d", super.def);
        $display("test_wr::this.def = %0d", this.def);
    endfunction
endclass

module tb;

basic_test t;
test_wr wr;

initial begin
    wr = new();
    t = wr;
    $display("wr.def = %0d", wr.def);
    $display("t.def = %0d", t.def);
end

创建了子类对象,子类对象调用的def值为多少?200

子类句柄的值赋值给父类,通过父类索引到def为多少?100

父类和子类句柄都指向了对象,但是子类句柄指向范围更广,父类只能访问父类那部分成员变量和方法。 

子类的访问:

wr.def         : 访问子类的变量

wr.super.def:通过子类访问父类的变量

拿到一个父类句柄,该句柄可能指向子类对象(被一个子类对象赋值之后)也可能指向父类对象。但是, 只能访问子类对象中属于父类的成员。

但是不能把父类赋值给子类对象,编译时会报错。编译时默认按照最差的情况运行。父类句柄转化为子类句柄只能通过

$cast(T,S)运行。

若子类中没有def,通过子类对象索引:wr.def时会发现子类中没有def成员变量值,接着就回去访问父类中的同名def=100。

而此时,父类中访问t.def也是只能访问100。

而子类中super.new(def)中的def调用的是子类中的def, 优先查找顺序是由近到远

若程序中,定义wr=new();t=new();定义了两个对象,则wr指向子类对象,t指向父类对象。调用成员变量和函数的时候,只从自己的类中调用。此时,使用$cast(T,S)将父类 赋值给子类也是错误的。大的类可以缩小访问(子类可以访问父类),但是小的类不能访问大类(父类不能赋值给子类)。

发布了14 篇原创文章 · 获赞 10 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Jay_who/article/details/105597794