更多内容请关注下面公众号!
硅芯思见
本文纯属学习之用,欢迎指正文中不足,封面图片若有侵权,请及时沟通!
在SystemVerilog中关于方法有两种“static”,一种是具有静态生命周期的方法(参见《SystemVerilog中的生命周期lifetime》),另一种则是静态的方法,这种方法常见于类中,先来看下两者的声明方式,如下:
[task/function] static method_name(arg_list); // static lifetime
static [task/function] method_name(arg_list); // static method
可见两种“static”方法在声明时“static”关键字所处的位置是不同的,本文将主要针对静态方法进行说明。在SystemVerilog中,类中方法的生命周期是动态的(automatic lifetime),不能将类中的方法声明为具有静态的生命周期,但是可以将类中的方法声明为静态方法(static method),那么这种静态方法有哪些特点和使用注意事项呢?本文我们将通过示例进行说明。
首先通过一个示例了解下将类中方法声明为具有static lifetime时的情况。
【示例】将类中方法声明为static lifetime
【仿真结果】
示例代码中,因为示例中在类packet中定义的方法disp被声明为了具有静态的生命周期,仿真器在对代码编译时产生warning提示在类中不能使用具有静态生命周期的方法,在IEEE1800中的Section 8.6描述如下:
从示例中代码仿真结果可以看到,方法中的变量tmp在两次disp调用中,其值是保持每次调用的修改,第一次调用时tmp在方法结束时进行了自加一操作,第二次调用时,tmp保持了前一次disp调用时对于tmp的修改。这是因为disp被声明为了具有静态生命周期的方法,其中的变量也就具有静态的生命周期,即tmp具有静态的生命周期。尽管可以进行仿真,并且有对应的仿真结果,但是这种在class中使用静态生命周期的方式在IEEE1800中是不允许的,也许会在以后的仿真工具中直接按照错误处理,为了保证代码的健壮性,建议不要在代码中使用标准中目前不允许的使用方式。
虽然在类中不能将方法声明为具有静态的生命周期,但是可以讲方法声明为静态方法,我们还是通过示例学习类中静态方法的使用声明方式。
1 静态方法(static method)
【示例】将类中方法声明为static method
【仿真结果】
示例中,在packet中disp声明为了静态方法,在initial过程块中,对packet中的静态方法和静态属性进行了如下操作:
首先通过packet::disp()的方式调用了方法disp,显示了packet中的静态变量svar的值,因为packet中svar此时并没有进行初始化也没有赋值,所以此时显示的值为0;
然后通过packet::svar的方式给svar赋值为3后,再次通过packet::disp()的方式调用了方法disp,显示的svar的值为修改后的值3;
句柄pkt1和pkt2分别指向创建的两个对象,通过pkt1.svar将svar的值改为4后,分别通过packet::disp()、pkt1.disp()和pkt2.disp()访问svar的值,此时通过这三种方式访问的svar的值都是通过pkt1.svar修改后的值4;
然后通过pkt2.svar将svar的值改为5后,分别通过packet::disp()、pkt1.disp()和pkt2.disp()访问svar的值,此时通过这三种方式访问的svar的值都是通过pkt2.svar修改后的值5;
通过这个示例,我们可以得到以下几点:
静态方法声明时需要在方法名前使用static关键字进行修饰,这与静态生命周期方法声明方式不同;
类中的静态属性和静态方法可以直接通过类名的方式进行引用:
class_name::property_name
class_name::method_name
类中的静态属性和静态方法也可以通过句柄名的方式进行引用:
handle_name.property_name
handle_name.method_name
在示例中,我们通过静态方法访问了类中的静态属性,那么是否可以在静态方法中访问类中的“一般属性”呢?我们一块看下下面的示例。
【示例】
【仿真结果】
示例中,packet中的svar不是static变量,在静态方法disp中引用svar,此时编译报错,提示非静态变量在静态方法中进行了引用。所以在SystemVerilog的类中,静态方法不能访问非静态属性。
2 静态属性
在SystemVerilog的类中,可以通过关键字static将属性声明为静态属性,通过静态属性可以在多个对象之间实现数据的共享,这里需要注意,静态属性不是某个对像所特有的。虽然静态属性可以实现数据在多个对象之间的共享,但是其并不会破坏类本身的隐藏特性,保证了类中属性的安全性。另外,因为静态属性对于所有对象来说都是共用的,其都只存储在一处,所以使用静态属性在某种程度上可以节省内存空间,同时也正是因为所有对象共享的原因,类的所有对象都能更新该值,每个对象对于静态属性的更新,其他所有对象都会获得更新后的值,通过这样的手段也可以提高数据属性的访问效率。下面我们通过一个示例,来了解下静态属性的使用和注意事项。
【示例】
【仿真结果】
示例中,packet中的svar为静态变量,并且在packet创建对象之前通过类名对svar进行引用访问,因为此时并没有调用new函数,也没有对svar进行任何赋值操作,所以此时通过类名访问到的svar值为svar对应数据类型的默认值0;
创建对象之后,通过类名可以访问到在new函数中被修改的静态变量svar的值,也可以通过句柄pkt1.svar访问到的svar的值也为new函数中被修改的静态变量svar的值;
然后通过fork...join结构开启了三个进程(p1、p2、p3),每个进程中分别使用了不同方式对svar进行了访问,并且三个进程同时启动:
在p1中,通过packet::svar方式对svar进行了赋值操作,赋值为5,然后显示svar的值为5,等待10个时间单位后通过packet::svar方式对svar再次进行赋值操作,此时赋值为6,然后显示svar的值为6,具体变化过程如下图所示:
在p2中,等待2个时间单位后,通过pkt1.svar方式对svar进行赋值操作,赋值为7,然后显示此时的svar的值为7,等待10个时间单位显示pkt1.svar的值为6,注意此时并不是7。这是因为在p2中第二次通过pkt1.svar访问svar时,因为在访问第二次访问之前,第10个时间单位时,在p1中通过packet::svar方式对svar进行了修改,且因为svar为静态变量,不属于任何对象而属于类本身,所以此时通过在p2中通过句柄pkt1访问到的svar为packet所有对象共享的svar,此时pkt1.svar访问到的svar即为p1中对于svar的修改后的值,具体变化过程如下图所示:
在p3中,每隔1个时间单位通过pkt2.svar方式访问svar的值,相当于每隔1个时间单位对svar的值进行检测,可以观测到每个时间单位svar的变化情况。同时通过与p2的对比,我们可以看出,pkt1和pkt2是两个句柄,通过不同句柄访问的静态变量是同一个变量。具体变化过程如下图所示:
除此之外,在静态方法中是否可以使用“this”等具有特殊意义的“指针”呢?我们可以看下下面的示例:
【示例】
【仿真结果】
示例中,在静态方法中通过this引用类的属性时,此时不管该属性是否为静态的,在SystemVerilog中都是不允许的。同时,SystemVerilog在类的new函数调用时会自动调用super.new,也可以在new函数中使用this,通过该示例我们也可以推断知道在SystemVerilog中new函数不能声明为static方法。
通过上述示例,我们可以知道类中的static具有以下特点:
Ø 静态属性在声明时需要使用关键字static;
Ø 静态属性被类的所有对象共享;
Ø 静态属性是静态存储的,即具有静态的生命周期;
Ø 静态属性引用的方式有两种:<类名>::<变量名>和<对象名>.<变量名>;
Ø 静态方法的生命周期是相对于类而言的,具有静态生命周期的方法指的是方法中的参数和变量的生命周期;
Ø 类中的方法和类中的属性默认都是automatic,类中的方法不能声明为静态生命周期的方法;
Ø 不能在静态方法中使用“this指针”和“super指针”(关于this和super的使用可以参考《SystemVerilog中这个this到底怎么回事》和《SystemVerilog中的超级英雄super》);
更多内容请关注下面公众号!
硅芯思见