SV学习笔记—类

目录

1.类(class)

1.1验证中为什么需要面向对象(OOP)?

1.1.1 OOP的三大特性

1.2术语

2.类的成员操作

2.1类的成员

2.2定制构造函数


1.类(class)

类是成员变量和成员方法的载体,一个类的功能应该尽可能简单,不应当承担过多的职责,更不应该承担不符合它的职责,这在设计模式中称之为单一职责原则(SRP Single Responsibility Principle)

1.1验证中为什么需要面向对象(OOP)?

在Testbench包含但不限于以下组件:

①激励生成器(stimulus generator):生成激励内容

②驱动器(driver):将激励以时序形式发送至DUT

③监测器(monitor):监测信号并记录数据

④比较器(checker):比较数据

而对于同一验证环境中,不同组件的功能和数据是不一样的;不同验证环境中,同一类型的组件的功能和数据是相似的(这提供了继承的可能性,使新的项目组件可以继承旧的项目组件)。而面向对象编程和验证世界的构建原则十分符合,所以我们需要OOP。

1.1.1 OOP的三大特性

1.封装:封装就是把属性和方法封装在一个class中

2.继承:一个类型获取另外一个类型的属性的方式;父类可以生成很多子类,子类可继承父类所有的属性和方法。

3.多态:同一个行为具有多个不同表现形式或形态的能力。

1.2术语

Class类:包含变量(属性)和子程序(方法)的基本构建块,就像Verilog中的Module一样,只不过Module属于硬件盒子,而class属于软件盒子(class就像一张图纸)。类可以定义在任何地方,既可以定义在program/module/package中,也可以定义在这之外的任何地方甚至单独的一个文件中。

Object对象:类通过实例化成为一个对象;类似于Verilog中的module例化,只不过那是硬件的例化,在SV中使用class例化,这属于软件的例化。(通过class实例化出的许多对象就像根据图纸建造出的许多房子)

Handle句柄:指向对象的指针;在SV的对象索引时,需要通过句柄来索引对象 的变量和方法。(句柄就像每个房子的地址,但句柄是指针,不是地址!)

Property属性:存储数据的变量

Method方法:类中使用task或者function来定义方法以便处理自身或者外部传入的数据。

通过以下的代码可以对上述术语有更直观的理解:

class Transaction;//定义一个类
//在实例化类的时候为变量分配空间和初始化,当然初始化值可在new构造函数中指定
    logic [31:0] addr;//类的属性
    logic kind;
    logic [63:0] data;

    function new(logic [31:0] addr);//类的方法,每个类的new函数也叫构造函数,因为在示例化这个类的时候,会通过new的方式来实例化,其实调用的就是这个函数,可以在这个函数中添加一些初始化的信息
        this.addr = addr;
        this.kind = 'x;
        this.data = 'x;
    endfunction

    function void display();//类的方法
        $display(“addr=%h, kind=%b, data=%h”,addr, kind, data);
    endfunction
endclass

program class;
    Transaction tr_gen;//声明一个句柄(指针),此时是一个空指针,系统也没有为其开辟物理内存
    Transaction tr_drv;//为此类声明另一个指针

    Transaction tr[10];//句柄数组

    initial begin
        tr_gen = new(32’b7788);//通过new创建一个对象obj1,并让上述的空指针指向这个对象,此时系统为该对象开辟物理内存
        tr_drv = new(32’b8877);//再创建一个新的对象obj2
//通过以上两步后才能使用对象

        foreach(tr[i])begin//将句柄数组中的每一个句柄实例化
            tr[i]=new;
        end

        tr_gen.kind = 1’b1;//通过句柄为obj1的属性kind赋值
        tr_drv.data = ’b0;//通过句柄为obj2的属性data赋值

        tr_gen.display();//通过句柄调用obj1的display函数
        tr_drv.display();//通过句柄调用obj2的display函数
    end
endprogram

2.类的成员操作

2.1类的成员

类作为载体,也具备了天生的闭合属性,即将其属性和方法封装在内部, 不会直接将成员变量暴露给外部,通过protected和local关键词来设置成 员变量和方法的外部访问权限

类的成员有属性和方法。类的功能应该尽可能的单一,方便复用和维护。对于类的变量(属性)来说,分为public/protected/local三种访问类型的变量

1.如果没有指明访问类型,那么成员的默认类型是public,子类和外部均可以访问成员,随意使用

2.如果指明了访问类型是protected,那么只有该类或者子类可以访问成员,而外部无法访问

3.如果指明了访问类型是local,那么只有该类可以访问成员,子类和外部均无法访问

以local为例看下列代码:

class clock
    local bit is_summer = 0;//local变量只能在当前类中操作,子类和外部没有权限
    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

    function print_data(string message);
        if(message=="is_summer")
            $display("is_summer=%0d",this.is_summer);
        else
            $display("nclock=%0d",this.nclock);
    endfunction

program example;
    clock ck;

    initial begin
        ck = new();
        $display(“now nclock is %0d”,ck.get_clock());//通过内置函数访问
        ck.is_summer=1;//无法通过外部直接改变,会编译错误
        ck.set_summer(1);//通过内置函数改变
        $display(“now nclock is %0d”,ck.get_clock());
        $display(“now nclock is %0d”,ck.nclock);//通过外部不能访问,会编译错误
        ck.print_data("nclock");//通过类的内置打印函数访问
        
    end
endprogram

 编译错误提示信息:

 可见如果直接访问local中的变量,编译器会报错,如果通过类中定义的print函数访问,则可以。

2.2定制构造函数

当我们将创建对象,也就是让句柄指向对象的时候,通常会这么写:

Trans t1;
t1=new();

其中的new()函数,也称构造函数,若定义的类中没有自己构建new函数,则调用new函数的时候则是系统的new函数;当然,我们可以通过自定义new函数将初始化的默认值改成想要的值:

class Transaction;
    logic [31:0] addr,crc,data[8];
    function new(logic[31:0]a=3,d=5);//当外部new的时候,由于这里自定义了new函数在,则外部使用的是这个new函数
        addr=a;
        foreach(data[i])    data[i]=d;
    endfunction
endclass

initial begin
    Transaction tr;
    tr=new(10);//a=10,d=5,调用谁的new函数取决于new函数左边的句柄类型
end

tr = new(.a(10));//a=10,d uses default of 5 tr=new(10),等价于上面的

一般在搭建验证环境的时候,除了用new函数修改默认值之外,类中自定义的new函数还用来连接接口或者信箱的,具体可见: SV小项目—异步fifo的简单验证环境搭建(全)

2.3静态变量

class Transaction;
    static int count=0;
    int id;
    
    function new();
        id=count++;//id首先使用count=0的值,再++
    endfunction
endclass

program example;
    Transaction t1,t2;
    initial begin
        t1=new;//第一个实例,id=0,count=1
        t2=new;//第二个实例,id=1,count=2
    end
endprogram

        可见不管创建多少个object,静态变量count只有一个,可以认为count是保存在类中而非object中的,而id不是静态的,所以每个object都有自己的id变量

        一般我们可以通过句柄。的方式引用类中的变量,对于静态变量也可以通过类作用域操作符的方式引用:

class Transaction;
    static int count=0;//在定义时初始化而不能在new中初始化
    ······
endclass

program example;
    run_test();
    $display("%d transaction were created",Transaction::count);//引用静态句柄
endprogram
  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值