SV——面向对象编程基础

1、OOP术语

a.类(class):包含变量和子程序(函数或者任务)的基本构建块。Verilog中与之对应的是模块(module)。

b.对象(object):类的一个实例。在Verilog中,你需要实例化一个模块才能使用它。

c.句柄(handle):指向对象的指针。在Verilog中,你通过实例名在模块外部引用信号和方法。一个OOP句柄就像一个对象的地址,但是它保存在一个只能指向单一数据类型的指针中。

d.属性(property):存储数据变量。在Verilog中,就是寄存器(reg)或线网(wire)类型的信号。

e.方法(method):任务或者函数中操作变量的程序性代码。Verilog模块除了initial和always块以外,还含有任务和函数。

f.原型(prototype):程序的头,包括程序名、返回类型和参数列表。程序体则包含了执行代码。

类是对象的一个模板,其内部定义了数据和方法。对象是类的一个例化和实现。

注:《SystemVerilog验证 测试平台编写指南》中使用 变量(variable) 程序(routine),而没有使用OOP中的属性(property)和方法(method)。

2、用户使用对象的三个步骤

a.定义类:

class packet;
...
endclass:packet

b.在module、class、function、task等地方声明对象:

packet my_packet;    //声明一个句柄
packet packet_array[32];

对象标识符(my_packet/packet_array)是例化该对象的句柄(指向对象的指针)。当该对象被创建的时候,该句柄有效,默认情况下句柄将为空(null)。

c.通过构造函数new创建对象的例化

通过new这个构造函数给对象分配内存空间,并且把入口地址赋给对象的句柄:

my_packet=new(168);        //为一个packet对象分配空间

例1 简单的Transaction类

class Transaction;
    bit [31:0] addr,crc,data[8];

     function void display;
         $display("Transaction:%h",addr);
     endfunction:display

    function void calc_crc;
         crc=addr^data.xor;
    endfunction:calc_crc

endclass:Transaction

3、在哪里定义类?

program、module、package中,或者在这些块之外的任何地方。

例2 Class in a package

//File abc.svh
package abc;
calss Transaction;
//Class body
endclass
endpackage

例3 Importing a package in program

program automatic test;
import abc::*;
Transaction tr;
//Test code
endprogram

sv中,激励对象不断的被创建并且用来驱动DUT,检查结果。最后这些对象所占用的内存可以被释放,以供新的对象使用。

例4 声明和使用一个句柄

Transaction tr;    //声明一个句柄
tr=new();     //为 Transaction对象分配空间 

注:
1、调用new函数例化一个对象,即在为该对象申请一个新的内存块来保存对象的变量。
2、new除了分配内存外,还初始化变量。default 二值变量为0,四值变量为X。
3、new函数不能有返回值,因为构造函数是返回一个指向类的对象的句柄,其类型就是类本身。

例5.4 简单的用户定义的new()函数

class Transaction;
    logic [31:0] addr,crc,data[8];
    function new;
        addr = 3;        //固定值
        data= '{default:5};        //crc未定义,初始化位默认值为X
    endfunction
endclass

例5 一个带有参数的new函数

class Transaction;
    logic [31:0] addr,crc,data[8]; 
    function new(logic [31:0] a=3,d=5);
        addr = a;
        data = '{default:5};//crc未定义,默认值为X
    endfunction
endclass
 
initial begin
    Transaction tr;
     tr = new(.a(10));//a=10,d uses default of 5 tr=new(10);
end

sv调用哪个new()函数,取决于赋值操作符左边的句柄类型。tr是 Transaction的句柄,索引会调用 Transaction的new函数。

例6 调用正确的new函数

class Transaction;
    logic [31:0] addr,csm,data[8];
endclass:Transaction

class Driver;
    Transaction tr;
    function new();//Driver's new function
        tr = new();//Call the Transaction new function
    endfunction
endclass:Driver

4、new()和new[]的区别

调用new()函数仅创建了一个对象,而new[]操作则建立一个含有多个元素的数组。

new()可以使用参数设置对象的值,而new[]只需使用一个数值来设置数组的大小。

通过声明一个句柄来创建一个对象

例7 为多个对象分配地址

Transaction t1,t2;//声明两个句柄
t1=new();    //为第1个Transaction对象分配地址
t2=t1;      //t1和t2都指向该对象
t1=new();    //为第2个Transaction对象分配地址

注:最后t2指向第一次分配的对象,t1指向第二次分配的对象。

5、对象的解除分配(deallocation)

垃圾回收就是一种自动释放不再被引用的对象的过程。sv分辨对象不再被引用的办法就是记住指向它的句柄的数量,当最后一个句柄不再引用某个对象了,sv就释放该对象的空间。

例8 创建多个对象

Transaction t;//创建一个句柄
t=new();    //分配一个新的 Transaction
t=new();    //分配第二个,并且释放第一个t
t=null;     //解除分配第二个

对对象使用“.”符号来引用变量和子程。

直接访问变量会限制以后对代码的修改,因此OOP规定只能通过对象的公有方法访问对象的变量。

例9 使用对象的变量和子程序

Transaction t;    //声明一个 Transaction句柄
t=new();    //创建一个 Transaction对象
t.addr=32'h42;    //设置变量的值
t.display();    //调用一个子程序

i++和++i区别:
i++:先引用后增加,先在i所在的表达式中使用i的当前值,后让i加1。
++i:先增加后引用,先让i加1,然后在i所在的表达式中使用i的新值。

6、静态属性与方法

6.1 简单的静态变量

1、每一个类的例化(也就是一个对象)都拥有它自身每个变量的复制,因此OOP规定只能通过对象的公有方法访问对象的变量。

2、对象1:变量1,变量2…
     对象2:变量1,变量2…
对象1 和 对象2 自身成员变量不和其他对象共享。

3、类的静态属性是存在类中的,而不是在每个类的对象中,在类中,使用静态static方式声明的属性可以被多个对象共享。

例10 含有一个静态变量的类

class Transaction;
    static int count=0;    //已创建的对象的数目
    int id;    //实例的唯一标志
    function new();
        id=count++;    //设置标志,count递增
    endfunction
endclass
 
Transaction t1,t2;
initial begin
    t1=new();    //第一个实例,id=0,count=1
    t2=new();    //第二个实例,id=1,count=2
    $display("Second id=%d,count=%d",t2.id,t2.count); //使用句柄来引用静态变量
end

6.2 例10使用了句柄来引用静态变量,也可以直接使用类名加上::,即类作用操作符。

例11 类的作用域操作符 ::

class Transaction;
    static int count=0;    //创建的对象数
    ...
endclass

initial begin
    run_test();
    $display("%d transaction were creat",Transaction::count);//引用静态句柄
end

6.3 静态变量的初始化

静态变量通常在声明时初始化,不能简单的在类的构造函数中初始化静态变量,因为每一个新的对象都会调用构造函数。

7、类的方法

类中的程序也称为方法,也就是在类的作用域内定义的内部task或function。

例12 为类Transaction和PCI_Tran定义了display()方法。System Verilog会根据句柄的类型调用正确的display()方法。

例12 类中的方法

class Transaction;
    bit [31:0] addr,crc,data[8];
    function void display();
    $display("@%0t:TR addr=%h,crc=%h",$time,addr,crc);
    $write("\tdata[0-7]=");
    foreach(data[i]) $write(data[i]);
    $display();
    endfunction
endclass

class PCI_Tran;
    bit [31:0] addr,data;
    function void display();
    $display("@%0t:PCI:addr=%h,data=%h",$time,addr,data);
    endfunction
endclass

Transaction t;
PCI_Tran pc;

initial begin
    t=new();//创建一个 Transaction对象
    t.display();//调用 Transaction的方法
    pc=new();//创建一个PCI事务
    pc.display();//调用PCI事务的方法
end

类中的方法默认使用自动存储,所以你不必担心忘记使用automatic修饰符。

8、在类之外定义方法

块外方法声明:

在开始处添加关键字extern,将整个方法移至类定义后面,并在方法名前面加上类名和::作用域操作符。

class Transaction;
    bit [31:0] addr,crc,data[8];
    extern function void display();
endclass
 
function void Transaction::display();
    $display("@%0t:TR addr=%h,crc=%h",$time,addr,crc);
    $write("\tdata[0-7]=");
    foreach(data[i]) $write(data[i]);
    $display();
endfunction

class PCI_Tran;
    bit[31:0] addr,data;//使用实名
    extern function void display();
endclass

function void PCI_Tran::display();
    $display(@%0t:PCI:addr=%h,data=%h,$time,addr,data);
endfunction

 

posted on 2021-07-15 16:30  一曲挽歌  阅读(1267)  评论(0编辑  收藏  举报

导航